2026-01-12 16:37:41 +03:00

183 lines
4.9 KiB
Vue

<template>
<div v-if="loading" class="text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div v-else-if="error" class="text-center py-5 text-danger">
{{ error }}
</div>
<quote-detail-template v-else-if="quote">
<template #header>
<quote-header
:reference="quote.reference"
:date="quote.quote_date"
:code="quote.reference"
/>
</template>
<template #lines>
<quote-lines-table :lines="quote.lines" />
</template>
<template #timeline>
<quote-timeline :history="quote.history" />
</template>
<template #billing>
<quote-billing-info
:client-name="quote.client ? quote.client.name : 'Client inconnu'"
:client-email="quote.client ? quote.client.email : ''"
:client-phone="quote.client ? quote.client.phone : ''"
/>
</template>
<template #summary>
<quote-summary
:ht="quote.total_ht"
:tva="quote.total_tva"
:ttc="quote.total_ttc"
/>
</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(quote.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 === quote.status }"
href="javascript:;"
@click="changeStatus(status); dropdownOpen = false;"
>
{{ getStatusLabel(status) }}
</a>
</li>
</ul>
</div>
</div>
</template>
</quote-detail-template>
</template>
<script setup>
import { ref, onMounted, defineProps } from "vue";
import { useRouter } from "vue-router";
import { useQuoteStore } from "@/stores/quoteStore";
import { useNotificationStore } from "@/stores/notification";
import QuoteDetailTemplate from "@/components/templates/Quote/QuoteDetailTemplate.vue";
import QuoteHeader from "@/components/molecules/Quote/QuoteHeader.vue";
import QuoteTimeline from "@/components/molecules/Quote/QuoteTimeline.vue";
import QuoteBillingInfo from "@/components/molecules/Quote/QuoteBillingInfo.vue";
import QuoteSummary from "@/components/molecules/Quote/QuoteSummary.vue";
import QuoteLinesTable from "@/components/molecules/Quote/QuoteLinesTable.vue";
import SoftButton from "@/components/SoftButton.vue";
const props = defineProps({
quoteId: {
type: [String, Number],
required: true,
},
});
const router = useRouter();
const quoteStore = useQuoteStore();
const notificationStore = useNotificationStore();
const quote = ref(null);
const loading = ref(true);
const error = ref(null);
const dropdownOpen = ref(false);
onMounted(async () => {
loading.value = true;
try {
const fetchedQuote = await quoteStore.fetchQuote(props.quoteId);
quote.value = fetchedQuote;
} catch (e) {
error.value = "Impossible de charger le devis.";
console.error(e);
} finally {
loading.value = false;
}
});
const goBack = () => {
router.back();
};
const formatDate = (dateString) => {
if (!dateString) return "-";
return new Date(dateString).toLocaleDateString("fr-FR");
};
const availableStatuses = [
"brouillon",
"envoye",
"accepte",
"refuse",
"expire",
"annule",
];
const getStatusLabel = (status) => {
const labels = {
brouillon: "Brouillon",
envoye: "Envoyé",
accepte: "Accepté",
refuse: "Refusé",
expire: "Expiré",
annule: "Annulé",
};
return labels[status] || status;
};
/* eslint-disable require-atomic-updates */
const changeStatus = async (newStatus) => {
if (!quote.value?.id) return;
// Capture the current quote ID to prevent race conditions
const currentQuoteId = quote.value.id;
try {
loading.value = true;
const updated = await quoteStore.updateQuote({
id: currentQuoteId,
status: newStatus,
});
// Only update if we're still viewing the same quote
if (quote.value?.id === currentQuoteId) {
quote.value = updated;
// Show success notification
notificationStore.success(
'Statut mis à jour',
`Le devis est maintenant "${getStatusLabel(newStatus)}"`,
3000
);
}
} catch (e) {
console.error("Failed to update status", e);
notificationStore.error(
'Erreur',
'Impossible de mettre à jour le statut',
3000
);
} finally {
loading.value = false;
}
};
</script>