1150 lines
32 KiB
TypeScript
1150 lines
32 KiB
TypeScript
import apiClient from "./client";
|
|
import { API_BASE_URL } from "./client";
|
|
import type {
|
|
AuthResponse,
|
|
ClientResponse,
|
|
CommandResponse,
|
|
Product,
|
|
DeliveryPerson,
|
|
DeliveryPersonsStats,
|
|
Alert,
|
|
} from "./types";
|
|
|
|
const V2 = `${API_BASE_URL}/api/v2`;
|
|
const CABINE_URL = `${API_BASE_URL}/api/v1/cabine`;
|
|
const V1_PUBLIC = `${API_BASE_URL}/api/v1`;
|
|
|
|
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 */
|
|
}
|
|
};
|
|
|
|
export interface StatsSummary {
|
|
total_orders: number;
|
|
total_revenue: number;
|
|
peak_weekday: string;
|
|
top_product: string;
|
|
avg_per_day: number;
|
|
}
|
|
export interface WeekdayStat {
|
|
weekday: string;
|
|
count: number;
|
|
}
|
|
export interface DayStat {
|
|
day: string;
|
|
label: string;
|
|
count: number;
|
|
}
|
|
export interface DayRevenueStat {
|
|
day: string;
|
|
label: string;
|
|
revenue: number;
|
|
}
|
|
export interface HourStat {
|
|
hour: number;
|
|
label: string;
|
|
count: number;
|
|
revenue: number;
|
|
}
|
|
export interface ProductStat {
|
|
product_id: number;
|
|
name: string;
|
|
quantity: number;
|
|
order_count: number;
|
|
revenue: number;
|
|
category: string;
|
|
category_color: string;
|
|
}
|
|
|
|
export interface QuantityStat {
|
|
quantity: number;
|
|
order_count: number;
|
|
total_sold: number;
|
|
revenue: number;
|
|
}
|
|
export interface ProductQuantityBreakdown {
|
|
product_id: number;
|
|
name: string;
|
|
category_color: string;
|
|
total_orders: number;
|
|
quantities: QuantityStat[];
|
|
}
|
|
|
|
export interface AdminStats {
|
|
summary: StatsSummary;
|
|
by_weekday: WeekdayStat[];
|
|
by_day_30: DayStat[];
|
|
by_day_revenue: DayRevenueStat[];
|
|
by_hour: HourStat[];
|
|
top_products: ProductStat[];
|
|
by_quantity: ProductQuantityBreakdown[];
|
|
}
|
|
|
|
export const getAdminStats = async (): Promise<AdminStats> => {
|
|
const { data } = await apiClient.get(`${V2}/admin/protected/stats`);
|
|
return data;
|
|
};
|
|
|
|
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 };
|
|
};
|
|
|
|
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 exportApprovedCommandsCSV = async (): Promise<string> => {
|
|
const { data } = await apiClient.get(
|
|
`${V2}/admin/protected/orders/export/csv`,
|
|
{
|
|
responseType: "text",
|
|
},
|
|
);
|
|
return data as string;
|
|
};
|
|
|
|
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,
|
|
};
|
|
};
|
|
|
|
// ============================================
|
|
|
|
export const getAvailableDeliveryPersons = async () => {
|
|
try {
|
|
const { data } = await apiClient.get(
|
|
`${V2}/admin/protected/delivery-persons`,
|
|
);
|
|
return {
|
|
success: true,
|
|
livreurs: data.livreurs || [],
|
|
count: data.count || 0,
|
|
};
|
|
} catch {
|
|
return { success: false, livreurs: [], 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",
|
|
};
|
|
}
|
|
};
|
|
|
|
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(
|
|
`${V2}/admin/protected/clients`,
|
|
data,
|
|
);
|
|
return {
|
|
success: true,
|
|
message: res.message || "Client créé",
|
|
client: res.client,
|
|
};
|
|
};
|
|
|
|
export const createUserByAdmin = async (data: {
|
|
username: string;
|
|
password: string;
|
|
role: string;
|
|
}) => {
|
|
const { data: res } = await apiClient.post(
|
|
`${V2}/admin/protected/users`,
|
|
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 setClientParrain = async (
|
|
username: string,
|
|
parrain: string,
|
|
): Promise<{ success: boolean; error?: string }> => {
|
|
try {
|
|
await apiClient.post(
|
|
`${V2}/admin/protected/client/${username}/parrain/set`,
|
|
{ parrain },
|
|
);
|
|
return { success: true };
|
|
} catch (e: any) {
|
|
return { success: false, error: e.response?.data?.error || "Erreur" };
|
|
}
|
|
};
|
|
|
|
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",
|
|
};
|
|
}
|
|
};
|
|
|
|
export const resetClientReferral = async (
|
|
username: string,
|
|
): Promise<{ success: boolean; message?: string }> => {
|
|
try {
|
|
const { data } = await apiClient.delete(
|
|
`${V2}/admin/protected/client/${username}/referral/reset`,
|
|
);
|
|
return { success: true, message: data.message };
|
|
} catch (error: any) {
|
|
return {
|
|
success: false,
|
|
message: error.response?.data?.error || "Erreur reset parrainage",
|
|
};
|
|
}
|
|
};
|
|
|
|
export interface CancelledOrder {
|
|
id: number;
|
|
username: string;
|
|
total_prix: number;
|
|
created_at: string;
|
|
updated_at: string;
|
|
items_count: number;
|
|
cancel_reason: string;
|
|
cancellation?: {
|
|
cancelled_at: string;
|
|
cancelled_by: string;
|
|
reason: string;
|
|
};
|
|
}
|
|
|
|
export const getClientCancelledOrders = async (
|
|
username: string,
|
|
): Promise<{ success: boolean; orders: CancelledOrder[] }> => {
|
|
try {
|
|
const { data } = await apiClient.get(
|
|
`${V2}/admin/protected/orders/cancelled`,
|
|
{ params: { username } },
|
|
);
|
|
return {
|
|
success: true,
|
|
orders: data.data?.cancelled_orders ?? [],
|
|
};
|
|
} catch {
|
|
return { success: false, orders: [] };
|
|
}
|
|
};
|
|
|
|
// ============================================
|
|
// AMENDES & POINTS CLIENT (Admin)
|
|
// ============================================
|
|
|
|
export const applyClientPenalty = async (
|
|
username: string,
|
|
points: number,
|
|
reason: string,
|
|
): Promise<{ success: boolean; compensated?: boolean; error?: string }> => {
|
|
try {
|
|
const { data } = await apiClient.post(`${V2}/admin/protected/penalty`, {
|
|
username,
|
|
points,
|
|
reason,
|
|
});
|
|
return { success: true, compensated: data?.compensated === true };
|
|
} catch (e: any) {
|
|
return { success: false, error: e.response?.data?.error || "Erreur" };
|
|
}
|
|
};
|
|
|
|
export const resetClientPenalties = async (
|
|
username: string,
|
|
resetCancellationsCount = false,
|
|
): Promise<{ success: boolean; error?: string }> => {
|
|
try {
|
|
await apiClient.post(
|
|
`${V2}/admin/protected/client/${username}/penalties/reset`,
|
|
{ reset_cancellations_count: resetCancellationsCount },
|
|
);
|
|
return { success: true };
|
|
} catch (e: any) {
|
|
return { success: false, error: e.response?.data?.error || "Erreur" };
|
|
}
|
|
};
|
|
|
|
export const resetClientPoints = async (
|
|
username: string,
|
|
pool = -1,
|
|
): Promise<{ success: boolean; error?: string }> => {
|
|
try {
|
|
await apiClient.post(
|
|
`${V2}/admin/protected/client/${username}/point/reset`,
|
|
{ pool },
|
|
);
|
|
return { success: true };
|
|
} catch (e: any) {
|
|
return { success: false, error: e.response?.data?.error || "Erreur" };
|
|
}
|
|
};
|
|
|
|
export const addClientPoints = async (
|
|
username: string,
|
|
poolKey: string,
|
|
points: number,
|
|
): Promise<{ success: boolean; error?: string }> => {
|
|
try {
|
|
await apiClient.post(
|
|
`${V2}/admin/protected/client/${username}/points/add`,
|
|
{ pool_key: poolKey, points },
|
|
);
|
|
return { success: true };
|
|
} catch (e: any) {
|
|
return { success: false, error: e.response?.data?.error || "Erreur" };
|
|
}
|
|
};
|
|
|
|
export const subtractClientPoints = async (
|
|
username: string,
|
|
poolKey: string,
|
|
points: number,
|
|
): Promise<{ success: boolean; error?: string }> => {
|
|
try {
|
|
await apiClient.post(
|
|
`${V2}/admin/protected/client/${username}/points/subtract`,
|
|
{ pool_key: poolKey, points },
|
|
);
|
|
return { success: true };
|
|
} catch (e: any) {
|
|
return { success: false, error: e.response?.data?.error || "Erreur" };
|
|
}
|
|
};
|
|
|
|
// ============================================
|
|
// 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 };
|
|
};
|
|
|
|
export const activateProductPrice = async (priceId: number) => {
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/active/product/price/${priceId}`,
|
|
);
|
|
return { success: true, message: data.message };
|
|
};
|
|
|
|
export const deactivateProductPrice = async (priceId: number) => {
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/desactive/product/price/${priceId}`,
|
|
);
|
|
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
|
|
// ============================================
|
|
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 RewardCategoryConfig {
|
|
category: string;
|
|
all_products: boolean;
|
|
product_ids: number[];
|
|
amount: number;
|
|
}
|
|
|
|
export interface PointsReward {
|
|
threshold: number;
|
|
type: "free_product" | "half_price_product" | "custom";
|
|
description: string;
|
|
category_configs: RewardCategoryConfig[];
|
|
}
|
|
|
|
export interface PointsPool {
|
|
key: string;
|
|
name: string;
|
|
categories: string[];
|
|
tiers: PointsTier[];
|
|
}
|
|
|
|
export interface PostalZone {
|
|
name: string;
|
|
min_amount: number;
|
|
codes: string[];
|
|
}
|
|
|
|
export interface DaySchedule {
|
|
enabled: boolean;
|
|
open_time: string; // "09:00"
|
|
close_time: string; // "20:00"
|
|
}
|
|
|
|
export interface DeliverySchedule {
|
|
monday: DaySchedule;
|
|
tuesday: DaySchedule;
|
|
wednesday: DaySchedule;
|
|
thursday: DaySchedule;
|
|
friday: DaySchedule;
|
|
saturday: DaySchedule;
|
|
sunday: DaySchedule;
|
|
}
|
|
|
|
export const DEFAULT_POSTAL_ZONES: PostalZone[] = [
|
|
{
|
|
name: "Zone 30€",
|
|
min_amount: 30,
|
|
codes: ["44000", "44100", "44200", "44300"],
|
|
},
|
|
{
|
|
name: "Zone 50€",
|
|
min_amount: 50,
|
|
codes: [
|
|
"44400",
|
|
"44880",
|
|
"44120",
|
|
"44230",
|
|
"44115",
|
|
"44980",
|
|
"44470",
|
|
"44240",
|
|
"44700",
|
|
"44800",
|
|
"44340",
|
|
"44620",
|
|
"44830",
|
|
],
|
|
},
|
|
{
|
|
name: "Zone 100€",
|
|
min_amount: 100,
|
|
codes: ["44860", "44220", "44118", "44710", "44690", "44119"],
|
|
},
|
|
];
|
|
|
|
export const DEFAULT_DAY: DaySchedule = {
|
|
enabled: true,
|
|
open_time: "09:00",
|
|
close_time: "20:00",
|
|
};
|
|
|
|
export const DEFAULT_DELIVERY_SCHEDULE: DeliverySchedule = {
|
|
monday: { ...DEFAULT_DAY },
|
|
tuesday: { ...DEFAULT_DAY },
|
|
wednesday: { ...DEFAULT_DAY },
|
|
thursday: { ...DEFAULT_DAY },
|
|
friday: { ...DEFAULT_DAY },
|
|
saturday: { ...DEFAULT_DAY },
|
|
sunday: { ...DEFAULT_DAY },
|
|
};
|
|
|
|
export interface CategoryRoute {
|
|
deliveryman_username: string;
|
|
categories: string[];
|
|
}
|
|
|
|
export interface DeliveryModeConfig {
|
|
mode: "single" | "category_based";
|
|
category_routes: CategoryRoute[];
|
|
}
|
|
|
|
export interface PenaltyTier {
|
|
min_cancel: number;
|
|
amount: number;
|
|
}
|
|
|
|
export interface AppSettings {
|
|
penalties_enabled: boolean;
|
|
show_amende_score: boolean;
|
|
penalty_tiers: PenaltyTier[];
|
|
points_enabled: boolean;
|
|
points_pools: PointsPool[];
|
|
points_reward?: PointsReward | null;
|
|
referral_enabled: boolean;
|
|
delivery_schedule: DeliverySchedule;
|
|
postal_zones: PostalZone[];
|
|
crypto_payment_enabled: boolean;
|
|
crypto_only: boolean;
|
|
nowpayments_api_key: string;
|
|
nowpayments_ipn_secret: string;
|
|
nowpayments_currencies: string[];
|
|
telegram_bot_token: string;
|
|
telegram_bot_username: string;
|
|
telegram_notifications_enabled: boolean;
|
|
telegram_2fa_enabled: boolean;
|
|
delivery_mode: DeliveryModeConfig;
|
|
shop_name: string;
|
|
contact_telegram: string;
|
|
}
|
|
|
|
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 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`);
|
|
};
|
|
|
|
// ============================================
|
|
// TELEGRAM — ADMIN
|
|
// ============================================
|
|
|
|
export const getAdminTelegramStatus = async (): Promise<{
|
|
linked: boolean;
|
|
enabled: boolean;
|
|
}> => {
|
|
try {
|
|
const { data } = await apiClient.get(
|
|
`${V2}/admin/protected/telegram/status`,
|
|
);
|
|
return data;
|
|
} catch {
|
|
return { linked: false, enabled: false };
|
|
}
|
|
};
|
|
|
|
export const generateAdminLinkToken = async (): Promise<{
|
|
link_url?: string;
|
|
error?: string;
|
|
}> => {
|
|
try {
|
|
const { data } = await apiClient.post(
|
|
`${V2}/admin/protected/telegram/link-token`,
|
|
);
|
|
return data;
|
|
} catch (e: any) {
|
|
return { error: e?.response?.data?.error || "Erreur" };
|
|
}
|
|
};
|
|
|
|
export const unlinkAdminTelegram = async (): Promise<void> => {
|
|
try {
|
|
await apiClient.delete(`${V2}/admin/protected/telegram/unlink`);
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
};
|
|
|
|
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",
|
|
};
|
|
}
|
|
};
|