mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
latest
This commit is contained in:
1 parent
e3fb29ab1d
commit
2485f71dd7
95 files changed
+41842
No files matched your search
@@ -0,0 +1,3 @@
|
||||
export function isUnauthorizedError(error: Error): boolean {
|
||||
return /^401: .*Unauthorized/.test(error.message);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { format } from "date-fns";
|
||||
import { fr } from "date-fns/locale";
|
||||
|
||||
/**
|
||||
* Formate une date de façon sécurisée pour éviter "Invalid time value"
|
||||
* Retourne une chaîne par défaut si la date est invalide
|
||||
*/
|
||||
export function safeFormat(
|
||||
date: string | Date | null | undefined,
|
||||
formatStr: string = "dd/MM/yyyy",
|
||||
options: { locale?: Locale; defaultValue?: string } = {}
|
||||
): string {
|
||||
const { locale = fr, defaultValue = "Date non disponible" } = options;
|
||||
|
||||
if (!date) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try {
|
||||
const dateObj = typeof date === 'string' ? new Date(date) : date;
|
||||
|
||||
// Vérifier si la date est valide
|
||||
if (isNaN(dateObj.getTime())) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return format(dateObj, formatStr, { locale });
|
||||
} catch (error) {
|
||||
console.warn('Date formatting error:', error, 'for date:', date);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un objet Date de façon sécurisée
|
||||
* Retourne null si la date est invalide
|
||||
*/
|
||||
export function safeDate(date: string | Date | null | undefined): Date | null {
|
||||
if (!date) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const dateObj = typeof date === 'string' ? new Date(date) : date;
|
||||
|
||||
// Vérifier si la date est valide
|
||||
if (isNaN(dateObj.getTime())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dateObj;
|
||||
} catch (error) {
|
||||
console.warn('Date creation error:', error, 'for date:', date);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare deux dates de façon sécurisée
|
||||
* Retourne 0 si l'une des dates est invalide
|
||||
*/
|
||||
export function safeCompareDate(a: string | Date | null | undefined, b: string | Date | null | undefined): number {
|
||||
const dateA = safeDate(a);
|
||||
const dateB = safeDate(b);
|
||||
|
||||
if (!dateA || !dateB) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dateA.getTime() - dateB.getTime();
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { QueryClient, QueryFunction } from "@tanstack/react-query";
|
||||
|
||||
async function throwIfResNotOk(res: Response) {
|
||||
if (!res.ok) {
|
||||
const text = (await res.text()) || res.statusText;
|
||||
throw new Error(`${res.status}: ${text}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function apiRequest(
|
||||
url: string,
|
||||
method: string = 'GET',
|
||||
body?: unknown,
|
||||
headers: Record<string, string> = {}
|
||||
): Promise<any> {
|
||||
console.log("🌐 API Request:", { url, method, body });
|
||||
|
||||
const res = await fetch(url, {
|
||||
method,
|
||||
headers: {
|
||||
...(body ? { "Content-Type": "application/json" } : {}),
|
||||
...headers,
|
||||
},
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
console.log("🌐 API Response:", { status: res.status, ok: res.ok });
|
||||
|
||||
await throwIfResNotOk(res);
|
||||
|
||||
// Return JSON response
|
||||
if (res.headers.get('content-type')?.includes('application/json')) {
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
type UnauthorizedBehavior = "returnNull" | "throw";
|
||||
export const getQueryFn: <T>(options: {
|
||||
on401: UnauthorizedBehavior;
|
||||
}) => QueryFunction<T> =
|
||||
({ on401: unauthorizedBehavior }) =>
|
||||
async ({ queryKey }) => {
|
||||
const res = await fetch(queryKey[0] as string, {
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (unauthorizedBehavior === "returnNull" && res.status === 401) {
|
||||
return null;
|
||||
}
|
||||
|
||||
await throwIfResNotOk(res);
|
||||
return await res.json();
|
||||
};
|
||||
|
||||
export const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
queryFn: getQueryFn({ on401: "returnNull" }),
|
||||
refetchInterval: false,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: true,
|
||||
refetchOnReconnect: false,
|
||||
staleTime: 30 * 1000, // 30 secondes de cache pour de meilleures performances
|
||||
gcTime: 10 * 60 * 1000, // 10 minutes
|
||||
retry: (failureCount, error: any) => {
|
||||
// Ne pas retry les erreurs d'authentification
|
||||
if (error?.message?.includes('401') || error?.message?.includes('Unauthorized')) {
|
||||
return false;
|
||||
}
|
||||
return failureCount < 2;
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
// Utilitaire pour harmoniser les couleurs des rôles dans toute l'application
|
||||
|
||||
export const ROLE_COLORS = {
|
||||
admin: '#f87171', // Rouge clair (Administrateur)
|
||||
manager: '#60a5fa', // Bleu clair (Manager)
|
||||
employee: '#4ade80', // Vert clair (Employé)
|
||||
directeur: '#a78bfa', // Violet clair (Directeur)
|
||||
} as const;
|
||||
|
||||
export const ROLE_DISPLAY_NAMES = {
|
||||
admin: 'Administrateur',
|
||||
manager: 'Manager',
|
||||
employee: 'Employé',
|
||||
directeur: 'Directeur',
|
||||
} as const;
|
||||
|
||||
export type RoleType = keyof typeof ROLE_COLORS;
|
||||
|
||||
export function getRoleColor(roleName: string): string {
|
||||
const roleKey = roleName.toLowerCase() as RoleType;
|
||||
return ROLE_COLORS[roleKey] || '#666666';
|
||||
}
|
||||
|
||||
export function getRoleDisplayName(roleName: string): string {
|
||||
const roleKey = roleName.toLowerCase() as RoleType;
|
||||
return ROLE_DISPLAY_NAMES[roleKey] || roleName;
|
||||
}
|
||||
|
||||
// Utilitaire pour les classes Tailwind (pour Users.tsx) - couleurs claires et douces
|
||||
export function getRoleTailwindClasses(roleName: string) {
|
||||
switch (roleName.toLowerCase()) {
|
||||
case 'admin':
|
||||
return {
|
||||
badgeClass: 'bg-red-50 text-red-600 border border-red-200',
|
||||
iconClass: 'text-red-400'
|
||||
};
|
||||
case 'manager':
|
||||
return {
|
||||
badgeClass: 'bg-blue-50 text-blue-600 border border-blue-200',
|
||||
iconClass: 'text-blue-400'
|
||||
};
|
||||
case 'employee':
|
||||
return {
|
||||
badgeClass: 'bg-green-50 text-green-600 border border-green-200',
|
||||
iconClass: 'text-green-400'
|
||||
};
|
||||
case 'directeur':
|
||||
return {
|
||||
badgeClass: 'bg-purple-50 text-purple-600 border border-purple-200',
|
||||
iconClass: 'text-purple-400'
|
||||
};
|
||||
default:
|
||||
return {
|
||||
badgeClass: 'bg-gray-50 text-gray-600 border border-gray-200',
|
||||
iconClass: 'text-gray-400'
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
Reference in new issue
Block a user