"use client"; import { useState, useEffect, useCallback } from "react"; import { Card } from "@/shared/components"; import { useTranslations } from "next-intl"; interface ModelsDevStatus { enabled: boolean; lastSync: string | null; lastSyncModelCount: number; lastSyncCapabilityCount: number; nextSync: string | null; intervalMs: number; providerCount: number; modelCount: number; capabilityCount: number; } interface SyncResult { success: boolean; modelCount: number; providerCount: number; capabilityCount: number; error?: string; } export default function ModelsDevSyncTab() { const t = useTranslations("settings"); const [status, setStatus] = useState(null); const [loading, setLoading] = useState(true); const [syncing, setSyncing] = useState(false); const [saving, setSaving] = useState(false); const [enabled, setEnabled] = useState(false); const [intervalHours, setIntervalHours] = useState(24); const [draftIntervalHours, setDraftIntervalHours] = useState(24); const [feedback, setFeedback] = useState<{ type: "success" | "error"; message: string } | null>( null ); const fetchStatus = useCallback(async () => { try { const res = await fetch("/api/settings/models-dev?action=status"); if (res.ok) { const data = await res.json(); setStatus(data); } } catch { // Silently fail — sync may not be initialized yet } setLoading(false); }, []); useEffect(() => { Promise.all([fetchStatus(), fetch("/api/settings").then((r) => (r.ok ? r.json() : null))]) .then(([, settingsData]) => { if (settingsData) { setEnabled(settingsData.modelsDevSyncEnabled === true); const intervalMs = settingsData.modelsDevSyncInterval || 86400000; const hours = Math.round(intervalMs / 3600000); setIntervalHours(hours); setDraftIntervalHours(hours); } }) .catch((err) => { console.error("Failed to fetch models.dev settings:", err); setFeedback({ type: "error", message: "Failed to load settings" }); }); }, [fetchStatus]); const triggerSync = async () => { setSyncing(true); setFeedback(null); try { const res = await fetch("/api/settings/models-dev", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "sync" }), }); if (res.ok) { const result: SyncResult = await res.json(); if (result.success) { setFeedback({ type: "success", message: `Synced ${result.modelCount.toLocaleString()} pricing entries, ${result.capabilityCount.toLocaleString()} capabilities`, }); fetchStatus(); } else { setFeedback({ type: "error", message: result.error || "Sync failed" }); } } else { setFeedback({ type: "error", message: "Sync request failed" }); } } catch { setFeedback({ type: "error", message: "Network error" }); } finally { setSyncing(false); setTimeout(() => setFeedback(null), 5000); } }; const toggleEnabled = async () => { const newVal = !enabled; setEnabled(newVal); setSaving(true); try { const res = await fetch("/api/settings", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ modelsDevSyncEnabled: newVal }), }); if (!res.ok) { setEnabled(!newVal); setFeedback({ type: "error", message: t("enableSyncError") || "Failed to update" }); } else { setFeedback({ type: "success", message: "Settings saved" }); } } catch { setEnabled(!newVal); setFeedback({ type: "error", message: "Network error" }); } finally { setSaving(false); setTimeout(() => setFeedback(null), 3000); } }; const updateInterval = async (hours: number) => { const oldInterval = intervalHours; setIntervalHours(hours); setDraftIntervalHours(hours); try { const res = await fetch("/api/settings", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ modelsDevSyncInterval: hours * 3600000 }), }); if (!res.ok) { setIntervalHours(oldInterval); setDraftIntervalHours(oldInterval); setFeedback({ type: "error", message: t("enableSyncError") || "Failed to update" }); } else { setFeedback({ type: "success", message: "Interval updated" }); } } catch { setIntervalHours(oldInterval); setDraftIntervalHours(oldInterval); setFeedback({ type: "error", message: "Network error" }); } finally { setTimeout(() => setFeedback(null), 3000); } }; if (loading) { return (

{t("modelsDevTitle")}

{t("modelsDevDesc")}

{t("loading")}...
); } const formatLastSync = (iso: string | null) => { if (!iso) return t("never"); const d = new Date(iso); const now = new Date(); const diffMs = now.getTime() - d.getTime(); const diffMin = Math.floor(diffMs / 60000); if (diffMin < 1) return t("justNow"); if (diffMin < 60) return `${diffMin}m ago`; const diffHr = Math.floor(diffMin / 60); if (diffHr < 24) return `${diffHr}h ago`; return d.toLocaleDateString(); }; return (
{/* Main sync card */}

{t("modelsDevTitle")}

{t("modelsDevDesc")}

{feedback && ( {feedback.type === "success" ? "check_circle" : "error"} {" "} {feedback.message} )}
{/* Enable toggle */}

{t("modelsDevEnabled")}

{t("modelsDevEnabledDesc")}

{/* Sync interval */} {enabled && (

{t("modelsDevInterval")}

{draftIntervalHours}h
setDraftIntervalHours(parseInt(e.target.value))} onMouseUp={(e) => updateInterval(parseInt((e.target as HTMLInputElement).value))} onBlur={(e) => updateInterval(parseInt(e.target.value))} className="w-full accent-blue-500" />
1h 6h 24h 7d
)} {/* Manual sync button */}
{status?.lastSync && ( {t("lastSync")}: {formatLastSync(status.lastSync)} )}
{/* Stats card */} {status && (status.providerCount > 0 || status.modelCount > 0) && (

{t("modelsDevStats")}

{t("modelsDevStatsDesc")}

{status.providerCount.toLocaleString()}

{t("providers")}

{status.modelCount.toLocaleString()}

{t("modelsWithPricing")}

{status.capabilityCount.toLocaleString()}

{t("capabilities")}

{status.lastSyncModelCount > 0 ? status.lastSyncModelCount.toLocaleString() : "—"}

{t("lastSyncCount")}

{status.lastSync && (
{t("lastSyncFull")}: {new Date(status.lastSync).toLocaleString()}
)}
)} {/* Info card */}

{t("modelsDevInfo")}

{t("modelsDevInfoDesc")}

{t("modelsDevInfoResolution")}

{t("modelsDevInfoOrder")}

); }