Major changes across backend, frontend, infrastructure: - Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud) - Admin panel: user management, pricing, settings - Glossary system with CSV import/export - Subscription and tier quota management - Security hardening (rate limiting, API key auth, path traversal fixes) - Docker compose for dev, prod, and IONOS deployment - Alembic migrations for new tables - Frontend: dashboard, pricing page, landing page, i18n (en/fr) - Test suite and verification scripts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
110 lines
4.1 KiB
TypeScript
110 lines
4.1 KiB
TypeScript
"use client"
|
|
|
|
import { useState, useCallback } from "react"
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
} from "@/components/ui/card"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Progress } from "@/components/ui/progress"
|
|
import {
|
|
HeartPulse,
|
|
HardDrive,
|
|
FileWarning,
|
|
Trash2,
|
|
Loader2,
|
|
} from "lucide-react"
|
|
|
|
export function SystemHealthCards() {
|
|
const [purging, setPurging] = useState(false)
|
|
const [orphanedCount, setOrphanedCount] = useState(12)
|
|
const diskUsed = 42
|
|
|
|
const handlePurge = useCallback(() => {
|
|
setPurging(true)
|
|
setTimeout(() => {
|
|
setPurging(false)
|
|
setOrphanedCount(0)
|
|
}, 1800)
|
|
}, [])
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 gap-3 md:grid-cols-3">
|
|
{/* Server Health */}
|
|
<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-[oklch(0.59_0.16_145/0.1)]">
|
|
<HeartPulse className="size-4 text-[oklch(0.59_0.16_145)]" />
|
|
</div>
|
|
<div className="flex flex-1 flex-col gap-0.5">
|
|
<span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
|
|
Server Health
|
|
</span>
|
|
<div className="flex items-center gap-1.5">
|
|
<span className="relative flex size-2">
|
|
<span className="absolute inline-flex size-full animate-ping rounded-full bg-[oklch(0.59_0.16_145)] opacity-75" />
|
|
<span className="relative inline-flex size-2 rounded-full bg-[oklch(0.59_0.16_145)]" />
|
|
</span>
|
|
<span className="text-sm font-semibold text-foreground">All Systems Operational</span>
|
|
</div>
|
|
<span className="text-[10px] text-muted-foreground">Uptime: 99.97% (30d)</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Disk Space */}
|
|
<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-accent/10">
|
|
<HardDrive className="size-4 text-accent" />
|
|
</div>
|
|
<div className="flex flex-1 flex-col gap-1">
|
|
<span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
|
|
Disk Space
|
|
</span>
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm font-semibold tabular-nums text-foreground">{diskUsed}% used</span>
|
|
<span className="text-[10px] tabular-nums text-muted-foreground">84 / 200 GB</span>
|
|
</div>
|
|
<Progress
|
|
value={diskUsed}
|
|
className="h-1.5 bg-muted [&>[data-slot=progress-indicator]]:bg-accent"
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Temporary Files */}
|
|
<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-destructive/10">
|
|
<FileWarning className="size-4 text-destructive" />
|
|
</div>
|
|
<div className="flex flex-1 flex-col gap-0.5">
|
|
<span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
|
|
Temporary Files
|
|
</span>
|
|
<span className="text-sm font-semibold tabular-nums text-foreground">
|
|
{orphanedCount} orphaned files
|
|
</span>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
className="h-7 shrink-0 gap-1.5 border-destructive/30 text-destructive hover:bg-destructive/10 hover:text-destructive text-xs"
|
|
onClick={handlePurge}
|
|
disabled={purging || orphanedCount === 0}
|
|
>
|
|
{purging ? (
|
|
<Loader2 className="size-3 animate-spin" />
|
|
) : (
|
|
<Trash2 className="size-3" />
|
|
)}
|
|
{purging ? "Purging..." : orphanedCount === 0 ? "Clean" : "Purge"}
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
)
|
|
}
|