feat: revue de code, doc CODE_REVIEW, forfaits 2026, traduction LLM, providers avec modèle
Made-with: Cursor
This commit is contained in:
61
frontend/src/app/admin/system/CleanupSection.tsx
Normal file
61
frontend/src/app/admin/system/CleanupSection.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
"use client";
|
||||
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Trash2, Loader2 } from "lucide-react";
|
||||
import type { CleanupResponse } from "../types";
|
||||
|
||||
interface CleanupSectionProps {
|
||||
trackedFilesCount: number;
|
||||
isPurging: boolean;
|
||||
purgeResult: CleanupResponse | null;
|
||||
onCleanup: () => void;
|
||||
}
|
||||
|
||||
export function CleanupSection({
|
||||
trackedFilesCount,
|
||||
isPurging,
|
||||
purgeResult,
|
||||
onCleanup,
|
||||
}: CleanupSectionProps) {
|
||||
return (
|
||||
<Card className="py-0">
|
||||
<CardContent className="flex items-center gap-3 px-4 py-3">
|
||||
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-red-500/10">
|
||||
<Trash2 className="size-4 text-red-500" />
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col gap-0.5">
|
||||
<span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
|
||||
Fichiers Temporaires
|
||||
</span>
|
||||
<span className="text-sm font-semibold tabular-nums text-foreground">
|
||||
{trackedFilesCount} fichier{trackedFilesCount !== 1 ? "s" : ""} orphelin{trackedFilesCount !== 1 ? "s" : ""}
|
||||
</span>
|
||||
{purgeResult && (
|
||||
<span className="text-[10px] text-green-500">
|
||||
{purgeResult.files_cleaned} fichier{purgeResult.files_cleaned !== 1 ? "s" : ""} supprimé{purgeResult.files_cleaned !== 1 ? "s" : ""}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-7 shrink-0 gap-1.5 border-red-200/30 text-red-500 hover:bg-red-500/10 hover:text-red-500 text-xs"
|
||||
onClick={onCleanup}
|
||||
disabled={isPurging || trackedFilesCount === 0}
|
||||
>
|
||||
{isPurging ? (
|
||||
<Loader2 className="size-3 animate-spin" />
|
||||
) : (
|
||||
<Trash2 className="size-3" />
|
||||
)}
|
||||
{isPurging
|
||||
? "Nettoyage..."
|
||||
: trackedFilesCount === 0
|
||||
? "Propre"
|
||||
: "Nettoyer"}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
51
frontend/src/app/admin/system/DiskSpaceCard.tsx
Normal file
51
frontend/src/app/admin/system/DiskSpaceCard.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
"use client";
|
||||
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { HardDrive } from "lucide-react";
|
||||
|
||||
interface DiskSpaceCardProps {
|
||||
usedPercent?: number;
|
||||
totalGb?: number;
|
||||
freeGb?: number;
|
||||
}
|
||||
|
||||
export function DiskSpaceCard({
|
||||
usedPercent = 0,
|
||||
totalGb,
|
||||
freeGb,
|
||||
}: DiskSpaceCardProps) {
|
||||
return (
|
||||
<Card className="py-0">
|
||||
<CardContent className="flex items-center gap-3 px-4 py-3">
|
||||
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-blue-500/10">
|
||||
<HardDrive className="size-4 text-blue-500" />
|
||||
</div>
|
||||
<div className="flex flex-1 flex-col gap-1">
|
||||
<span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
|
||||
Espace Disque
|
||||
</span>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-semibold tabular-nums text-foreground">
|
||||
{usedPercent.toFixed(1)}% utilisé
|
||||
</span>
|
||||
{totalGb !== undefined && (
|
||||
<span className="text-[10px] tabular-nums text-muted-foreground">
|
||||
{totalGb} GB total
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Progress
|
||||
value={usedPercent}
|
||||
className="h-1.5 bg-muted [&>[data-slot=progress-indicator]]:bg-blue-500"
|
||||
/>
|
||||
{freeGb !== undefined && (
|
||||
<span className="text-[10px] text-muted-foreground">
|
||||
{freeGb.toFixed(1)} GB libres
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
61
frontend/src/app/admin/system/page.tsx
Normal file
61
frontend/src/app/admin/system/page.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
"use client";
|
||||
|
||||
import { Settings, AlertCircle, Loader2 } from "lucide-react";
|
||||
import { useSystemPage } from "./useSystemPage";
|
||||
import { CleanupSection } from "./CleanupSection";
|
||||
import { DiskSpaceCard } from "./DiskSpaceCard";
|
||||
import { ProviderStatus } from "../ProviderStatus";
|
||||
|
||||
export default function AdminSystemPage() {
|
||||
const { data, isLoading, error, isPurging, purgeResult, handleCleanup } = useSystemPage();
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-purple-600/20 rounded-lg flex items-center justify-center">
|
||||
<Settings className="w-5 h-5 text-purple-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-xl font-semibold text-foreground">Système</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Surveiller l'état du système et gérer les ressources
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="flex items-center gap-2 rounded-lg border border-red-200/30 bg-red-500/10 px-4 py-3 text-red-500">
|
||||
<AlertCircle className="size-4" />
|
||||
<span className="text-sm">{error}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoading && !data ? (
|
||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||
{[1, 2].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="h-[88px] animate-pulse rounded-lg border border-border bg-card"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||
<DiskSpaceCard
|
||||
usedPercent={data?.system?.disk?.used_percent}
|
||||
totalGb={data?.system?.disk?.total_gb}
|
||||
freeGb={data?.system?.disk?.free_gb}
|
||||
/>
|
||||
<CleanupSection
|
||||
trackedFilesCount={data?.cleanup?.tracked_files_count ?? 0}
|
||||
isPurging={isPurging}
|
||||
purgeResult={purgeResult}
|
||||
onCleanup={handleCleanup}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ProviderStatus data={data} isLoading={isLoading} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
20
frontend/src/app/admin/system/useSystemPage.ts
Normal file
20
frontend/src/app/admin/system/useSystemPage.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import { useAdminDashboard } from "../useAdminDashboard";
|
||||
import { useCleanup } from "../useCleanup";
|
||||
|
||||
export function useSystemPage() {
|
||||
const { data, isLoading, error } = useAdminDashboard();
|
||||
const { isPurging, purgeResult, error: cleanupError, triggerCleanup } = useCleanup();
|
||||
|
||||
const handleCleanup = () => triggerCleanup();
|
||||
|
||||
return {
|
||||
data,
|
||||
isLoading,
|
||||
error: error || cleanupError,
|
||||
isPurging,
|
||||
purgeResult,
|
||||
handleCleanup,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user