768 lines
23 KiB
TypeScript
768 lines
23 KiB
TypeScript
import apiClient from "./client";
|
|
import type {
|
|
AuthResponse,
|
|
ClientResponse,
|
|
CommandResponse,
|
|
Product,
|
|
DeliveryPerson,
|
|
DeliveryPersonsStats,
|
|
Alert,
|
|
} from "./types";
|
|
|
|
const V2 = "http://5.181.0.112/api/v2";
|
|
const CABINE_URL = "http://5.181.0.112/api/v1/cabine";
|
|
|
|
// ============================================
|
|
// AUTH
|
|
// ============================================
|
|
|
|
export const loginAdmin = async (
|
|
username: string,
|
|
password: string,
|
|
): Promise<AuthResponse> => {
|
|
try {
|
|
const { data } = await apiClient.post(`${V2}/admin/auth/login`, {
|
|
username,
|
|
password,
|
|
});
|
|
if (!data.access_token) {
|
|
return { success: false, message: "Token non reçu du serveur" };
|
|
}
|
|
return {
|
|
success: true,
|
|
message: "Connexion réussie",
|
|
access_token: data.access_token,
|
|
token_type: data.token_type,
|
|
expires_in: data.expires_in,
|
|
user: data.user,
|
|
};
|
|
} catch (error: any) {
|
|
return {
|
|
success: false,
|
|
message:
|
|
error.response?.data?.error ||
|
|
error.message ||
|
|
"Erreur de connexion",
|
|
};
|
|
}
|
|
};
|
|
|
|
export const logoutAdmin = async (): Promise<void> => {
|
|
try {
|
|
await apiClient.post(`${V2}/admin/auth/logout`);
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
};
|
|
|
|
// ============================================
|
|
// CLIENTS
|
|
// ============================================
|
|
|
|
export const getAllClients = async (): Promise<ClientResponse[]> => {
|
|
const { data } = await apiClient.get(`${V2}/admin/protected/all/clients`);
|
|
return data.clients || [];
|
|
};
|
|
|
|
export const getAllUsers = async () => {
|
|
const { data } = await apiClient.get(`${V2}/admin/protected/all/users`);
|
|
return data.users || [];
|
|
};
|
|
|
|
export const updateClientByAdmin = async (
|
|
clientId: number,
|
|
updates: Record<string, any>,
|
|
) => {
|
|
const { data } = await apiClient.put(
|
|
`${V2}/admin/protected/clients/${clientId}`,
|
|
updates,
|
|
);
|
|
return { success: true, message: data.message, client: data.client };
|
|
};
|
|
|
|
export const updateUserByAdmin = async (
|
|
userId: number,
|
|
updates: Record<string, any>,
|
|
) => {
|
|
const { data } = await apiClient.put(
|
|
`${V2}/admin/protected/users/${userId}`,
|
|
updates,
|
|
);
|
|
return { success: true, message: data.message, user: data.user };
|
|
};
|
|
|
|
// ============================================
|
|
// COMMANDES
|
|
// ============================================
|
|
|
|
export const getAllCommands = async (status?: string, username?: string) => {
|
|
let url = `${V2}/admin/protected/orders`;
|
|
const params: string[] = [];
|
|
if (status) params.push(`status=${status}`);
|
|
if (username) params.push(`username=${username}`);
|
|
if (params.length) url += `?${params.join("&")}`;
|
|
|
|
const { data } = await apiClient.get(url);
|
|
return {
|
|
success: true,
|
|
commands: data.commands || [],
|
|
count: data.count || 0,
|
|
};
|
|
};
|
|
|
|
export const getCommandByID = async (commandId: number) => {
|
|
const { data } = await apiClient.get(
|
|
`${V2}/admin/protected/orders/${commandId}`,
|
|
);
|
|
return { success: true, command: data.command };
|
|
};
|
|
|
|
export const getCommandItems = async (commandId: number) => {
|
|
const { data } = await apiClient.get(
|
|
`${V2}/admin/protected/orders/${commandId}/items`,
|
|
);
|
|
return {
|
|
success: true,
|
|
items: data.items || [],
|
|
count: data.count || 0,
|
|
command_info: data.command_info,
|
|
client_info: data.client_info,
|
|
};
|
|
};
|
|
|
|
export const deleteCommand = async (commandId: number) => {
|
|
const { data } = await apiClient.delete(
|
|
`${V2}/admin/protected/orders/${commandId}`,
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
export const deleteCommandItem = async (commandId: number, itemId: number) => {
|
|
const { data } = await apiClient.delete(
|
|
`${V2}/admin/protected/orders/${commandId}/items/${itemId}`,
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
export const updateCommandStatus = async (
|
|
commandId: number,
|
|
newStatus: string,
|
|
) => {
|
|
const { data } = await apiClient.put(
|
|
`${V2}/admin/protected/orders/${commandId}/status`,
|
|
{ status: newStatus },
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
export const updateCommandAddress = async (
|
|
commandId: number,
|
|
newAddress: string,
|
|
) => {
|
|
const { data } = await apiClient.put(
|
|
`${V2}/admin/protected/orders/${commandId}/address`,
|
|
{ delivery_address: newAddress },
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
export const validateCommand = async (commandId: number) => {
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/orders/${commandId}/force-validate`,
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
export const proposeAddressChangeAdmin = async (
|
|
commandId: number,
|
|
proposedAddress: string,
|
|
) => {
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/orders/${commandId}/propose-address`,
|
|
{ proposed_address: proposedAddress },
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
export const notifyClientToDescend = async (commandId: number) => {
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/orders/${commandId}/notify-client`,
|
|
);
|
|
return {
|
|
success: true,
|
|
message: data.message,
|
|
client_username: data.client_username,
|
|
};
|
|
};
|
|
|
|
export const confirmReceptionAdmin = async (commandId: number) => {
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/orders/${commandId}/confirm-reception`,
|
|
);
|
|
return {
|
|
success: true,
|
|
message: data.message,
|
|
points_earned: data.points_earned,
|
|
client_username: data.client_username,
|
|
};
|
|
};
|
|
|
|
// ============================================
|
|
// LIVREURS
|
|
// ============================================
|
|
|
|
export const getAvailableDeliveryPersons = async () => {
|
|
const { data } = await apiClient.get(
|
|
`${V2}/admin/protected/delivery-persons`,
|
|
);
|
|
return {
|
|
success: true,
|
|
livreurs: data.livreurs || [],
|
|
count: data.count || 0,
|
|
};
|
|
};
|
|
|
|
export const assignDeliveryPerson = async (
|
|
commandId: number,
|
|
deliveryUsername: string,
|
|
) => {
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/delivery-persons/${deliveryUsername}/assign/${commandId}`,
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
export const getDeliveryPersonDetails = async (username: string) => {
|
|
const { data } = await apiClient.get(
|
|
`${V2}/admin/protected/delivery-persons/${username}`,
|
|
);
|
|
return data.deliveryman || data;
|
|
};
|
|
|
|
const parseStatus = (status: any): "available" | "busy" | "offline" => {
|
|
if (!status) return "offline";
|
|
if (status === "available" || status === "busy" || status === "offline")
|
|
return status;
|
|
if (typeof status === "string" && status.startsWith("{")) {
|
|
try {
|
|
const obj = JSON.parse(status);
|
|
if (
|
|
obj.status === "available" ||
|
|
obj.status === "busy" ||
|
|
obj.status === "offline"
|
|
)
|
|
return obj.status;
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
}
|
|
return "offline";
|
|
};
|
|
|
|
export const getAllDeliveryPersonsWithDetails = async (): Promise<{
|
|
success: boolean;
|
|
deliveryPersons: DeliveryPerson[];
|
|
count: number;
|
|
stats: DeliveryPersonsStats;
|
|
}> => {
|
|
try {
|
|
const { data } = await apiClient.get(
|
|
`${V2}/admin/protected/delivery-persons`,
|
|
);
|
|
const livreurs = data.livreurs || [];
|
|
|
|
const enriched = await Promise.all(
|
|
livreurs.map(async (l: any): Promise<DeliveryPerson> => {
|
|
try {
|
|
const details = await getDeliveryPersonDetails(l.username);
|
|
const parsedStatus = parseStatus(details.status);
|
|
const hasLoc =
|
|
details.location?.latitude &&
|
|
details.location?.longitude;
|
|
return {
|
|
id: l.id,
|
|
username: l.username,
|
|
status: parsedStatus,
|
|
location: {
|
|
latitude: hasLoc ? details.location.latitude : 0,
|
|
longitude: hasLoc ? details.location.longitude : 0,
|
|
last_update: details.location?.last_update
|
|
? new Date(
|
|
details.location.last_update * 1000,
|
|
).toISOString()
|
|
: new Date().toISOString(),
|
|
is_recent: details.location?.is_recent || false,
|
|
},
|
|
stats: {
|
|
total_deliveries: details.total_deliveries || 0,
|
|
completed_today: details.completed_deliveries || 0,
|
|
queue_size: details.queue_size || 0,
|
|
current_command: details.current_command || null,
|
|
},
|
|
};
|
|
} catch {
|
|
return {
|
|
id: l.id,
|
|
username: l.username,
|
|
status: "offline",
|
|
location: {
|
|
latitude: 0,
|
|
longitude: 0,
|
|
last_update: new Date().toISOString(),
|
|
is_recent: false,
|
|
},
|
|
stats: {
|
|
total_deliveries: 0,
|
|
completed_today: 0,
|
|
queue_size: 0,
|
|
current_command: null,
|
|
},
|
|
};
|
|
}
|
|
}),
|
|
);
|
|
|
|
const stats: DeliveryPersonsStats = {
|
|
total: enriched.length,
|
|
available: enriched.filter((d) => d.status === "available").length,
|
|
busy: enriched.filter((d) => d.status === "busy").length,
|
|
offline: enriched.filter((d) => d.status === "offline").length,
|
|
active_deliveries: enriched.filter(
|
|
(d) => d.stats.current_command !== null,
|
|
).length,
|
|
};
|
|
|
|
return {
|
|
success: true,
|
|
deliveryPersons: enriched,
|
|
count: enriched.length,
|
|
stats,
|
|
};
|
|
} catch {
|
|
return {
|
|
success: false,
|
|
deliveryPersons: [],
|
|
count: 0,
|
|
stats: {
|
|
total: 0,
|
|
available: 0,
|
|
busy: 0,
|
|
offline: 0,
|
|
active_deliveries: 0,
|
|
},
|
|
};
|
|
}
|
|
};
|
|
|
|
export const getDeliverymanLocationForCommand = async (commandId: number) => {
|
|
try {
|
|
const { data } = await apiClient.get(
|
|
`${V2}/admin/protected/commands/${commandId}/deliveryman/location`,
|
|
);
|
|
return { success: true, data: data.data };
|
|
} catch (error: any) {
|
|
return {
|
|
success: false,
|
|
error: error.response?.data?.error || "Erreur",
|
|
};
|
|
}
|
|
};
|
|
|
|
// ============================================
|
|
// PRODUITS
|
|
// ============================================
|
|
|
|
export const getAllProductsAdmin = async (): Promise<{
|
|
success: boolean;
|
|
data: Product[];
|
|
count: number;
|
|
}> => {
|
|
try {
|
|
const { data } = await apiClient.get(`${V2}/admin/protected/products`);
|
|
return { success: true, data: data.data || [], count: data.count || 0 };
|
|
} catch {
|
|
return { success: false, data: [], count: 0 };
|
|
}
|
|
};
|
|
|
|
export const createProductAdmin = async (formData: FormData) => {
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/products`,
|
|
formData,
|
|
{
|
|
headers: { "Content-Type": "multipart/form-data" },
|
|
timeout: 120000,
|
|
},
|
|
);
|
|
return { success: true, data: data.data, message: data.message };
|
|
};
|
|
|
|
export const updateProductAdmin = async (
|
|
productId: number,
|
|
updates: Record<string, any>,
|
|
) => {
|
|
const { data } = await apiClient.put(
|
|
`${V2}/admin/protected/products/${productId}`,
|
|
updates,
|
|
);
|
|
return { success: true, data: data.data, message: data.message };
|
|
};
|
|
|
|
export const deleteProductAdmin = async (productId: number) => {
|
|
const { data } = await apiClient.delete(
|
|
`${V2}/admin/protected/products/${productId}`,
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
// ============================================
|
|
// CREATION UTILISATEURS / CLIENTS
|
|
// ============================================
|
|
|
|
export const createClientByAdmin = async (data: {
|
|
username: string;
|
|
password: string;
|
|
nom: string;
|
|
prenom: string;
|
|
telephone: string;
|
|
}) => {
|
|
const { data: res } = await apiClient.post(
|
|
`https://uber-stup.club/api/v1/auth/register`,
|
|
data,
|
|
);
|
|
return {
|
|
success: true,
|
|
message: res.message || "Client créé",
|
|
user: res.user,
|
|
};
|
|
};
|
|
|
|
export const createUserByAdmin = async (data: {
|
|
username: string;
|
|
password: string;
|
|
role: string;
|
|
}) => {
|
|
const { data: res } = await apiClient.post(
|
|
`${V2}/admin/auth/register`,
|
|
data,
|
|
);
|
|
return {
|
|
success: true,
|
|
message: res.message || "Utilisateur créé",
|
|
user: res.user,
|
|
};
|
|
};
|
|
|
|
// ============================================
|
|
// SUPPRESSION UTILISATEURS / CLIENTS
|
|
// ============================================
|
|
|
|
export const deleteUserAdmin = async (userId: number) => {
|
|
const { data } = await apiClient.delete(
|
|
`${V2}/admin/protected/users/${userId}`,
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
export const deleteClientAdmin = async (clientId: number) => {
|
|
const { data } = await apiClient.delete(
|
|
`${V2}/admin/protected/clients/${clientId}`,
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
// ============================================
|
|
// PARRAINAGE ADMIN
|
|
// ============================================
|
|
|
|
export const creditClientReferral = async (
|
|
username: string,
|
|
amount: number,
|
|
): Promise<{ success: boolean; balance?: number; message?: string }> => {
|
|
try {
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/client/${username}/referral/credit`,
|
|
{ amount },
|
|
);
|
|
return { success: true, balance: data.balance, message: data.message };
|
|
} catch (error: any) {
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.error || "Erreur crédit parrainage",
|
|
};
|
|
}
|
|
};
|
|
|
|
export const getClientReferralAdmin = async (
|
|
username: string,
|
|
): Promise<{ success: boolean; balance?: number; message?: string }> => {
|
|
try {
|
|
const { data } = await apiClient.get(
|
|
`${V2}/admin/protected/client/${username}/referral`,
|
|
);
|
|
return { success: true, balance: data.balance };
|
|
} catch (error: any) {
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.error || "Erreur récupération",
|
|
};
|
|
}
|
|
};
|
|
|
|
// ============================================
|
|
// ALERTES
|
|
// ============================================
|
|
|
|
export const getAdminAlerts = async (): Promise<{
|
|
success: boolean;
|
|
alerts: Alert[];
|
|
count: number;
|
|
}> => {
|
|
try {
|
|
const { data } = await apiClient.get(`${CABINE_URL}/alerts`);
|
|
return {
|
|
success: true,
|
|
alerts: data.alerts || [],
|
|
count: data.count || 0,
|
|
};
|
|
} catch {
|
|
return { success: false, alerts: [], count: 0 };
|
|
}
|
|
};
|
|
|
|
// ============================================
|
|
// MEDIAS PRODUITS
|
|
// ============================================
|
|
|
|
export const uploadProductMediaAdmin = async (
|
|
productId: number,
|
|
fileUri: string,
|
|
fileName: string,
|
|
fileType: string,
|
|
mediaType: "image" | "video",
|
|
) => {
|
|
const fd = new FormData();
|
|
fd.append("file", { uri: fileUri, name: fileName, type: fileType } as any);
|
|
fd.append("type", mediaType);
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/products/${productId}/media`,
|
|
fd,
|
|
{ headers: { "Content-Type": "multipart/form-data" }, timeout: 120000 },
|
|
);
|
|
return { success: true, media: data.media, message: data.message };
|
|
};
|
|
|
|
export const deleteProductMediaAdmin = async (
|
|
productId: number,
|
|
mediaId: number,
|
|
) => {
|
|
const { data } = await apiClient.delete(
|
|
`${V2}/admin/protected/products/${productId}/media/${mediaId}`,
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
// ============================================
|
|
// ============================================
|
|
// ADDRESSES
|
|
// ============================================
|
|
|
|
export const addAddress = async (
|
|
invalidAddress: string,
|
|
correctAddress: string,
|
|
): Promise<{ success: boolean; message: string }> => {
|
|
const { data } = await apiClient.post(`${V2}/admin/protected/add/address`, {
|
|
invalid_address: invalidAddress,
|
|
correct_address: correctAddress,
|
|
});
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
export const deleteAddress = async (
|
|
invalidAddress: string,
|
|
correctAddress: string,
|
|
): Promise<{ success: boolean; message: string }> => {
|
|
const { data } = await apiClient.delete(
|
|
`${V2}/admin/protected/delete/address`,
|
|
{
|
|
data: {
|
|
invalid_address: invalidAddress,
|
|
correct_address: correctAddress,
|
|
},
|
|
},
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
export const getAllAddresses = async (): Promise<
|
|
{ invalid_address: string; correct_address: string }[]
|
|
> => {
|
|
const { data } = await apiClient.get(`${V2}/admin/protected/addresses`);
|
|
return (data.addresses ?? []).map((a: any) => ({
|
|
invalid_address: a.invalid_address ?? a.InvalidAddress ?? "",
|
|
correct_address: a.correct_address ?? a.CorrectAddress ?? "",
|
|
}));
|
|
};
|
|
|
|
// ============================================
|
|
// STATS HELPERS
|
|
// ============================================
|
|
|
|
export const getCommandCountByStatus = async (
|
|
status: string,
|
|
): Promise<number> => {
|
|
try {
|
|
const result = await getAllCommands(status);
|
|
return result.commands.length;
|
|
} catch {
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
// ============================================
|
|
// CATÉGORIES
|
|
// ============================================
|
|
|
|
const V1_PUBLIC = "http://5.181.0.112/api/v1";
|
|
|
|
export interface Category {
|
|
id: number;
|
|
name: string;
|
|
color: string;
|
|
is_coming_soon: boolean;
|
|
created_at: string;
|
|
}
|
|
|
|
export const getCategories = async (): Promise<Category[]> => {
|
|
try {
|
|
const { data } = await apiClient.get(`${V1_PUBLIC}/categories`);
|
|
return data.categories || [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
};
|
|
|
|
export const createCategoryAdmin = async (
|
|
name: string,
|
|
color: string,
|
|
isComingSoon: boolean = false,
|
|
): Promise<{ success: boolean; category?: Category; error?: string }> => {
|
|
try {
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/categories`,
|
|
{ name, color, is_coming_soon: isComingSoon },
|
|
);
|
|
return { success: true, category: data.category };
|
|
} catch (error: any) {
|
|
return {
|
|
success: false,
|
|
error: error.response?.data?.error || "Erreur",
|
|
};
|
|
}
|
|
};
|
|
|
|
export const updateCategoryAdmin = async (
|
|
id: number,
|
|
name: string,
|
|
color: string,
|
|
isComingSoon: boolean = false,
|
|
): Promise<{ success: boolean; category?: Category; error?: string }> => {
|
|
try {
|
|
const { data } = await apiClient.put(
|
|
`${V2}/admin/protected/categories/${id}`,
|
|
{ name, color, is_coming_soon: isComingSoon },
|
|
);
|
|
return { success: true, category: data.category };
|
|
} catch (error: any) {
|
|
return {
|
|
success: false,
|
|
error: error.response?.data?.error || "Erreur",
|
|
};
|
|
}
|
|
};
|
|
|
|
export const deleteCategoryAdmin = async (
|
|
id: number,
|
|
): Promise<{ success: boolean; error?: string }> => {
|
|
try {
|
|
await apiClient.delete(`${V2}/admin/protected/categories/${id}`);
|
|
return { success: true };
|
|
} catch (error: any) {
|
|
return {
|
|
success: false,
|
|
error: error.response?.data?.error || "Erreur",
|
|
};
|
|
}
|
|
};
|
|
|
|
// ============================================
|
|
// PARAMÈTRES GLOBAUX
|
|
// ============================================
|
|
|
|
export interface PointsTier {
|
|
min: number;
|
|
max: number; // 0 = illimité
|
|
points: number;
|
|
}
|
|
|
|
export interface AppSettings {
|
|
penalties_enabled: boolean;
|
|
show_amende_score: boolean;
|
|
points_enabled: boolean;
|
|
points_categories_weed: string[];
|
|
points_categories_zipette: string[];
|
|
points_separated: boolean;
|
|
points_weed_tiers: PointsTier[];
|
|
points_zipette_tiers: PointsTier[];
|
|
referral_enabled: boolean;
|
|
}
|
|
|
|
export const getSettings = async (): Promise<{ success: boolean; settings?: AppSettings; error?: string }> => {
|
|
try {
|
|
const { data } = await apiClient.get(`${V2}/admin/protected/settings`);
|
|
return { success: true, settings: data.settings };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.response?.data?.error || "Erreur" };
|
|
}
|
|
};
|
|
|
|
export const registerAdminPushToken = async (pushToken: string): Promise<void> => {
|
|
try {
|
|
await apiClient.post(`${V2}/admin/protected/push-token`, { push_token: pushToken });
|
|
} catch { /* ignore */ }
|
|
};
|
|
|
|
export const unregisterAdminPushToken = async (): Promise<void> => {
|
|
try {
|
|
await apiClient.delete(`${V2}/admin/protected/push-token`);
|
|
} catch { /* ignore */ }
|
|
};
|
|
|
|
export interface AppNotification {
|
|
command_id: number;
|
|
type: string;
|
|
message: string;
|
|
created_at: string;
|
|
read: boolean;
|
|
}
|
|
|
|
export const getAdminNotifications = async (): Promise<{ notifications: AppNotification[]; unread_count: number }> => {
|
|
const { data } = await apiClient.get(`${V2}/admin/protected/notifications`);
|
|
return data;
|
|
};
|
|
|
|
export const markAdminNotificationsRead = async (): Promise<void> => {
|
|
await apiClient.post(`${V2}/admin/protected/notifications/read`);
|
|
};
|
|
|
|
export const updateSettings = async (
|
|
settings: AppSettings,
|
|
): Promise<{ success: boolean; settings?: AppSettings; error?: string }> => {
|
|
try {
|
|
const { data } = await apiClient.put(`${V2}/admin/protected/settings`, settings);
|
|
return { success: true, settings: data.settings };
|
|
} catch (error: any) {
|
|
return { success: false, error: error.response?.data?.error || "Erreur" };
|
|
}
|
|
};
|