feat(admin): dashboard home, telemetry section, enrollment ops screen
- mission-control dashboard: now strip, tournament quick actions, usage sparklines, error feed, live activity, tools list - /admin/telemetry with functions/events/errors/players/usage child routes, hand-rolled svg chart primitives, decomposed activities table - error browser with grouping and mark-resolved - consolidated per-tournament enrollment screen; hub simplified with run tournament promoted - /admin/activities redirects to /admin/telemetry/functions
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import { List, Text } from "@mantine/core";
|
||||
import { Plural, Trans, useLingui } from "@lingui/react/macro";
|
||||
import {
|
||||
DatabaseIcon,
|
||||
TreeStructureIcon,
|
||||
TrophyIcon,
|
||||
MedalIcon,
|
||||
CrownIcon,
|
||||
} from "@phosphor-icons/react";
|
||||
import ListLink from "@/components/list-link";
|
||||
import ListButton from "@/components/list-button";
|
||||
import { getBadgeMigrationStatus, migrateBadgeProgress } from "@/features/badges/server";
|
||||
import { useServerQuery } from "@/lib/tanstack-query/hooks";
|
||||
import toast from "@/lib/sonner";
|
||||
|
||||
export const ToolsList = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const { data: migrationStatus, refetch: refetchMigrationStatus } = useServerQuery({
|
||||
queryKey: ["admin", "badge-migration-status"],
|
||||
queryFn: () => getBadgeMigrationStatus(),
|
||||
options: {
|
||||
refetchInterval: (query) =>
|
||||
query.state.data?.state === "running" ? 2000 : false,
|
||||
},
|
||||
});
|
||||
|
||||
const isMigrating = migrationStatus?.state === "running";
|
||||
|
||||
const handleMigrateBadges = async () => {
|
||||
if (isMigrating) return;
|
||||
|
||||
const result = await migrateBadgeProgress();
|
||||
if (result.success) {
|
||||
toast.success(
|
||||
result.data.started
|
||||
? t`Badge recalculation started in the background`
|
||||
: t`Badge recalculation is already running`
|
||||
);
|
||||
} else {
|
||||
toast.error(t`Failed to start badge recalculation`);
|
||||
}
|
||||
await refetchMigrationStatus();
|
||||
};
|
||||
|
||||
return (
|
||||
<List p="0">
|
||||
<ListLink
|
||||
label={t`Manage Tournaments`}
|
||||
Icon={TrophyIcon}
|
||||
to="/admin/tournaments"
|
||||
/>
|
||||
<ListLink
|
||||
label={t`Award Badges`}
|
||||
Icon={CrownIcon}
|
||||
to="/admin/badges"
|
||||
/>
|
||||
<ListButton
|
||||
label={t`Migrate Badge Progress`}
|
||||
Icon={MedalIcon}
|
||||
onClick={handleMigrateBadges}
|
||||
loading={isMigrating}
|
||||
/>
|
||||
{migrationStatus?.state === "running" && (
|
||||
<Text size="sm" c="dimmed" px="md" py="xs">
|
||||
<Trans>
|
||||
Recalculating badges in the background ({migrationStatus.playersProcessed}/{migrationStatus.totalPlayers})
|
||||
</Trans>
|
||||
</Text>
|
||||
)}
|
||||
{migrationStatus?.state === "completed" && (
|
||||
<Text size="sm" c="dimmed" px="md" py="xs">
|
||||
<Trans>
|
||||
Last recalculation:{" "}
|
||||
<Plural
|
||||
value={migrationStatus.result.totalBadgesEarned}
|
||||
one="# badge earned"
|
||||
other="# badges earned"
|
||||
/>
|
||||
</Trans>
|
||||
</Text>
|
||||
)}
|
||||
{migrationStatus?.state === "failed" && (
|
||||
<Text size="sm" c="red" px="md" py="xs">
|
||||
<Trans>Badge recalculation failed. Check the server logs.</Trans>
|
||||
</Text>
|
||||
)}
|
||||
<ListButton
|
||||
label={t`Open Pocketbase`}
|
||||
Icon={DatabaseIcon}
|
||||
onClick={() =>
|
||||
window.location.replace(process.env.POCKETBASE_URL! + "/_/")
|
||||
}
|
||||
/>
|
||||
<ListLink
|
||||
label={t`Bracket Preview`}
|
||||
Icon={TreeStructureIcon}
|
||||
to="/admin/preview"
|
||||
/>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToolsList;
|
||||
Reference in New Issue
Block a user