"use client"; import { useMemo, useState } from "react"; type CheatFlag = { id: number; user_id: string; pseudo: string; game: "icarus" | "melon"; score: number; severity: "warning" | "high"; trigger_code: string; reason: string; details: { duration_ms?: number | null; actions?: number | null; merges?: number | null; previous_best?: number | null; } | null; created_at: string; }; const GAME_LABELS: Record = { icarus: "Vol d'Icare", melon: "Corne d'Abondance", }; function formatDuration(durationMs: number | null | undefined): string | null { if (durationMs == null || !Number.isFinite(durationMs)) return null; if (durationMs < 1000) return `${Math.round(durationMs)} ms`; const seconds = durationMs / 1000; if (seconds < 60) return `${seconds.toFixed(seconds < 10 ? 1 : 0)} s`; const minutes = Math.floor(seconds / 60); const remainingSeconds = Math.round(seconds % 60); return `${minutes} min ${remainingSeconds.toString().padStart(2, "0")} s`; } function FlagRow({ flag }: { flag: CheatFlag }) { const duration = formatDuration(flag.details?.duration_ms); const happenedAt = new Intl.DateTimeFormat("fr-FR", { timeZone: "Europe/Paris", dateStyle: "short", timeStyle: "medium", }).format(new Date(flag.created_at)); return (
  • {flag.severity === "high" ? "Fort" : "À vérifier"} {flag.pseudo} · {GAME_LABELS[flag.game]}

    {flag.reason}

    {flag.score}

    {happenedAt}

    {duration && durée : {duration}} {flag.details?.actions != null && actions : {flag.details.actions}} {flag.details?.merges != null && fusions : {flag.details.merges}} {flag.details?.previous_best != null && ancien record : {flag.details.previous_best}} {flag.trigger_code}
  • ); } export function AdminCheatFlags({ flags }: { flags: CheatFlag[] }) { const [game, setGame] = useState<"all" | CheatFlag["game"]>("all"); const filtered = useMemo( () => (game === "all" ? flags : flags.filter((flag) => flag.game === game)), [flags, game], ); const flaggedUsers = useMemo(() => new Set(flags.map((flag) => flag.user_id)).size, [flags]); return (

    Détection de triche

    Les scores restent acceptés. Cette liste signale seulement les parties qui ont déclenché un contrôle simple côté serveur.

    {flags.length} signalement{flags.length > 1 ? "s" : ""} · {flaggedUsers} joueur {flaggedUsers > 1 ? "s" : ""}

    {([ ["all", "Tous"], ["icarus", "Icare"], ["melon", "Corne"], ] as const).map(([value, label]) => ( ))}
    {filtered.length === 0 ? (

    Aucun signalement pour ce filtre.

    ) : ( )}
    ); }