chore: build
This commit is contained in:
@@ -104,6 +104,36 @@ export interface ProductQuantityBreakdown {
|
||||
quantities: QuantityStat[];
|
||||
}
|
||||
|
||||
export interface DailyProductItem {
|
||||
product_id: number;
|
||||
name: string;
|
||||
quantity: number;
|
||||
order_count: number;
|
||||
revenue: number;
|
||||
}
|
||||
export interface DailyCategoryDetail {
|
||||
category: string;
|
||||
category_color: string;
|
||||
total_quantity: number;
|
||||
total_revenue: number;
|
||||
products: DailyProductItem[];
|
||||
}
|
||||
export interface DailyDetail {
|
||||
date: string;
|
||||
total_orders: number;
|
||||
total_quantity: number;
|
||||
total_revenue: number;
|
||||
categories: DailyCategoryDetail[];
|
||||
}
|
||||
|
||||
export type StatSection =
|
||||
| "commandes"
|
||||
| "revenus"
|
||||
| "produits"
|
||||
| "heures"
|
||||
| "jours"
|
||||
| "doses";
|
||||
|
||||
export interface AdminStats {
|
||||
summary: StatsSummary;
|
||||
by_weekday: WeekdayStat[];
|
||||
@@ -112,6 +142,13 @@ export interface AdminStats {
|
||||
by_hour: HourStat[];
|
||||
top_products: ProductStat[];
|
||||
by_quantity: ProductQuantityBreakdown[];
|
||||
daily_detail?: DailyDetail;
|
||||
reset_at_commandes?: string;
|
||||
reset_at_revenus?: string;
|
||||
reset_at_produits?: string;
|
||||
reset_at_heures?: string;
|
||||
reset_at_jours?: string;
|
||||
reset_at_doses?: string;
|
||||
}
|
||||
|
||||
export const getAdminStats = async (): Promise<AdminStats> => {
|
||||
@@ -119,6 +156,55 @@ export const getAdminStats = async (): Promise<AdminStats> => {
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getAdminDailyDetail = async (
|
||||
date: string,
|
||||
): Promise<DailyDetail> => {
|
||||
const { data } = await apiClient.get(`${V2}/admin/protected/stats/daily`, {
|
||||
params: { date },
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const resetAdminStats = async (
|
||||
section: StatSection,
|
||||
): Promise<{ success: boolean; reset_at: string }> => {
|
||||
const { data } = await apiClient.post(
|
||||
`${V2}/admin/protected/stats/reset/${section}`,
|
||||
);
|
||||
return data;
|
||||
};
|
||||
// ============================================
|
||||
// STATISTIQUES MENSUELLES (jour par jour)
|
||||
// ============================================
|
||||
export interface MonthlyDayStat {
|
||||
day: string; // "2026-06-05"
|
||||
label: string; // "05/06"
|
||||
count: number;
|
||||
revenue: number;
|
||||
quantity: number;
|
||||
}
|
||||
export interface MonthlyStatsSummary {
|
||||
total_orders: number;
|
||||
total_revenue: number;
|
||||
total_quantity: number;
|
||||
}
|
||||
export interface MonthlyStats {
|
||||
month: string; // "2026-06"
|
||||
summary: MonthlyStatsSummary;
|
||||
by_day: MonthlyDayStat[];
|
||||
}
|
||||
|
||||
export const getAdminStatsByMonth = async (
|
||||
month?: string,
|
||||
): Promise<MonthlyStats> => {
|
||||
const { data } = await apiClient.get(
|
||||
`${V2}/admin/protected/stats/monthly`,
|
||||
{
|
||||
params: month ? { month } : undefined,
|
||||
},
|
||||
);
|
||||
return data;
|
||||
};
|
||||
export const getAllClients = async (): Promise<ClientResponse[]> => {
|
||||
const { data } = await apiClient.get(`${V2}/admin/protected/all/clients`);
|
||||
return data.clients || [];
|
||||
@@ -237,9 +323,17 @@ export const validateCommand = async (commandId: number) => {
|
||||
`${V2}/admin/protected/orders/${commandId}/force-validate`,
|
||||
{ command_id: commandId },
|
||||
);
|
||||
const validated = (data.validated ?? []) as { command_id: number; points_awarded: number }[];
|
||||
const points = validated.find((v) => v.command_id === commandId)?.points_awarded ?? 0;
|
||||
return { success: true, points_awarded: points, validated_count: data.validated_count ?? 0 };
|
||||
const validated = (data.validated ?? []) as {
|
||||
command_id: number;
|
||||
points_awarded: number;
|
||||
}[];
|
||||
const points =
|
||||
validated.find((v) => v.command_id === commandId)?.points_awarded ?? 0;
|
||||
return {
|
||||
success: true,
|
||||
points_awarded: points,
|
||||
validated_count: data.validated_count ?? 0,
|
||||
};
|
||||
};
|
||||
|
||||
export const proposeAddressChangeAdmin = async (
|
||||
@@ -264,7 +358,6 @@ export const notifyClientToDescend = async (commandId: number) => {
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// ============================================
|
||||
|
||||
export const getAvailableDeliveryPersons = async () => {
|
||||
@@ -299,6 +392,74 @@ export const getDeliveryPersonDetails = async (username: string) => {
|
||||
return data.deliveryman || data;
|
||||
};
|
||||
|
||||
export const getLivreurRatings = async (
|
||||
username: string,
|
||||
): Promise<{
|
||||
ratings: {
|
||||
id: number;
|
||||
order_id: number;
|
||||
client_username: string;
|
||||
rating: number;
|
||||
comment: string;
|
||||
created_at: string;
|
||||
}[];
|
||||
average: number;
|
||||
count: number;
|
||||
}> => {
|
||||
try {
|
||||
const { data } = await apiClient.get(
|
||||
`${V2}/admin/protected/delivery-persons/${username}/ratings`,
|
||||
);
|
||||
return data;
|
||||
} catch {
|
||||
return { ratings: [], average: 0, count: 0 };
|
||||
}
|
||||
};
|
||||
|
||||
export interface LoginHistoryEntry {
|
||||
id: number;
|
||||
username: string;
|
||||
created_at: string;
|
||||
}
|
||||
export interface LoginHistoryWeek {
|
||||
week: number;
|
||||
entries: LoginHistoryEntry[];
|
||||
}
|
||||
|
||||
export const getLivreurLoginHistory = async (
|
||||
username: string,
|
||||
year?: number,
|
||||
month?: number,
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
year: number;
|
||||
month: number;
|
||||
weeks: LoginHistoryWeek[];
|
||||
count: number;
|
||||
}> => {
|
||||
try {
|
||||
const { data } = await apiClient.get(
|
||||
`${V2}/admin/protected/delivery-persons/${username}/login-history`,
|
||||
{ params: { year, month } },
|
||||
);
|
||||
return {
|
||||
success: true,
|
||||
year: data.year,
|
||||
month: data.month,
|
||||
weeks: data.weeks || [],
|
||||
count: data.count || 0,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
success: false,
|
||||
year: year ?? new Date().getFullYear(),
|
||||
month: month ?? new Date().getMonth() + 1,
|
||||
weeks: [],
|
||||
count: 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const parseStatus = (status: any): "available" | "busy" | "offline" => {
|
||||
if (!status) return "offline";
|
||||
if (status === "available" || status === "busy" || status === "offline")
|
||||
@@ -446,10 +607,11 @@ export const createProductAdmin = async (formData: FormData) => {
|
||||
`${V2}/admin/protected/products`,
|
||||
formData,
|
||||
{
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
timeout: 120000,
|
||||
},
|
||||
);
|
||||
return { success: true, data: data.product, message: data.message };
|
||||
return { success: true, data: data.data, message: data.message };
|
||||
};
|
||||
|
||||
export const updateProductAdmin = async (
|
||||
@@ -544,6 +706,18 @@ export const setClientParrain = async (
|
||||
return { success: false, error: e.response?.data?.error || "Erreur" };
|
||||
}
|
||||
};
|
||||
export const getClientParrain = async (
|
||||
username: string,
|
||||
): Promise<{ success: boolean; parrain?: string | null; error?: string }> => {
|
||||
try {
|
||||
const { data } = await apiClient.get(
|
||||
`${V2}/admin/protected/client/${username}/parrain`,
|
||||
);
|
||||
return { success: true, parrain: data.parrain ?? null };
|
||||
} catch (e: any) {
|
||||
return { success: false, error: e.response?.data?.error || "Erreur" };
|
||||
}
|
||||
};
|
||||
|
||||
export const creditClientReferral = async (
|
||||
username: string,
|
||||
@@ -748,7 +922,7 @@ export const uploadProductMediaAdmin = async (
|
||||
const { data } = await apiClient.post(
|
||||
`${V2}/admin/protected/products/${productId}/media`,
|
||||
fd,
|
||||
{ timeout: 120000 },
|
||||
{ headers: { "Content-Type": "multipart/form-data" }, timeout: 120000 },
|
||||
);
|
||||
return { success: true, media: data.media, message: data.message };
|
||||
};
|
||||
@@ -841,6 +1015,7 @@ export interface Category {
|
||||
name: string;
|
||||
color: string;
|
||||
is_coming_soon: boolean;
|
||||
position: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
@@ -906,6 +1081,22 @@ export const deleteCategoryAdmin = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const reorderCategoriesAdmin = async (
|
||||
ids: number[],
|
||||
): Promise<{ success: boolean; error?: string }> => {
|
||||
try {
|
||||
await apiClient.put(`${V2}/admin/protected/categories/reorder`, {
|
||||
ids,
|
||||
});
|
||||
return { success: true };
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.response?.data?.error || "Erreur",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// ============================================
|
||||
// PARAMÈTRES GLOBAUX
|
||||
// ============================================
|
||||
@@ -1050,6 +1241,18 @@ export interface AppSettings {
|
||||
delivery_mode: DeliveryModeConfig;
|
||||
shop_name: string;
|
||||
contact_telegram: string;
|
||||
admin_color_primary: string;
|
||||
admin_color_secondary: string;
|
||||
admin_color_success: string;
|
||||
admin_color_danger: string;
|
||||
admin_color_warning: string;
|
||||
client_color_primary: string;
|
||||
client_color_secondary: string;
|
||||
client_color_success: string;
|
||||
client_color_danger: string;
|
||||
client_color_warning: string;
|
||||
client_title_gradient_from: string;
|
||||
client_title_gradient_to: string;
|
||||
}
|
||||
|
||||
export const getSettings = async (): Promise<{
|
||||
|
||||
@@ -379,6 +379,8 @@ export const getMyStats = async (): Promise<{
|
||||
by_day?: StatPoint[];
|
||||
by_week?: StatPoint[];
|
||||
by_month?: StatPoint[];
|
||||
today_count?: number;
|
||||
today_revenue?: number;
|
||||
error?: string;
|
||||
}> => {
|
||||
try {
|
||||
@@ -388,12 +390,39 @@ export const getMyStats = async (): Promise<{
|
||||
by_day: data.by_day || [],
|
||||
by_week: data.by_week || [],
|
||||
by_month: data.by_month || [],
|
||||
today_count: data.today_count || 0,
|
||||
today_revenue: data.today_revenue || 0,
|
||||
};
|
||||
} catch (error: any) {
|
||||
return { success: false, error: error.response?.data?.error || "Erreur réseau" };
|
||||
}
|
||||
};
|
||||
|
||||
export interface LivreurRating {
|
||||
id: number;
|
||||
order_id: number;
|
||||
livreur_username: string;
|
||||
client_username: string;
|
||||
rating: number;
|
||||
comment: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const getMyRatings = async (): Promise<{
|
||||
success: boolean;
|
||||
ratings: LivreurRating[];
|
||||
average: number;
|
||||
count: number;
|
||||
error?: string;
|
||||
}> => {
|
||||
try {
|
||||
const { data } = await apiClient.get(`${API}/ratings`);
|
||||
return { success: true, ratings: data.ratings || [], average: data.average || 0, count: data.count || 0 };
|
||||
} catch (e: any) {
|
||||
return { success: false, ratings: [], average: 0, count: 0, error: e?.response?.data?.error || "Erreur" };
|
||||
}
|
||||
};
|
||||
|
||||
export const reportDeliveryIssue = async (
|
||||
deliveryId: number,
|
||||
issueType: IssueType,
|
||||
|
||||
@@ -70,6 +70,7 @@ export const maneuverIcons: Record<string, string> = {
|
||||
DEFAULT: "arrow-up",
|
||||
};
|
||||
|
||||
// ---- Helpers ----
|
||||
function formatDistance(meters: number): string {
|
||||
if (meters < 1000) return `${Math.round(meters)} m`;
|
||||
return `${(meters / 1000).toFixed(1)} km`;
|
||||
|
||||
+124
-114
@@ -1,166 +1,176 @@
|
||||
// Shared types for admin panel
|
||||
|
||||
export interface AdminUser {
|
||||
id: number;
|
||||
username: string;
|
||||
role: string;
|
||||
id: number;
|
||||
username: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface AuthResponse {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
access_token?: string;
|
||||
token_type?: string;
|
||||
expires_in?: number;
|
||||
user?: AdminUser;
|
||||
success: boolean;
|
||||
message?: string;
|
||||
access_token?: string;
|
||||
token_type?: string;
|
||||
expires_in?: number;
|
||||
user?: AdminUser;
|
||||
}
|
||||
|
||||
export interface ClientResponse {
|
||||
id: number;
|
||||
username: string;
|
||||
nom: string;
|
||||
prenom: string;
|
||||
telephone: string;
|
||||
adresse?: string;
|
||||
role?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
command: number;
|
||||
points_extra: Record<string, number>;
|
||||
amende: number;
|
||||
cancellations_count: number;
|
||||
last_penalty_reason?: string;
|
||||
referral_balance?: number;
|
||||
id: number;
|
||||
username: string;
|
||||
nom: string;
|
||||
prenom: string;
|
||||
telephone: string;
|
||||
adresse?: string;
|
||||
role?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
command: number;
|
||||
points_extra: Record<string, number>;
|
||||
amende: number;
|
||||
cancellations_count: number;
|
||||
last_penalty_reason?: string;
|
||||
referral_balance?: number;
|
||||
}
|
||||
|
||||
export interface CommandResponse {
|
||||
id: number;
|
||||
client_order_number?: number;
|
||||
username: string;
|
||||
status: string;
|
||||
adresse: string;
|
||||
total_prix: number;
|
||||
livreur_assign?: string | null;
|
||||
proposed_address?: string | null;
|
||||
address_proposal_status?: string;
|
||||
referral_used?: number;
|
||||
cancel_reason?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
id: number;
|
||||
client_order_number?: number;
|
||||
username: string;
|
||||
status: string;
|
||||
adresse: string;
|
||||
total_prix: number;
|
||||
livreur_assign?: string | null;
|
||||
proposed_address?: string | null;
|
||||
address_proposal_status?: string;
|
||||
referral_used?: number;
|
||||
cancel_reason?: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface OrderItem {
|
||||
id: number;
|
||||
command_id: number;
|
||||
product_id: number;
|
||||
product_name: string;
|
||||
quantity: number;
|
||||
price: number;
|
||||
status: string;
|
||||
created_at: string;
|
||||
id: number;
|
||||
command_id: number;
|
||||
product_id: number;
|
||||
product_name: string;
|
||||
quantity: number;
|
||||
price: number;
|
||||
status: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface Alert {
|
||||
id: number;
|
||||
username: string;
|
||||
status: string;
|
||||
message: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
id: number;
|
||||
username: string;
|
||||
status: string;
|
||||
message: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface DeliveryPerson {
|
||||
id: number;
|
||||
username: string;
|
||||
status: 'available' | 'busy' | 'offline';
|
||||
location: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
last_update: string;
|
||||
is_recent: boolean;
|
||||
};
|
||||
stats: {
|
||||
total_deliveries: number;
|
||||
completed_today: number;
|
||||
queue_size: number;
|
||||
current_command: number | null;
|
||||
};
|
||||
id: number;
|
||||
username: string;
|
||||
status: "available" | "busy" | "offline";
|
||||
location: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
last_update: string;
|
||||
is_recent: boolean;
|
||||
};
|
||||
stats: {
|
||||
total_deliveries: number;
|
||||
completed_today: number;
|
||||
queue_size: number;
|
||||
current_command: number | null;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DeliveryPersonsStats {
|
||||
total: number;
|
||||
available: number;
|
||||
busy: number;
|
||||
offline: number;
|
||||
active_deliveries: number;
|
||||
total: number;
|
||||
available: number;
|
||||
busy: number;
|
||||
offline: number;
|
||||
active_deliveries: number;
|
||||
}
|
||||
|
||||
export interface Product {
|
||||
id: number;
|
||||
name: string;
|
||||
description?: string;
|
||||
category: string;
|
||||
stock: number;
|
||||
unit: string;
|
||||
prices?: Array<{ id?: number; quantity: number; price: number; active_price?: boolean }>;
|
||||
media?: Array<{ url: string; type: string; id?: number; created_at?: string }>;
|
||||
coming_soon?: boolean;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
id: number;
|
||||
name: string;
|
||||
description?: string;
|
||||
category: string;
|
||||
stock: number;
|
||||
unit: string;
|
||||
prices?: Array<{
|
||||
id?: number;
|
||||
quantity: number;
|
||||
price: number;
|
||||
active_price?: boolean;
|
||||
}>;
|
||||
media?: Array<{
|
||||
url: string;
|
||||
type: string;
|
||||
id?: number;
|
||||
created_at?: string;
|
||||
}>;
|
||||
coming_soon?: boolean;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
export interface DeliveryStatus {
|
||||
status: 'available' | 'busy' | 'offline';
|
||||
current_command?: number;
|
||||
last_update?: number;
|
||||
status: "available" | "busy" | "offline";
|
||||
current_command?: number;
|
||||
last_update?: number;
|
||||
}
|
||||
|
||||
export interface QueueInfo {
|
||||
queue_size: number;
|
||||
commands: any[];
|
||||
queue_size: number;
|
||||
commands: any[];
|
||||
}
|
||||
|
||||
export interface DeliveryItemProduct {
|
||||
produit: string;
|
||||
quantite: number;
|
||||
prix: number;
|
||||
produit: string;
|
||||
quantite: number;
|
||||
prix: number;
|
||||
}
|
||||
|
||||
export interface DeliveryItem {
|
||||
id: number;
|
||||
status: string;
|
||||
adresse: string;
|
||||
total_prix: number;
|
||||
referral_used?: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
eta?: string;
|
||||
items?: DeliveryItemProduct[];
|
||||
items_count?: number;
|
||||
id: number;
|
||||
status: string;
|
||||
adresse: string;
|
||||
total_prix: number;
|
||||
referral_used?: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
eta?: string;
|
||||
items?: DeliveryItemProduct[];
|
||||
items_count?: number;
|
||||
}
|
||||
|
||||
export interface ClientInfo {
|
||||
username: string;
|
||||
nom?: string;
|
||||
prenom?: string;
|
||||
telephone?: string;
|
||||
username: string;
|
||||
nom?: string;
|
||||
prenom?: string;
|
||||
telephone?: string;
|
||||
}
|
||||
|
||||
export interface DeliveryDetails {
|
||||
delivery: DeliveryItem;
|
||||
client_info: ClientInfo;
|
||||
delivery: DeliveryItem;
|
||||
client_info: ClientInfo;
|
||||
}
|
||||
|
||||
export interface PenaltyInfo {
|
||||
client_username: string;
|
||||
current_amende: number;
|
||||
cancellations_count: number;
|
||||
next_penalty: number;
|
||||
client_username: string;
|
||||
current_amende: number;
|
||||
cancellations_count: number;
|
||||
next_penalty: number;
|
||||
}
|
||||
|
||||
export interface MapLinks {
|
||||
google_maps: string;
|
||||
waze: string;
|
||||
apple_maps: string;
|
||||
openstreetmap: string;
|
||||
google_maps: string;
|
||||
waze: string;
|
||||
apple_maps: string;
|
||||
openstreetmap: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user