diff --git a/src/app/_components/DownloadedScriptsTab.tsx b/src/app/_components/DownloadedScriptsTab.tsx index bb158c3..4bb3f0e 100644 --- a/src/app/_components/DownloadedScriptsTab.tsx +++ b/src/app/_components/DownloadedScriptsTab.tsx @@ -8,7 +8,9 @@ import { ScriptDetailModal } from "./ScriptDetailModal"; import { CategorySidebar } from "./CategorySidebar"; import { FilterBar, type FilterState } from "./FilterBar"; import { ViewToggle } from "./ViewToggle"; +import { ConfirmationModal } from "./ConfirmationModal"; import { Button } from "./ui/button"; +import { RefreshCw } from "lucide-react"; import type { ScriptCard as ScriptCardType } from "~/types/script"; import type { Server } from "~/types/server"; import { getDefaultFilters, mergeFiltersWithDefaults } from "./filterUtils"; @@ -32,8 +34,15 @@ export function DownloadedScriptsTab({ const [filters, setFilters] = useState(getDefaultFilters()); const [saveFiltersEnabled, setSaveFiltersEnabled] = useState(false); const [isLoadingFilters, setIsLoadingFilters] = useState(true); + const [updateAllConfirmOpen, setUpdateAllConfirmOpen] = useState(false); + const [updateResult, setUpdateResult] = useState<{ + successCount: number; + failCount: number; + failed: { slug: string; error: string }[]; + } | null>(null); const gridRef = useRef(null); + const utils = api.useUtils(); const { data: scriptCardsData, isLoading: githubLoading, @@ -50,6 +59,27 @@ export function DownloadedScriptsTab({ { enabled: !!selectedSlug }, ); + const loadMultipleScriptsMutation = api.scripts.loadMultipleScripts.useMutation({ + onSuccess: (data) => { + void utils.scripts.getAllDownloadedScripts.invalidate(); + void utils.scripts.getScriptCardsWithCategories.invalidate(); + setUpdateResult({ + successCount: data.successful?.length ?? 0, + failCount: data.failed?.length ?? 0, + failed: data.failed ?? [], + }); + setTimeout(() => setUpdateResult(null), 8000); + }, + onError: (error) => { + setUpdateResult({ + successCount: 0, + failCount: 1, + failed: [{ slug: "Request failed", error: error.message }], + }); + setTimeout(() => setUpdateResult(null), 8000); + }, + }); + // Load SAVE_FILTER setting, saved filters, and view mode on component mount useEffect(() => { const loadSettings = async () => { @@ -416,6 +446,21 @@ export function DownloadedScriptsTab({ setSelectedSlug(null); }; + const handleUpdateAllClick = () => { + setUpdateResult(null); + setUpdateAllConfirmOpen(true); + }; + + const handleUpdateAllConfirm = () => { + setUpdateAllConfirmOpen(false); + const slugs = downloadedScripts + .map((s) => s.slug) + .filter((slug): slug is string => Boolean(slug)); + if (slugs.length > 0) { + loadMultipleScriptsMutation.mutate({ slugs }); + } + }; + if (githubLoading || localLoading) { return (
@@ -508,6 +553,43 @@ export function DownloadedScriptsTab({ {/* Main Content */}
+ {/* Update all downloaded scripts */} +
+ + {updateResult && ( + + Updated {updateResult.successCount} successfully + {updateResult.failCount > 0 + ? `, ${updateResult.failCount} failed` + : ""} + . + {updateResult.failCount > 0 && updateResult.failed.length > 0 && ( + `${f.slug}: ${f.error}`).join("\n")}> + (hover for details) + + )} + + )} +
+ {/* Enhanced Filter Bar */} + + setUpdateAllConfirmOpen(false)} + onConfirm={handleUpdateAllConfirm} + title="Update all downloaded scripts" + message={`Update all ${downloadedScripts.length} downloaded scripts? This may take several minutes.`} + variant="simple" + confirmButtonText="Update all" + cancelButtonText="Cancel" + />