diff --git a/thanasoft-front/src/components/Atom/Stats/StatKpiCard.vue b/thanasoft-front/src/components/Atom/Stats/StatKpiCard.vue index a90db9d..1b8bfa0 100644 --- a/thanasoft-front/src/components/Atom/Stats/StatKpiCard.vue +++ b/thanasoft-front/src/components/Atom/Stats/StatKpiCard.vue @@ -74,7 +74,9 @@ export default defineComponent({ }, computed: { headerHtml(): string { - const val = this.suffix ? `${this.value}${this.suffix}` : String(this.value); + const val = this.suffix + ? `${this.value}${this.suffix}` + : String(this.value); if (!this.trend) return val; const isNeg = this.trend.startsWith("-"); const cls = isNeg ? "text-danger" : "text-success"; @@ -88,7 +90,9 @@ export default defineComponent({ }, methods: { renderChart() { - const canvas = document.getElementById(this.chartId) as HTMLCanvasElement | null; + const canvas = document.getElementById( + this.chartId + ) as HTMLCanvasElement | null; if (!canvas) return; const ctx = canvas.getContext("2d"); if (!ctx) return; diff --git a/thanasoft-front/src/components/Atom/Stats/StatProgressRow.vue b/thanasoft-front/src/components/Atom/Stats/StatProgressRow.vue index babbf3d..ffd5d7e 100644 --- a/thanasoft-front/src/components/Atom/Stats/StatProgressRow.vue +++ b/thanasoft-front/src/components/Atom/Stats/StatProgressRow.vue @@ -5,7 +5,7 @@ {{ label }} {{ count }} -
+
{{ value }}
- +
- {{ suffix }} + {{ + suffix + }}
@@ -24,8 +30,8 @@ diff --git a/thanasoft-front/src/components/Molecule/Stats/ConversionCard.vue b/thanasoft-front/src/components/Molecule/Stats/ConversionCard.vue index 33a0d11..25e3c12 100644 --- a/thanasoft-front/src/components/Molecule/Stats/ConversionCard.vue +++ b/thanasoft-front/src/components/Molecule/Stats/ConversionCard.vue @@ -1,18 +1,23 @@ diff --git a/thanasoft-front/src/components/Organism/CRM/ClientStatsDashboard.vue b/thanasoft-front/src/components/Organism/CRM/ClientStatsDashboard.vue index 39a8e1d..2163904 100644 --- a/thanasoft-front/src/components/Organism/CRM/ClientStatsDashboard.vue +++ b/thanasoft-front/src/components/Organism/CRM/ClientStatsDashboard.vue @@ -90,7 +90,20 @@ import TopClientsCard from "@/components/Molecule/Stats/TopClientsCard.vue"; import GeographicCard from "@/components/Molecule/Stats/GeographicCard.vue"; // Shared x-axis labels for sparklines -const MONTHS = ["Jan", "Fév", "Mar", "Avr", "Mai", "Juin", "Juil", "Aoû", "Sep", "Oct", "Nov", "Déc"]; +const MONTHS = [ + "Jan", + "Fév", + "Mar", + "Avr", + "Mai", + "Juin", + "Juil", + "Aoû", + "Sep", + "Oct", + "Nov", + "Déc", +]; export default defineComponent({ name: "ClientStatsDashboard", diff --git a/thanasoft-front/src/components/Organism/CRM/SousTraitantDetailPresentation.vue b/thanasoft-front/src/components/Organism/CRM/SousTraitantDetailPresentation.vue new file mode 100644 index 0000000..3e72499 --- /dev/null +++ b/thanasoft-front/src/components/Organism/CRM/SousTraitantDetailPresentation.vue @@ -0,0 +1,218 @@ + + + + + diff --git a/thanasoft-front/src/components/Organism/CRM/SousTraitantPresentation.vue b/thanasoft-front/src/components/Organism/CRM/SousTraitantPresentation.vue new file mode 100644 index 0000000..4e28971 --- /dev/null +++ b/thanasoft-front/src/components/Organism/CRM/SousTraitantPresentation.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/thanasoft-front/src/components/Organism/Interventions/intervention/InterventionDetailSidebar.vue b/thanasoft-front/src/components/Organism/Interventions/intervention/InterventionDetailSidebar.vue index 901eed7..7f9d98a 100644 --- a/thanasoft-front/src/components/Organism/Interventions/intervention/InterventionDetailSidebar.vue +++ b/thanasoft-front/src/components/Organism/Interventions/intervention/InterventionDetailSidebar.vue @@ -181,4 +181,3 @@ const tabs = [ box-shadow: 0 0 2rem 0 rgba(136, 152, 170, 0.15); } - diff --git a/thanasoft-front/src/components/Organism/Invoice/FinancialStatsDashboard.vue b/thanasoft-front/src/components/Organism/Invoice/FinancialStatsDashboard.vue index c0189b1..54bf7e0 100644 --- a/thanasoft-front/src/components/Organism/Invoice/FinancialStatsDashboard.vue +++ b/thanasoft-front/src/components/Organism/Invoice/FinancialStatsDashboard.vue @@ -70,7 +70,9 @@
- +
n === null @@ -145,10 +160,17 @@ export default defineComponent({ // ── Sparklines ──────────────────────────────────────────────────────── const kpiChartCA = computed(() => { const dataMap: Record = {}; - props.stats.revenue.monthly.forEach((m) => { dataMap[m.month] = m.total_ttc; }); + props.stats.revenue.monthly.forEach((m) => { + dataMap[m.month] = m.total_ttc; + }); return { labels: MONTHS, - datasets: [{ label: "CA", data: Array.from({ length: 12 }, (_, i) => dataMap[i + 1] ?? 0) }], + datasets: [ + { + label: "CA", + data: Array.from({ length: 12 }, (_, i) => dataMap[i + 1] ?? 0), + }, + ], }; }); @@ -157,7 +179,10 @@ export default defineComponent({ const data = Array.from({ length: 9 }, (_, i) => Math.max(0, Math.round(rate * (0.8 + (i / 8) * 0.2))) ); - return { labels: MONTHS.slice(0, 9), datasets: [{ label: "Conversion %", data }] }; + return { + labels: MONTHS.slice(0, 9), + datasets: [{ label: "Conversion %", data }], + }; }); const kpiChartPanier = computed(() => { @@ -165,7 +190,10 @@ export default defineComponent({ const data = Array.from({ length: 9 }, (_, i) => Math.round(avg * (0.85 + (i / 8) * 0.2)) ); - return { labels: MONTHS.slice(0, 9), datasets: [{ label: "Panier moyen", data }] }; + return { + labels: MONTHS.slice(0, 9), + datasets: [{ label: "Panier moyen", data }], + }; }); return { diff --git a/thanasoft-front/src/components/Organism/Parametrage/Emails/EmailSettingsPresentation.vue b/thanasoft-front/src/components/Organism/Parametrage/Emails/EmailSettingsPresentation.vue index 72bc9ba..fd595db 100644 --- a/thanasoft-front/src/components/Organism/Parametrage/Emails/EmailSettingsPresentation.vue +++ b/thanasoft-front/src/components/Organism/Parametrage/Emails/EmailSettingsPresentation.vue @@ -50,9 +50,7 @@ :sync-label=" syncing ? 'Synchronisation...' : 'Synchroniser la boîte' " - :smtp-test-label=" - smtpTesting ? 'Test SMTP...' : 'Tester le SMTP' - " + :smtp-test-label="smtpTesting ? 'Test SMTP...' : 'Tester le SMTP'" @update:field="$emit('update:field', $event)" @submit="$emit('submit-settings')" @sync="$emit('sync-mailbox')" @@ -185,7 +183,13 @@ const props = defineProps({ }, }); -defineEmits(["update:field", "submit-settings", "sync-mailbox", "test-smtp", "reset-form"]); +defineEmits([ + "update:field", + "submit-settings", + "sync-mailbox", + "test-smtp", + "reset-form", +]); const lastSyncedLabel = computed(() => { if (!props.settings?.last_synced_at) { diff --git a/thanasoft-front/src/components/Organism/Stock/StockStatsDashboard.vue b/thanasoft-front/src/components/Organism/Stock/StockStatsDashboard.vue index a2980a0..bd712a3 100644 --- a/thanasoft-front/src/components/Organism/Stock/StockStatsDashboard.vue +++ b/thanasoft-front/src/components/Organism/Stock/StockStatsDashboard.vue @@ -53,16 +53,16 @@ + + diff --git a/thanasoft-front/src/components/molecules/form/NewSousTraitantForm.vue b/thanasoft-front/src/components/molecules/form/NewSousTraitantForm.vue new file mode 100644 index 0000000..5476105 --- /dev/null +++ b/thanasoft-front/src/components/molecules/form/NewSousTraitantForm.vue @@ -0,0 +1,541 @@ + + + + + diff --git a/thanasoft-front/src/components/molecules/parametrage/emails/MailboxSettingsFormPanel.vue b/thanasoft-front/src/components/molecules/parametrage/emails/MailboxSettingsFormPanel.vue index a2102d0..1fc0230 100644 --- a/thanasoft-front/src/components/molecules/parametrage/emails/MailboxSettingsFormPanel.vue +++ b/thanasoft-front/src/components/molecules/parametrage/emails/MailboxSettingsFormPanel.vue @@ -320,7 +320,13 @@ const props = defineProps({ }, }); -const emit = defineEmits(["update:field", "submit", "sync", "test-smtp", "reset"]); +const emit = defineEmits([ + "update:field", + "submit", + "sync", + "test-smtp", + "reset", +]); const updateField = (field, value) => { emit("update:field", { field, value }); diff --git a/thanasoft-front/src/components/templates/CRM/NewSousTraitantTemplate.vue b/thanasoft-front/src/components/templates/CRM/NewSousTraitantTemplate.vue new file mode 100644 index 0000000..2386f8e --- /dev/null +++ b/thanasoft-front/src/components/templates/CRM/NewSousTraitantTemplate.vue @@ -0,0 +1,15 @@ + diff --git a/thanasoft-front/src/components/templates/CRM/SousTraitantDetailTemplate.vue b/thanasoft-front/src/components/templates/CRM/SousTraitantDetailTemplate.vue new file mode 100644 index 0000000..f007b12 --- /dev/null +++ b/thanasoft-front/src/components/templates/CRM/SousTraitantDetailTemplate.vue @@ -0,0 +1,49 @@ + + + diff --git a/thanasoft-front/src/components/templates/CRM/SousTraitantTemplate.vue b/thanasoft-front/src/components/templates/CRM/SousTraitantTemplate.vue new file mode 100644 index 0000000..05a42cf --- /dev/null +++ b/thanasoft-front/src/components/templates/CRM/SousTraitantTemplate.vue @@ -0,0 +1,67 @@ + + + diff --git a/thanasoft-front/src/router/index.js b/thanasoft-front/src/router/index.js index 9730d63..2e6ab7b 100644 --- a/thanasoft-front/src/router/index.js +++ b/thanasoft-front/src/router/index.js @@ -525,6 +525,11 @@ const routes = [ name: "Gestion sous-traitants", component: () => import("@/views/pages/SousTraitants/SousTraitants.vue"), }, + { + path: "/sous-traitants/new", + name: "Creation sous-traitant", + component: () => import("@/views/pages/SousTraitants/AddSousTraitant.vue"), + }, { path: "/sous-traitants/contacts", name: "Contacts sous-traitants", @@ -545,6 +550,12 @@ const routes = [ name: "Statistiques sous-traitants", component: () => import("@/views/pages/SousTraitants/Statistiques.vue"), }, + { + path: "/sous-traitants/:id", + name: "Sous-traitant details", + component: () => + import("@/views/pages/SousTraitants/SousTraitantDetails.vue"), + }, // Ventes { path: "/ventes/devis", diff --git a/thanasoft-front/src/services/leave.ts b/thanasoft-front/src/services/leave.ts index 54768d8..1acb6cf 100644 --- a/thanasoft-front/src/services/leave.ts +++ b/thanasoft-front/src/services/leave.ts @@ -147,16 +147,16 @@ export const LeaveService = { }); }, - async deleteLeave( - id: number - ): Promise<{ message: string; status?: string }> { + async deleteLeave(id: number): Promise<{ message: string; status?: string }> { return request<{ message: string; status?: string }>({ url: `/api/leaves/${id}`, method: "delete", }); }, - transformLeavePayload(payload: Partial): Record { + transformLeavePayload( + payload: Partial + ): Record { const transformed: Record = { ...payload }; Object.keys(transformed).forEach((key) => { @@ -171,4 +171,4 @@ export const LeaveService = { unwrapLeave, }; -export default LeaveService; \ No newline at end of file +export default LeaveService; diff --git a/thanasoft-front/src/services/stockStatistics.ts b/thanasoft-front/src/services/stockStatistics.ts index 9966512..d777de1 100644 --- a/thanasoft-front/src/services/stockStatistics.ts +++ b/thanasoft-front/src/services/stockStatistics.ts @@ -1,4 +1,4 @@ -import axios from 'axios'; +import axios from "axios"; export interface ProductRotation { product_id: number; @@ -41,8 +41,8 @@ export interface StockStatisticsResponse { data: StockStatistics; } -const API_BASE = process.env.VUE_APP_API_BASE_URL || ''; -const TOKEN = localStorage.getItem('auth_token'); +const API_BASE = process.env.VUE_APP_API_BASE_URL || ""; +const TOKEN = localStorage.getItem("auth_token"); export const StockStatisticsService = { async getStatistics(): Promise { @@ -59,7 +59,7 @@ export const StockStatisticsService = { } catch (error: any) { throw new Error( error.response?.data?.message || - 'Erreur lors de la récupération des statistiques de stock' + "Erreur lors de la récupération des statistiques de stock" ); } }, diff --git a/thanasoft-front/src/stores/clientStatisticsStore.ts b/thanasoft-front/src/stores/clientStatisticsStore.ts index d5d782f..e63e6f0 100644 --- a/thanasoft-front/src/stores/clientStatisticsStore.ts +++ b/thanasoft-front/src/stores/clientStatisticsStore.ts @@ -3,54 +3,51 @@ import { ref, computed } from "vue"; import ClientStatisticsService from "@/services/clientStatistics"; import type { ClientStatistics } from "@/services/clientStatistics"; -export const useClientStatisticsStore = defineStore( - "clientStatistics", - () => { - // ─── State ───────────────────────────────────────────────────────────── - const statistics = ref(null); - const loading = ref(false); - const error = ref(null); +export const useClientStatisticsStore = defineStore("clientStatistics", () => { + // ─── State ───────────────────────────────────────────────────────────── + const statistics = ref(null); + const loading = ref(false); + const error = ref(null); - // ─── Getters ─────────────────────────────────────────────────────────── - const isLoading = computed(() => loading.value); - const hasError = computed(() => error.value !== null); - const getError = computed(() => error.value); - const hasData = computed(() => statistics.value !== null); + // ─── Getters ─────────────────────────────────────────────────────────── + const isLoading = computed(() => loading.value); + const hasError = computed(() => error.value !== null); + const getError = computed(() => error.value); + const hasData = computed(() => statistics.value !== null); - // ─── Actions ─────────────────────────────────────────────────────────── - async function fetchStatistics(): Promise { - loading.value = true; - error.value = null; - try { - statistics.value = await ClientStatisticsService.getStatistics(); - } catch (err: unknown) { - error.value = - err instanceof Error - ? err.message - : "Erreur lors du chargement des statistiques."; - } finally { - loading.value = false; - } + // ─── Actions ─────────────────────────────────────────────────────────── + async function fetchStatistics(): Promise { + loading.value = true; + error.value = null; + try { + statistics.value = await ClientStatisticsService.getStatistics(); + } catch (err: unknown) { + error.value = + err instanceof Error + ? err.message + : "Erreur lors du chargement des statistiques."; + } finally { + loading.value = false; } - - function clearStatistics(): void { - statistics.value = null; - error.value = null; - } - - return { - // state - statistics, - loading, - error, - // getters - isLoading, - hasError, - getError, - hasData, - // actions - fetchStatistics, - clearStatistics, - }; } -); + + function clearStatistics(): void { + statistics.value = null; + error.value = null; + } + + return { + // state + statistics, + loading, + error, + // getters + isLoading, + hasError, + getError, + hasData, + // actions + fetchStatistics, + clearStatistics, + }; +}); diff --git a/thanasoft-front/src/stores/leaveStore.ts b/thanasoft-front/src/stores/leaveStore.ts index a2ec571..c8abff0 100644 --- a/thanasoft-front/src/stores/leaveStore.ts +++ b/thanasoft-front/src/stores/leaveStore.ts @@ -223,7 +223,9 @@ export const useLeaveStore = defineStore("leave", () => { const response = await LeaveService.updateLeave(payload); const updatedLeave = LeaveService.unwrapLeave(response); - const index = leaves.value.findIndex((leave) => leave.id === updatedLeave.id); + const index = leaves.value.findIndex( + (leave) => leave.id === updatedLeave.id + ); if (index !== -1) { leaves.value[index] = updatedLeave; } @@ -309,4 +311,4 @@ export const useLeaveStore = defineStore("leave", () => { clearCurrentLeave, clearStore, }; -}); \ No newline at end of file +}); diff --git a/thanasoft-front/src/stores/sousTraitantStore.ts b/thanasoft-front/src/stores/sousTraitantStore.ts index 36a8d26..f89d2e6 100644 --- a/thanasoft-front/src/stores/sousTraitantStore.ts +++ b/thanasoft-front/src/stores/sousTraitantStore.ts @@ -143,8 +143,9 @@ export const useSousTraitantStore = defineStore("sousTraitant", () => { sort_direction?: "asc" | "desc"; }; - const response = - await SousTraitantService.getAllSousTraitants(requestParams); + const response = await SousTraitantService.getAllSousTraitants( + requestParams + ); setSousTraitants(response.data); if (response.meta) { setPagination(response.meta); @@ -245,7 +246,9 @@ export const useSousTraitantStore = defineStore("sousTraitant", () => { try { const response = await SousTraitantService.deleteSousTraitant(id); - sousTraitants.value = sousTraitants.value.filter((item) => item.id !== id); + sousTraitants.value = sousTraitants.value.filter( + (item) => item.id !== id + ); if (currentSousTraitant.value && currentSousTraitant.value.id === id) { setCurrentSousTraitant(null); diff --git a/thanasoft-front/src/stores/stockStatisticsStore.ts b/thanasoft-front/src/stores/stockStatisticsStore.ts index 39a1f61..e8a616a 100644 --- a/thanasoft-front/src/stores/stockStatisticsStore.ts +++ b/thanasoft-front/src/stores/stockStatisticsStore.ts @@ -1,6 +1,6 @@ -import { defineStore } from 'pinia'; -import { StockStatisticsService } from '@/services/stockStatistics'; -import type { StockStatistics } from '@/services/stockStatistics'; +import { defineStore } from "pinia"; +import { StockStatisticsService } from "@/services/stockStatistics"; +import type { StockStatistics } from "@/services/stockStatistics"; interface StockStatisticsState { statistics: StockStatistics | null; @@ -8,7 +8,7 @@ interface StockStatisticsState { error: string | null; } -export const useStockStatisticsStore = defineStore('stockStatistics', { +export const useStockStatisticsStore = defineStore("stockStatistics", { state: (): StockStatisticsState => ({ statistics: null, loading: false, @@ -18,7 +18,7 @@ export const useStockStatisticsStore = defineStore('stockStatistics', { getters: { isLoading: (state) => state.loading, hasError: (state) => state.error !== null, - getError: (state) => state.error || '', + getError: (state) => state.error || "", hasData: (state) => state.statistics !== null, }, diff --git a/thanasoft-front/src/views/pages/Clients/Statistiques.vue b/thanasoft-front/src/views/pages/Clients/Statistiques.vue index 31bd1e4..6d78c4c 100644 --- a/thanasoft-front/src/views/pages/Clients/Statistiques.vue +++ b/thanasoft-front/src/views/pages/Clients/Statistiques.vue @@ -79,4 +79,3 @@ export default defineComponent({ }, }); - diff --git a/thanasoft-front/src/views/pages/Parametrage/Emails.vue b/thanasoft-front/src/views/pages/Parametrage/Emails.vue index fcc8783..d80fdff 100644 --- a/thanasoft-front/src/views/pages/Parametrage/Emails.vue +++ b/thanasoft-front/src/views/pages/Parametrage/Emails.vue @@ -193,7 +193,8 @@ const testSmtp = async () => { } catch { notificationStore.error( "Erreur de test SMTP", - mailboxSettingsStore.error || "Impossible de tester la configuration SMTP." + mailboxSettingsStore.error || + "Impossible de tester la configuration SMTP." ); } }; diff --git a/thanasoft-front/src/views/pages/Planning.vue b/thanasoft-front/src/views/pages/Planning.vue index 3a09f9a..8de7103 100644 --- a/thanasoft-front/src/views/pages/Planning.vue +++ b/thanasoft-front/src/views/pages/Planning.vue @@ -100,12 +100,14 @@ const practitioners = computed( ); const collaborators = computed(() => - practitioners.value.map((p) => ({ - id: p.employee_id || p.employee?.id, - name: - `${p.employee?.first_name || ""} ${p.employee?.last_name || ""}`.trim() || - `Collaborateur #${p.id}`, - })) + practitioners.value + .map((p) => ({ + id: p.employee_id || p.employee?.id, + name: + `${p.employee?.first_name || ""} ${ + p.employee?.last_name || "" + }`.trim() || `Collaborateur #${p.id}`, + })) .filter((collaborator) => Boolean(collaborator.id)) ); @@ -136,7 +138,9 @@ const currentEmployee = computed(() => { ); }); -const currentEmployeeName = computed(() => currentEmployee.value?.full_name || ""); +const currentEmployeeName = computed( + () => currentEmployee.value?.full_name || "" +); const resetLeaveForm = () => { leaveForm.value = { diff --git a/thanasoft-front/src/views/pages/SousTraitants/AddSousTraitant.vue b/thanasoft-front/src/views/pages/SousTraitants/AddSousTraitant.vue new file mode 100644 index 0000000..cd0a5d4 --- /dev/null +++ b/thanasoft-front/src/views/pages/SousTraitants/AddSousTraitant.vue @@ -0,0 +1,54 @@ + + + diff --git a/thanasoft-front/src/views/pages/SousTraitants/SousTraitantDetails.vue b/thanasoft-front/src/views/pages/SousTraitants/SousTraitantDetails.vue new file mode 100644 index 0000000..829a674 --- /dev/null +++ b/thanasoft-front/src/views/pages/SousTraitants/SousTraitantDetails.vue @@ -0,0 +1,74 @@ + + + diff --git a/thanasoft-front/src/views/pages/SousTraitants/SousTraitants.vue b/thanasoft-front/src/views/pages/SousTraitants/SousTraitants.vue index a6d8e08..8976875 100644 --- a/thanasoft-front/src/views/pages/SousTraitants/SousTraitants.vue +++ b/thanasoft-front/src/views/pages/SousTraitants/SousTraitants.vue @@ -1,11 +1,57 @@ -