Feat: front-end service et store pour les sous traitant

This commit is contained in:
kevin 2026-05-22 10:06:07 +03:00
parent 7506bdcf8b
commit 8c6787d03e
3 changed files with 535 additions and 0 deletions

View File

@ -1,3 +1,4 @@
export * from "./http";
export { default as AuthService } from "./auth";
export { default as WebmailService } from "./webmail";
export { default as SousTraitantService } from "./sousTraitant";

View File

@ -0,0 +1,189 @@
import { request } from "./http";
export type SousTraitantStatut = "actif" | "inactif" | "en_evaluation";
export interface SousTraitant {
id: number;
nom_entreprise: string;
siret: string | null;
forme_juridique: string | null;
code_ape: string | null;
adresse: string | null;
contact_principal: string;
telephone: string | null;
email: string | null;
site_web: string | null;
numero_contrat: string | null;
montant_contrat: string | number | null;
date_debut_contrat: string | null;
date_fin_contrat: string | null;
type_prestation: string | null;
conditions_paiement: string | null;
statut: SousTraitantStatut;
note_qualite: string | number | null;
certifications_labels: string[];
created_at: string;
updated_at: string;
}
export interface SousTraitantListResponse {
data: SousTraitant[];
meta?: {
current_page: number;
last_page: number;
per_page: number;
total: number;
from?: number;
to?: number;
stats?: {
actif: number;
inactif: number;
en_evaluation: number;
};
};
}
export interface SousTraitantResponse {
data: SousTraitant;
}
export interface CreateSousTraitantPayload {
nom_entreprise: string;
siret?: string | null;
forme_juridique?: string | null;
code_ape?: string | null;
adresse?: string | null;
contact_principal: string;
telephone?: string | null;
email?: string | null;
site_web?: string | null;
numero_contrat?: string | null;
montant_contrat?: number | string | null;
date_debut_contrat?: string | null;
date_fin_contrat?: string | null;
type_prestation?: string | null;
conditions_paiement?: string | null;
statut?: SousTraitantStatut;
note_qualite?: number | string | null;
certifications_labels?: string[];
}
export interface UpdateSousTraitantPayload
extends Partial<CreateSousTraitantPayload> {
id: number;
}
export const SousTraitantService = {
async getAllSousTraitants(params?: {
page?: number;
per_page?: number;
search?: string;
statut?: SousTraitantStatut;
sort_by?: string;
sort_direction?: "asc" | "desc";
}): Promise<SousTraitantListResponse> {
const response = await request<SousTraitantListResponse>({
url: "/api/sous-traitants",
method: "get",
params,
});
return response;
},
async getSousTraitant(id: number): Promise<SousTraitantResponse> {
const response = await request<SousTraitantResponse>({
url: `/api/sous-traitants/${id}`,
method: "get",
});
return response;
},
async createSousTraitant(
payload: CreateSousTraitantPayload
): Promise<SousTraitantResponse> {
const formattedPayload = this.transformSousTraitantPayload(payload);
const response = await request<SousTraitantResponse>({
url: "/api/sous-traitants",
method: "post",
data: formattedPayload,
});
return response;
},
async updateSousTraitant(
payload: UpdateSousTraitantPayload
): Promise<SousTraitantResponse> {
const { id, ...updateData } = payload;
const formattedPayload = this.transformSousTraitantPayload(updateData);
const response = await request<SousTraitantResponse>({
url: `/api/sous-traitants/${id}`,
method: "put",
data: formattedPayload,
});
return response;
},
async deleteSousTraitant(
id: number
): Promise<{ success?: boolean; message: string }> {
const response = await request<{ success?: boolean; message: string }>({
url: `/api/sous-traitants/${id}`,
method: "delete",
});
return response;
},
transformSousTraitantPayload(
payload: Partial<CreateSousTraitantPayload>
): Record<string, unknown> {
const transformed: Record<string, unknown> = { ...payload };
if (
Array.isArray(transformed.certifications_labels) &&
transformed.certifications_labels.length > 0
) {
transformed.certifications_labels = transformed.certifications_labels
.map((item) => (typeof item === "string" ? item.trim() : item))
.filter(Boolean);
}
Object.keys(transformed).forEach((key) => {
if (transformed[key] === undefined) {
delete transformed[key];
}
});
return transformed;
},
async searchSousTraitants(
query: string,
params?: {
exact_match?: boolean;
}
): Promise<SousTraitant[]> {
const response = await request<{
data: SousTraitant[];
count: number;
message: string;
}>({
url: "/api/sous-traitants/searchBy",
method: "get",
params: {
name: query,
exact_match: params?.exact_match || false,
},
});
return response.data;
},
};
export default SousTraitantService;

View File

@ -0,0 +1,345 @@
import { defineStore } from "pinia";
import { computed, ref } from "vue";
import SousTraitantService from "@/services/sousTraitant";
import type {
CreateSousTraitantPayload,
SousTraitant,
SousTraitantStatut,
UpdateSousTraitantPayload,
} from "@/services/sousTraitant";
export const useSousTraitantStore = defineStore("sousTraitant", () => {
const sousTraitants = ref<SousTraitant[]>([]);
const currentSousTraitant = ref<SousTraitant | null>(null);
const loading = ref(false);
const error = ref<string | null>(null);
const searchResults = ref<SousTraitant[]>([]);
const filters = ref<{
page: number;
per_page: number;
search?: string;
statut?: SousTraitantStatut;
sort_by?: string;
sort_direction?: "asc" | "desc";
}>({
page: 1,
per_page: 10,
sort_by: "created_at",
sort_direction: "desc",
});
const pagination = ref({
current_page: 1,
last_page: 1,
per_page: 10,
total: 0,
from: 0,
to: 0,
});
const allSousTraitants = computed(() => sousTraitants.value);
const activeSousTraitants = computed(() =>
sousTraitants.value.filter((item) => item.statut === "actif")
);
const inactiveSousTraitants = computed(() =>
sousTraitants.value.filter((item) => item.statut === "inactif")
);
const evaluatingSousTraitants = computed(() =>
sousTraitants.value.filter((item) => item.statut === "en_evaluation")
);
const isLoading = computed(() => loading.value);
const hasError = computed(() => error.value !== null);
const getError = computed(() => error.value);
const getPagination = computed(() => pagination.value);
const getSousTraitantById = computed(() => (id: number) =>
sousTraitants.value.find((item) => item.id === id)
);
const setLoading = (isLoading: boolean) => {
loading.value = isLoading;
};
const setError = (err: string | null) => {
error.value = err;
};
const clearError = () => {
error.value = null;
};
const setSousTraitants = (items: SousTraitant[]) => {
sousTraitants.value = items;
};
const setCurrentSousTraitant = (item: SousTraitant | null) => {
currentSousTraitant.value = item;
};
const setSearchResults = (items: SousTraitant[]) => {
searchResults.value = items;
};
const setPagination = (meta: any) => {
if (meta) {
const getValue = (val: any) => (Array.isArray(val) ? val[0] : val);
pagination.value = {
current_page: Number(getValue(meta.current_page)) || 1,
last_page: Number(getValue(meta.last_page)) || 1,
per_page: Number(getValue(meta.per_page)) || 10,
total: Number(getValue(meta.total)) || 0,
from: Number(getValue(meta.from)) || 0,
to: Number(getValue(meta.to)) || 0,
};
filters.value = {
...filters.value,
page: pagination.value.current_page,
per_page: pagination.value.per_page,
};
}
};
const setFilters = (params?: {
page?: number;
per_page?: number;
search?: string;
statut?: SousTraitantStatut;
sort_by?: string;
sort_direction?: "asc" | "desc";
}) => {
filters.value = {
...filters.value,
...params,
page: params?.page ?? filters.value.page ?? 1,
per_page: params?.per_page ?? filters.value.per_page ?? 10,
};
};
const fetchSousTraitants = async (params?: {
page?: number;
per_page?: number;
search?: string;
statut?: SousTraitantStatut;
sort_by?: string;
sort_direction?: "asc" | "desc";
}) => {
setLoading(true);
setError(null);
try {
setFilters(params);
const requestParams = Object.fromEntries(
Object.entries(filters.value).filter(
([, value]) => value !== undefined && value !== null && value !== ""
)
) as {
page?: number;
per_page?: number;
search?: string;
statut?: SousTraitantStatut;
sort_by?: string;
sort_direction?: "asc" | "desc";
};
const response =
await SousTraitantService.getAllSousTraitants(requestParams);
setSousTraitants(response.data);
if (response.meta) {
setPagination(response.meta);
}
return response;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Failed to fetch sous-traitants";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
const fetchSousTraitant = async (id: number) => {
setLoading(true);
setError(null);
try {
const response = await SousTraitantService.getSousTraitant(id);
setCurrentSousTraitant(response.data);
return response.data;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Failed to fetch sous-traitant";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
const createSousTraitant = async (payload: CreateSousTraitantPayload) => {
setLoading(true);
setError(null);
try {
const response = await SousTraitantService.createSousTraitant(payload);
sousTraitants.value.push(response.data);
setCurrentSousTraitant(response.data);
return response.data;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Failed to create sous-traitant";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
const updateSousTraitant = async (payload: UpdateSousTraitantPayload) => {
setLoading(true);
setError(null);
try {
const response = await SousTraitantService.updateSousTraitant(payload);
const updatedSousTraitant = response.data;
const index = sousTraitants.value.findIndex(
(item) => item.id === updatedSousTraitant.id
);
if (index !== -1) {
sousTraitants.value[index] = updatedSousTraitant;
}
if (
currentSousTraitant.value &&
currentSousTraitant.value.id === updatedSousTraitant.id
) {
setCurrentSousTraitant(updatedSousTraitant);
}
return updatedSousTraitant;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Failed to update sous-traitant";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
const deleteSousTraitant = async (id: number) => {
setLoading(true);
setError(null);
try {
const response = await SousTraitantService.deleteSousTraitant(id);
sousTraitants.value = sousTraitants.value.filter((item) => item.id !== id);
if (currentSousTraitant.value && currentSousTraitant.value.id === id) {
setCurrentSousTraitant(null);
}
return response;
} catch (err: any) {
const errorMessage =
err.response?.data?.message ||
err.message ||
"Failed to delete sous-traitant";
setError(errorMessage);
throw err;
} finally {
setLoading(false);
}
};
const searchSousTraitants = async (
query: string,
exactMatch: boolean = false
) => {
setLoading(true);
setError(null);
try {
const results = await SousTraitantService.searchSousTraitants(query, {
exact_match: exactMatch,
});
setSearchResults(results);
return results;
} catch (err) {
setError("Erreur lors de la recherche des sous-traitants");
console.error("Error searching sous-traitants:", err);
setSearchResults([]);
throw err;
} finally {
setLoading(false);
}
};
const clearCurrentSousTraitant = () => {
setCurrentSousTraitant(null);
};
const clearStore = () => {
sousTraitants.value = [];
currentSousTraitant.value = null;
searchResults.value = [];
error.value = null;
pagination.value = {
current_page: 1,
last_page: 1,
per_page: 10,
total: 0,
from: 0,
to: 0,
};
filters.value = {
page: 1,
per_page: 10,
sort_by: "created_at",
sort_direction: "desc",
};
};
return {
sousTraitants,
currentSousTraitant,
loading,
error,
searchResults,
filters,
allSousTraitants,
activeSousTraitants,
inactiveSousTraitants,
evaluatingSousTraitants,
isLoading,
hasError,
getError,
getPagination,
getSousTraitantById,
fetchSousTraitants,
fetchSousTraitant,
createSousTraitant,
updateSousTraitant,
deleteSousTraitant,
searchSousTraitants,
clearCurrentSousTraitant,
clearStore,
clearError,
};
});
export default useSousTraitantStore;