feat(admin): server-safe StatusCard and Import hub polish
CI / check (push) Failing after 8s
CI / release (push) Skipped
CI / deploy (push) Skipped

Split OnlineUsersWidget from StatusCard, decouple ad delete button, sync badge import to ExternalTexts+WebsiteBadges, add Import section hub with cancelable SSE jobs and upload SQL option.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-08-01 15:48:21 +02:00
1 parent db957d7fb1
commit 9c4949186c
21 files changed
+576 -249

No files matched your search

+33 -12
View File
@@ -1,7 +1,7 @@
"use client";
import { Check, CloudDownload, Loader2 } from "lucide-react";
import { useCallback, useState } from "react";
import { useCallback, useRef, useState } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { adminFetch } from "@/lib/admin-fetch";
@@ -26,6 +26,7 @@ export function SyncAllClient() {
failed: number;
sources: number;
} | null>(null);
const abortRef = useRef<AbortController | null>(null);
const startSync = useCallback(async () => {
setSyncing(true);
@@ -33,11 +34,15 @@ export function SyncAllClient() {
setLog([]);
setResult(null);
const abort = new AbortController();
abortRef.current = abort;
try {
const res = await adminFetch("/api/admin/import/clone/sync-all", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
signal: abort.signal,
});
if (!res.body) {
@@ -117,26 +122,42 @@ export function SyncAllClient() {
setResult({ succeeded, failed, sources: 1 });
if (succeeded > 0) toast.success(`${succeeded} items synced`);
if (failed > 0) toast.error(`${failed} items failed`);
} catch {
toast.error("Sync request failed");
} catch (err) {
if ((err as Error).name === "AbortError") {
toast.info("Sync cancelled");
} else {
toast.error("Sync request failed");
}
} finally {
setSyncing(false);
abortRef.current = null;
}
}, []);
return (
<div className="space-y-4">
{!result && (
<Button onClick={startSync} disabled={syncing} size="lg">
{syncing ? (
<Loader2 className="w-4 h-4 animate-spin mr-2" />
) : (
<CloudDownload className="w-4 h-4 mr-2" />
<div className="flex items-center gap-2 flex-wrap">
<Button onClick={startSync} disabled={syncing} size="lg">
{syncing ? (
<Loader2 className="w-4 h-4 animate-spin mr-2" />
) : (
<CloudDownload className="w-4 h-4 mr-2" />
)}
{syncing
? `Syncing… ${progress ? `${progress.done}/${progress.total}` : ""}`
: "Start Sync All Sources"}
</Button>
{syncing && (
<Button
variant="outline"
size="lg"
onClick={() => abortRef.current?.abort()}
>
Cancel
</Button>
)}
{syncing
? `Syncing… ${progress ? `${progress.done}/${progress.total}` : ""}`
: "Start Sync All Sources"}
</Button>
</div>
)}
{progress && (