first commit

This commit is contained in:
nBiqoz
2025-10-05 16:10:35 +02:00
parent 201fca4e68
commit 13cd637391
70 changed files with 7287 additions and 130 deletions

19
hooks/use-mobile.ts Normal file
View File

@@ -0,0 +1,19 @@
import * as React from "react"
const MOBILE_BREAKPOINT = 768
export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
React.useEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
const onChange = () => {
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
}
mql.addEventListener("change", onChange)
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
return () => mql.removeEventListener("change", onChange)
}, [])
return !!isMobile
}

66
hooks/useCollection.ts Normal file
View File

@@ -0,0 +1,66 @@
"use client";
import { useState, useEffect, useCallback, useMemo } from 'react';
interface UseCollectionOptions {
page?: number;
limit?: number;
filter?: Record<string, unknown>;
}
interface CollectionResponse<T> {
data: T[];
total: number;
page: number;
limit: number;
totalPages: number;
}
export function useCollection<T = Record<string, unknown>>(
collectionName: string,
options: UseCollectionOptions = {}
) {
const [data, setData] = useState<T[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [total, setTotal] = useState(0);
const [totalPages, setTotalPages] = useState(0);
const { page = 1, limit = 20, filter = {} } = options;
// Mémoriser la chaîne JSON du filtre pour éviter les re-renders inutiles
const filterString = useMemo(() => JSON.stringify(filter), [filter]);
const fetchData = useCallback(async () => {
try {
setLoading(true);
const params = new URLSearchParams({
page: page.toString(),
limit: limit.toString(),
filter: filterString
});
const response = await fetch(`/api/collections/${collectionName}?${params}`);
if (!response.ok) throw new Error(`Erreur lors du chargement de ${collectionName}`);
const result: CollectionResponse<T> = await response.json();
setData(result.data);
setTotal(result.total);
setTotalPages(result.totalPages);
} catch (err) {
setError(err instanceof Error ? err.message : 'Erreur inconnue');
} finally {
setLoading(false);
}
}, [collectionName, page, limit, filterString]);
useEffect(() => {
fetchData();
}, [fetchData]);
const refetch = useCallback(() => {
return fetchData();
}, [fetchData]);
return { data, loading, error, total, totalPages, refetch };
}

34
hooks/useMetrics.ts Normal file
View File

@@ -0,0 +1,34 @@
"use client";
import { useState, useEffect, useCallback } from 'react';
import { DashboardMetrics } from '@/lib/types';
export function useMetrics() {
const [metrics, setMetrics] = useState<DashboardMetrics | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchMetrics = useCallback(async () => {
try {
setLoading(true);
const response = await fetch('/api/metrics');
if (!response.ok) throw new Error('Erreur lors du chargement des métriques');
const data = await response.json();
setMetrics(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Erreur inconnue');
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetchMetrics();
}, [fetchMetrics]);
const refetch = useCallback(() => {
return fetchMetrics();
}, [fetchMetrics]);
return { metrics, loading, error, refetch };
}

54
hooks/useStats.ts Normal file
View File

@@ -0,0 +1,54 @@
"use client";
import { useState, useEffect } from "react";
interface DailyToken {
name: string;
value: number;
}
interface ModelDistribution {
name: string;
value: number;
}
interface StatsData {
dailyTokens: DailyToken[];
modelDistribution: ModelDistribution[];
}
export function useStats() {
const [stats, setStats] = useState<StatsData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchStats = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/stats");
if (!response.ok) {
throw new Error("Erreur lors du chargement des statistiques");
}
const data = await response.json();
setStats(data);
} catch (err) {
setError(err instanceof Error ? err.message : "Erreur inconnue");
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchStats();
}, []);
return {
stats,
loading,
error,
refetch: fetchStats
};
}

45
hooks/useUserActivity.ts Normal file
View File

@@ -0,0 +1,45 @@
"use client";
import { useState, useEffect } from "react";
interface UserActivityData {
activeUsers: number;
inactiveUsers: number;
totalUsers: number;
}
export function useUserActivity() {
const [activity, setActivity] = useState<UserActivityData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchActivity = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch("/api/user-activity");
if (!response.ok) {
throw new Error("Erreur lors du chargement de l'activité des utilisateurs");
}
const data = await response.json();
setActivity(data);
} catch (err) {
setError(err instanceof Error ? err.message : "Erreur inconnue");
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchActivity();
}, []);
return {
activity,
loading,
error,
refetch: fetchActivity
};
}