Feat: redesign facture comme dans le facutre
This commit is contained in:
parent
8171a20d41
commit
8ee7d8f8e9
@ -1,103 +1,129 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="loading" class="text-center py-5">
|
<!-- Loading -->
|
||||||
<div class="spinner-border text-primary" role="status">
|
<div v-if="loading" class="detail-state">
|
||||||
<span class="visually-hidden">Loading...</span>
|
<div class="spinner-ring"></div>
|
||||||
|
<p>Chargement de la facture…</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Error -->
|
||||||
|
<div v-else-if="error" class="detail-state detail-state--error">
|
||||||
|
<i class="fas fa-exclamation-triangle"></i>
|
||||||
|
<p>{{ error }}</p>
|
||||||
|
<button class="btn-retry" @click="reload">
|
||||||
|
<i class="fas fa-redo me-2"></i>Réessayer
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="error" class="text-center py-5 text-danger">
|
|
||||||
{{ error }}
|
<!-- Content -->
|
||||||
</div>
|
|
||||||
<invoice-detail-template v-else-if="invoice">
|
<invoice-detail-template v-else-if="invoice">
|
||||||
<template #header>
|
<template #header>
|
||||||
<invoice-header
|
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3">
|
||||||
:invoice-number="invoice.invoice_number"
|
<div>
|
||||||
:date="invoice.invoice_date"
|
<h6 class="mb-1">Détails Facture</h6>
|
||||||
/>
|
<p class="text-sm mb-0">
|
||||||
|
Facture n°
|
||||||
|
<b>{{ invoice.invoice_number || "—" }}</b>
|
||||||
|
du
|
||||||
|
<b>{{ formatDate(invoice.invoice_date) }}</b>
|
||||||
|
</p>
|
||||||
|
<p class="text-sm mb-0">
|
||||||
|
Échéance :
|
||||||
|
<b>{{ formatDate(invoice.due_date) }}</b>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex align-items-center flex-wrap gap-2">
|
||||||
|
<soft-badge :color="statusBadgeColor(invoice.status)" variant="gradient">
|
||||||
|
<i :class="statusIcon(invoice.status) + ' me-1'"></i>
|
||||||
|
{{ getStatusLabel(invoice.status) }}
|
||||||
|
</soft-badge>
|
||||||
|
<soft-button color="secondary" variant="gradient" class="mb-0">
|
||||||
|
Export PDF
|
||||||
|
</soft-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #lines>
|
<template #product>
|
||||||
<invoice-lines-table :lines="invoice.lines" />
|
<div class="d-flex">
|
||||||
|
<div class="inv-visual me-3">
|
||||||
|
<i class="fas fa-file-invoice-dollar"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h6 class="text-lg mb-0 mt-1">{{ invoice.client?.name || "Client inconnu" }}</h6>
|
||||||
|
<p class="text-sm mb-3">{{ invoice.lines?.length || 0 }} ligne(s) dans cette facture.</p>
|
||||||
|
<soft-badge :color="statusBadgeColor(invoice.status)" variant="gradient" size="sm">
|
||||||
|
{{ getStatusLabel(invoice.status) }}
|
||||||
|
</soft-badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #cta>
|
||||||
|
<div class="d-flex justify-content-end">
|
||||||
|
<div class="inv-status-select-wrap">
|
||||||
|
<label class="form-label text-xs mb-1">Changer le statut</label>
|
||||||
|
<select
|
||||||
|
class="form-select inv-status-select"
|
||||||
|
:value="selectedStatus"
|
||||||
|
:disabled="updating"
|
||||||
|
@change="onStatusSelect"
|
||||||
|
>
|
||||||
|
<option v-for="s in availableStatuses" :key="s" :value="s">
|
||||||
|
{{ getStatusLabel(s) }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm mt-2 mb-0 text-end">
|
||||||
|
Modifier le statut de la facture directement depuis cette section.
|
||||||
|
</p>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #timeline>
|
<template #timeline>
|
||||||
<div>
|
<h6 class="mb-3">Suivi facture</h6>
|
||||||
<h6 class="mb-3 text-sm">Historique</h6>
|
<invoice-timeline :history="invoice.history" />
|
||||||
<div v-if="invoice.history && invoice.history.length > 0">
|
|
||||||
<div
|
|
||||||
v-for="(entry, index) in invoice.history"
|
|
||||||
:key="index"
|
|
||||||
class="mb-2"
|
|
||||||
>
|
|
||||||
<span class="text-xs text-secondary">
|
|
||||||
{{ formatDate(entry.changed_at) }}
|
|
||||||
</span>
|
|
||||||
<p class="text-xs mb-0">{{ entry.comment }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p v-else class="text-xs text-secondary">Aucun historique</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #billing>
|
<template #payment>
|
||||||
<div>
|
<h6 class="mb-3">Lignes de facture</h6>
|
||||||
<h6 class="mb-3 text-sm">Informations Client</h6>
|
<invoice-lines-table :lines="invoice.lines" />
|
||||||
<p class="text-sm mb-1">
|
|
||||||
<strong>{{
|
<h6 class="mb-3 mt-4">Informations Client</h6>
|
||||||
invoice.client ? invoice.client.name : "Client inconnu"
|
<ul class="list-group">
|
||||||
}}</strong>
|
<li class="list-group-item border-0 d-flex p-4 mb-2 bg-gray-100 border-radius-lg">
|
||||||
</p>
|
<div class="d-flex flex-column">
|
||||||
<p class="text-xs text-secondary mb-1">
|
<h6 class="mb-3 text-sm">{{ invoice.client?.name || "Client inconnu" }}</h6>
|
||||||
{{ invoice.client ? invoice.client.email : "" }}
|
<span class="mb-2 text-xs">
|
||||||
</p>
|
Adresse email :
|
||||||
<p class="text-xs text-secondary mb-0">
|
<span class="text-dark ms-2 font-weight-bold">{{ invoice.client?.email || "—" }}</span>
|
||||||
{{ invoice.client ? invoice.client.phone : "" }}
|
</span>
|
||||||
</p>
|
<span class="mb-2 text-xs">
|
||||||
|
Téléphone :
|
||||||
|
<span class="text-dark ms-2 font-weight-bold">{{ invoice.client?.phone || "—" }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="text-xs">
|
||||||
|
Référence facture :
|
||||||
|
<span class="text-dark ms-2 font-weight-bold">{{ invoice.invoice_number || "—" }}</span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #summary>
|
<template #summary>
|
||||||
<invoice-summary
|
<h6 class="mb-3">Résumé Facture</h6>
|
||||||
:ht="invoice.total_ht"
|
<div class="d-flex justify-content-between">
|
||||||
:tva="invoice.total_tva"
|
<span class="mb-2 text-sm">Total HT :</span>
|
||||||
:ttc="invoice.total_ttc"
|
<span class="text-dark font-weight-bold ms-2">{{ formatCurrency(invoice.total_ht) }}</span>
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #actions>
|
|
||||||
<div class="d-flex justify-content-end">
|
|
||||||
<div class="position-relative d-inline-block me-2">
|
|
||||||
<soft-button
|
|
||||||
color="secondary"
|
|
||||||
variant="gradient"
|
|
||||||
@click="dropdownOpen = !dropdownOpen"
|
|
||||||
>
|
|
||||||
{{ getStatusLabel(invoice.status) }}
|
|
||||||
<i class="fas fa-chevron-down ms-2"></i>
|
|
||||||
</soft-button>
|
|
||||||
<ul
|
|
||||||
v-if="dropdownOpen"
|
|
||||||
class="dropdown-menu show position-absolute"
|
|
||||||
style="top: 100%; left: 0; z-index: 1000"
|
|
||||||
>
|
|
||||||
<li v-for="status in availableStatuses" :key="status">
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
:class="{ active: status === invoice.status }"
|
|
||||||
href="javascript:;"
|
|
||||||
@click="
|
|
||||||
changeStatus(status);
|
|
||||||
dropdownOpen = false;
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ getStatusLabel(status) }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
<soft-button color="info" variant="outline">
|
<span class="mb-2 text-sm">TVA :</span>
|
||||||
<i class="fas fa-file-pdf me-1"></i> Télécharger PDF
|
<span class="text-dark ms-2 font-weight-bold">{{ formatCurrency(invoice.total_tva) }}</span>
|
||||||
</soft-button>
|
</div>
|
||||||
|
<div class="d-flex justify-content-between mt-4">
|
||||||
|
<span class="mb-2 text-lg">Total TTC :</span>
|
||||||
|
<span class="text-dark text-lg ms-2 font-weight-bold">{{ formatCurrency(invoice.total_ttc) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</invoice-detail-template>
|
</invoice-detail-template>
|
||||||
@ -105,14 +131,13 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, defineProps } from "vue";
|
import { ref, onMounted, defineProps } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
|
||||||
import { useInvoiceStore } from "@/stores/invoiceStore";
|
import { useInvoiceStore } from "@/stores/invoiceStore";
|
||||||
import { useNotificationStore } from "@/stores/notification";
|
import { useNotificationStore } from "@/stores/notification";
|
||||||
import InvoiceDetailTemplate from "@/components/templates/Invoice/InvoiceDetailTemplate.vue";
|
import InvoiceDetailTemplate from "@/components/templates/Invoice/InvoiceDetailTemplate.vue";
|
||||||
import InvoiceHeader from "@/components/molecules/Invoice/InvoiceHeader.vue";
|
import InvoiceTimeline from "@/components/molecules/Invoice/InvoiceTimeline.vue";
|
||||||
import InvoiceLinesTable from "@/components/molecules/Invoice/InvoiceLinesTable.vue";
|
import InvoiceLinesTable from "@/components/molecules/Invoice/InvoiceLinesTable.vue";
|
||||||
import InvoiceSummary from "@/components/molecules/Invoice/InvoiceSummary.vue";
|
|
||||||
import SoftButton from "@/components/SoftButton.vue";
|
import SoftButton from "@/components/SoftButton.vue";
|
||||||
|
import SoftBadge from "@/components/SoftBadge.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
invoiceId: {
|
invoiceId: {
|
||||||
@ -121,30 +146,49 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const invoiceStore = useInvoiceStore();
|
const invoiceStore = useInvoiceStore();
|
||||||
const notificationStore = useNotificationStore();
|
const notificationStore = useNotificationStore();
|
||||||
|
|
||||||
const invoice = ref(null);
|
const invoice = ref(null);
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
|
const updating = ref(false);
|
||||||
const error = ref(null);
|
const error = ref(null);
|
||||||
const dropdownOpen = ref(false);
|
const selectedStatus = ref("brouillon");
|
||||||
|
|
||||||
onMounted(async () => {
|
const load = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
error.value = null;
|
||||||
try {
|
try {
|
||||||
const fetchedInvoice = await invoiceStore.fetchInvoice(props.invoiceId);
|
invoice.value = await invoiceStore.fetchInvoice(props.invoiceId);
|
||||||
invoice.value = fetchedInvoice;
|
selectedStatus.value = invoice.value?.status || "brouillon";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error.value = "Impossible de charger la facture.";
|
error.value = "Impossible de charger la facture.";
|
||||||
console.error(e);
|
console.error(e);
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
const formatDate = (dateString) => {
|
const reload = () => load();
|
||||||
if (!dateString) return "-";
|
onMounted(load);
|
||||||
return new Date(dateString).toLocaleDateString("fr-FR");
|
|
||||||
|
/* ── Helpers ── */
|
||||||
|
const formatDate = (d) =>
|
||||||
|
d
|
||||||
|
? new Date(d).toLocaleDateString("fr-FR", {
|
||||||
|
day: "2-digit",
|
||||||
|
month: "short",
|
||||||
|
year: "numeric",
|
||||||
|
})
|
||||||
|
: "—";
|
||||||
|
|
||||||
|
const formatCurrency = (value) => {
|
||||||
|
const amount = Number(value || 0);
|
||||||
|
return new Intl.NumberFormat("fr-FR", {
|
||||||
|
style: "currency",
|
||||||
|
currency: "EUR",
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
}).format(amount);
|
||||||
};
|
};
|
||||||
|
|
||||||
const availableStatuses = [
|
const availableStatuses = [
|
||||||
@ -158,8 +202,7 @@ const availableStatuses = [
|
|||||||
"avoir",
|
"avoir",
|
||||||
];
|
];
|
||||||
|
|
||||||
const getStatusLabel = (status) => {
|
const statusLabels = {
|
||||||
const labels = {
|
|
||||||
brouillon: "Brouillon",
|
brouillon: "Brouillon",
|
||||||
emise: "Émise",
|
emise: "Émise",
|
||||||
envoyee: "Envoyée",
|
envoyee: "Envoyée",
|
||||||
@ -168,41 +211,130 @@ const getStatusLabel = (status) => {
|
|||||||
echue: "Échue",
|
echue: "Échue",
|
||||||
annulee: "Annulée",
|
annulee: "Annulée",
|
||||||
avoir: "Avoir",
|
avoir: "Avoir",
|
||||||
};
|
|
||||||
return labels[status] || status;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* eslint-disable require-atomic-updates */
|
const statusIcons = {
|
||||||
const changeStatus = async (newStatus) => {
|
brouillon: "fas fa-pencil-alt",
|
||||||
if (!invoice.value?.id) return;
|
emise: "fas fa-file-export",
|
||||||
|
envoyee: "fas fa-paper-plane",
|
||||||
|
partiellement_payee: "fas fa-coins",
|
||||||
|
payee: "fas fa-check-circle",
|
||||||
|
echue: "fas fa-clock",
|
||||||
|
annulee: "fas fa-ban",
|
||||||
|
avoir: "fas fa-undo",
|
||||||
|
};
|
||||||
|
|
||||||
const currentInvoiceId = invoice.value.id;
|
const statusBadgeColor = (status) => {
|
||||||
|
const map = {
|
||||||
|
brouillon: "warning",
|
||||||
|
emise: "info",
|
||||||
|
envoyee: "info",
|
||||||
|
partiellement_payee: "warning",
|
||||||
|
payee: "success",
|
||||||
|
echue: "danger",
|
||||||
|
annulee: "dark",
|
||||||
|
avoir: "secondary",
|
||||||
|
};
|
||||||
|
return map[status] || "secondary";
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
const getStatusLabel = (s) => statusLabels[s] || s;
|
||||||
loading.value = true;
|
const statusIcon = (s) => statusIcons[s] || "fas fa-circle";
|
||||||
const updated = await invoiceStore.updateInvoice({
|
|
||||||
id: currentInvoiceId,
|
|
||||||
status: newStatus,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (invoice.value?.id === currentInvoiceId) {
|
const onStatusSelect = (event) => {
|
||||||
|
const newStatus = event.target.value;
|
||||||
|
selectedStatus.value = newStatus;
|
||||||
|
if (!invoice.value?.id || newStatus === invoice.value.status) return;
|
||||||
|
changeStatus(invoice.value.id, newStatus);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ── Status Update ── */
|
||||||
|
const changeStatus = (id, newStatus) => {
|
||||||
|
if (!id || updating.value) return;
|
||||||
|
updating.value = true;
|
||||||
|
invoiceStore
|
||||||
|
.updateInvoice({ id, status: newStatus })
|
||||||
|
.then((updated) => {
|
||||||
|
if (`${props.invoiceId}` !== `${id}`) return;
|
||||||
invoice.value = updated;
|
invoice.value = updated;
|
||||||
|
selectedStatus.value = updated?.status || newStatus;
|
||||||
notificationStore.success(
|
notificationStore.success(
|
||||||
"Statut mis à jour",
|
"Statut mis à jour",
|
||||||
`La facture est maintenant "${getStatusLabel(newStatus)}"`,
|
`La facture est maintenant "${getStatusLabel(newStatus)}"`,
|
||||||
3000
|
3000
|
||||||
);
|
);
|
||||||
}
|
})
|
||||||
} catch (e) {
|
.catch((e) => {
|
||||||
console.error("Failed to update status", e);
|
console.error(e);
|
||||||
notificationStore.error(
|
notificationStore.error("Erreur", "Impossible de mettre à jour le statut", 3000);
|
||||||
"Erreur",
|
})
|
||||||
"Impossible de mettre à jour le statut",
|
.finally(() => {
|
||||||
3000
|
updating.value = false;
|
||||||
);
|
});
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* ── States ── */
|
||||||
|
.detail-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 60vh;
|
||||||
|
color: #8898aa;
|
||||||
|
font-size: .9rem;
|
||||||
|
gap: .6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-state--error i {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
color: #f5365c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner-ring {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
border: 3px solid #e9ecef;
|
||||||
|
border-top-color: #5e72e4;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin .8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin { to { transform: rotate(360deg); } }
|
||||||
|
|
||||||
|
.btn-retry {
|
||||||
|
margin-top: .25rem;
|
||||||
|
padding: .4rem 1.1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1.5px solid #f5365c;
|
||||||
|
background: none;
|
||||||
|
color: #f5365c;
|
||||||
|
font-size: .8rem;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background .15s;
|
||||||
|
}
|
||||||
|
.btn-retry:hover { background: #fff0f3; }
|
||||||
|
|
||||||
|
.inv-visual {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: linear-gradient(135deg, #2dce89, #11cdef);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inv-status-select-wrap {
|
||||||
|
min-width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inv-status-select {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -0,0 +1,111 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h6 class="mb-3">Suivi de la Facture</h6>
|
||||||
|
<div class="timeline-scrollable">
|
||||||
|
<div class="timeline timeline-one-side">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in history"
|
||||||
|
:key="index"
|
||||||
|
class="timeline-block mb-3"
|
||||||
|
>
|
||||||
|
<span class="timeline-step">
|
||||||
|
<i :class="getStatusIcon(item.new_status)"></i>
|
||||||
|
</span>
|
||||||
|
<div class="timeline-content">
|
||||||
|
<h6 class="text-dark text-sm font-weight-bold mb-0">
|
||||||
|
{{ getStatusLabel(item.new_status) }}
|
||||||
|
</h6>
|
||||||
|
<p class="text-secondary font-weight-bold text-xs mt-1 mb-0">
|
||||||
|
{{ formatDate(item.changed_at) }}
|
||||||
|
<span v-if="item.changed_by">par {{ item.changed_by }}</span>
|
||||||
|
</p>
|
||||||
|
<p v-if="item.comment" class="text-sm mt-2 mb-0">
|
||||||
|
{{ item.comment }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="history.length === 0" class="text-sm text-secondary">
|
||||||
|
Aucun historique disponible.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { defineProps } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
history: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const formatDate = (dateString) => {
|
||||||
|
if (!dateString) return "-";
|
||||||
|
const date = new Date(dateString);
|
||||||
|
const options = {
|
||||||
|
day: "numeric",
|
||||||
|
month: "short",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
};
|
||||||
|
return date.toLocaleDateString("fr-FR", options);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusIcon = (status) => {
|
||||||
|
const map = {
|
||||||
|
brouillon: "ni ni-bell-55 text-secondary",
|
||||||
|
emise: "ni ni-send text-info",
|
||||||
|
envoyee: "ni ni-email-83 text-info",
|
||||||
|
partiellement_payee: "ni ni-money-coins text-warning",
|
||||||
|
payee: "ni ni-check-bold text-success text-gradient",
|
||||||
|
echue: "ni ni-time-alarm text-danger",
|
||||||
|
annulee: "ni ni-fat-remove text-danger text-gradient",
|
||||||
|
avoir: "ni ni-archive-2 text-dark",
|
||||||
|
};
|
||||||
|
return map[status] || "ni ni-bell-55 text-secondary";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusLabel = (status) => {
|
||||||
|
const labels = {
|
||||||
|
brouillon: "Facture créée (Brouillon)",
|
||||||
|
emise: "Facture émise",
|
||||||
|
envoyee: "Facture envoyée",
|
||||||
|
partiellement_payee: "Partiellement payée",
|
||||||
|
payee: "Facture payée",
|
||||||
|
echue: "Facture échue",
|
||||||
|
annulee: "Facture annulée",
|
||||||
|
avoir: "Avoir généré",
|
||||||
|
};
|
||||||
|
return labels[status] || status;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.timeline-scrollable {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-scrollable::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-scrollable::-webkit-scrollbar-track {
|
||||||
|
background: #f1f1f1;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-scrollable::-webkit-scrollbar-thumb {
|
||||||
|
background: #888;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeline-scrollable::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: #555;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,42 +1,41 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container-fluid py-4">
|
<div class="container-fluid py-4">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-8 mx-auto">
|
<div class="col-lg-10 mx-auto">
|
||||||
<div class="card mb-4">
|
<div class="card mb-4">
|
||||||
|
<div class="card-header p-3 pb-0">
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card-body p-3 pt-0">
|
<div class="card-body p-3 pt-0">
|
||||||
<hr class="horizontal dark mt-0 mb-4" />
|
<hr class="horizontal dark mt-0 mb-4" />
|
||||||
|
|
||||||
<!-- Product Lines Section -->
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-lg-6 col-md-6 col-12">
|
||||||
<slot name="lines"></slot>
|
<slot name="product"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-6 col-md-6 col-12 my-auto text-end">
|
||||||
|
<slot name="cta"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr class="horizontal dark mt-4 mb-4" />
|
<hr class="horizontal dark mt-4 mb-4" />
|
||||||
|
|
||||||
<div class="row">
|
<div class="row g-3">
|
||||||
<!-- Tracking/Timeline Section -->
|
|
||||||
<div class="col-lg-3 col-md-6 col-12">
|
<div class="col-lg-3 col-md-6 col-12">
|
||||||
<slot name="timeline"></slot>
|
<slot name="timeline"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Billing Info Section -->
|
|
||||||
<div class="col-lg-5 col-md-6 col-12">
|
<div class="col-lg-5 col-md-6 col-12">
|
||||||
<slot name="billing"></slot>
|
<slot name="payment"></slot>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Summary Section -->
|
<div class="col-lg-4 col-12 ms-auto">
|
||||||
<div class="col-lg-3 col-12 ms-auto">
|
|
||||||
<slot name="summary"></slot>
|
<slot name="summary"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer p-3">
|
|
||||||
<slot name="actions"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -128,7 +128,6 @@ watch(
|
|||||||
|
|
||||||
// Load data on component mount
|
// Load data on component mount
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
console.log("test");
|
|
||||||
fetchIntervention();
|
fetchIntervention();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user