mirror of
https://github.com/xCyanGrizzly/DragonsStash.git
synced 2026-05-10 22:01:16 +00:00
feat: add skipped/failed packages tab to STL files page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import { useDataTable } from "@/hooks/use-data-table";
|
|||||||
import { getPackageColumns, type PackageRow } from "./package-columns";
|
import { getPackageColumns, type PackageRow } from "./package-columns";
|
||||||
import { PackageFilesDrawer } from "./package-files-drawer";
|
import { PackageFilesDrawer } from "./package-files-drawer";
|
||||||
import { IngestionStatus } from "./ingestion-status";
|
import { IngestionStatus } from "./ingestion-status";
|
||||||
|
import { SkippedPackagesTab } from "./skipped-packages-tab";
|
||||||
import { DataTable } from "@/components/shared/data-table";
|
import { DataTable } from "@/components/shared/data-table";
|
||||||
import { DataTablePagination } from "@/components/shared/data-table-pagination";
|
import { DataTablePagination } from "@/components/shared/data-table-pagination";
|
||||||
import { DataTableViewOptions } from "@/components/shared/data-table-view-options";
|
import { DataTableViewOptions } from "@/components/shared/data-table-view-options";
|
||||||
@@ -20,7 +21,10 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
import type { IngestionAccountStatus } from "@/lib/telegram/types";
|
import type { IngestionAccountStatus } from "@/lib/telegram/types";
|
||||||
|
import type { SkippedRow } from "./skipped-columns";
|
||||||
import { updatePackageCreator, updatePackageTags } from "../actions";
|
import { updatePackageCreator, updatePackageTags } from "../actions";
|
||||||
|
|
||||||
interface StlTableProps {
|
interface StlTableProps {
|
||||||
@@ -30,6 +34,9 @@ interface StlTableProps {
|
|||||||
ingestionStatus: IngestionAccountStatus[];
|
ingestionStatus: IngestionAccountStatus[];
|
||||||
availableTags: string[];
|
availableTags: string[];
|
||||||
searchTerm: string;
|
searchTerm: string;
|
||||||
|
skippedData: SkippedRow[];
|
||||||
|
skippedPageCount: number;
|
||||||
|
skippedTotalCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function StlTable({
|
export function StlTable({
|
||||||
@@ -39,6 +46,9 @@ export function StlTable({
|
|||||||
ingestionStatus,
|
ingestionStatus,
|
||||||
availableTags,
|
availableTags,
|
||||||
searchTerm,
|
searchTerm,
|
||||||
|
skippedData,
|
||||||
|
skippedPageCount,
|
||||||
|
skippedTotalCount,
|
||||||
}: StlTableProps) {
|
}: StlTableProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
@@ -77,6 +87,22 @@ export function StlTable({
|
|||||||
[router, pathname, searchParams]
|
[router, pathname, searchParams]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const activeTab = searchParams.get("tab") ?? "packages";
|
||||||
|
|
||||||
|
const updateTab = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
const params = new URLSearchParams(searchParams.toString());
|
||||||
|
if (value === "packages") {
|
||||||
|
params.delete("tab");
|
||||||
|
} else {
|
||||||
|
params.set("tab", value);
|
||||||
|
}
|
||||||
|
params.set("page", "1");
|
||||||
|
router.push(`${pathname}?${params.toString()}`, { scroll: false });
|
||||||
|
},
|
||||||
|
[router, pathname, searchParams]
|
||||||
|
);
|
||||||
|
|
||||||
const columns = getPackageColumns({
|
const columns = getPackageColumns({
|
||||||
onViewFiles: (pkg) => setViewPkg(pkg),
|
onViewFiles: (pkg) => setViewPkg(pkg),
|
||||||
searchTerm,
|
searchTerm,
|
||||||
@@ -125,39 +151,63 @@ export function StlTable({
|
|||||||
<IngestionStatus initialStatus={ingestionStatus} />
|
<IngestionStatus initialStatus={ingestionStatus} />
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<Tabs value={activeTab} onValueChange={updateTab}>
|
||||||
<div className="relative flex-1 min-w-[200px] max-w-sm">
|
<TabsList>
|
||||||
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
<TabsTrigger value="packages">Packages</TabsTrigger>
|
||||||
<Input
|
<TabsTrigger value="skipped" className="gap-1.5">
|
||||||
placeholder="Search packages or files..."
|
Skipped / Failed
|
||||||
value={searchValue}
|
{skippedTotalCount > 0 && (
|
||||||
onChange={(e) => updateSearch(e.target.value)}
|
<Badge variant="secondary" className="text-[10px] ml-1">
|
||||||
className="pl-9 h-9"
|
{skippedTotalCount}
|
||||||
/>
|
</Badge>
|
||||||
</div>
|
)}
|
||||||
{availableTags.length > 0 && (
|
</TabsTrigger>
|
||||||
<Select value={activeTag || "all"} onValueChange={updateTagFilter}>
|
</TabsList>
|
||||||
<SelectTrigger className="w-[160px] h-9">
|
|
||||||
<SelectValue placeholder="All Tags" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="all">All Tags</SelectItem>
|
|
||||||
{availableTags.map((tag) => (
|
|
||||||
<SelectItem key={tag} value={tag}>
|
|
||||||
{tag}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
)}
|
|
||||||
<DataTableViewOptions table={table} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DataTable
|
<TabsContent value="packages" className="space-y-4">
|
||||||
table={table}
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
emptyMessage="No packages found. Archives will appear here after ingestion."
|
<div className="relative flex-1 min-w-[200px] max-w-sm">
|
||||||
/>
|
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||||
<DataTablePagination table={table} totalCount={totalCount} />
|
<Input
|
||||||
|
placeholder="Search packages or files..."
|
||||||
|
value={searchValue}
|
||||||
|
onChange={(e) => updateSearch(e.target.value)}
|
||||||
|
className="pl-9 h-9"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{availableTags.length > 0 && (
|
||||||
|
<Select value={activeTag || "all"} onValueChange={updateTagFilter}>
|
||||||
|
<SelectTrigger className="w-[160px] h-9">
|
||||||
|
<SelectValue placeholder="All Tags" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="all">All Tags</SelectItem>
|
||||||
|
{availableTags.map((tag) => (
|
||||||
|
<SelectItem key={tag} value={tag}>
|
||||||
|
{tag}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
)}
|
||||||
|
<DataTableViewOptions table={table} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DataTable
|
||||||
|
table={table}
|
||||||
|
emptyMessage="No packages found. Archives will appear here after ingestion."
|
||||||
|
/>
|
||||||
|
<DataTablePagination table={table} totalCount={totalCount} />
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="skipped">
|
||||||
|
<SkippedPackagesTab
|
||||||
|
data={skippedData}
|
||||||
|
pageCount={skippedPageCount}
|
||||||
|
totalCount={skippedTotalCount}
|
||||||
|
/>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
|
||||||
<PackageFilesDrawer
|
<PackageFilesDrawer
|
||||||
pkg={viewPkg}
|
pkg={viewPkg}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { auth } from "@/lib/auth";
|
import { auth } from "@/lib/auth";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { listPackages, searchPackages, getIngestionStatus, getAllPackageTags } from "@/lib/telegram/queries";
|
import { listPackages, searchPackages, getIngestionStatus, getAllPackageTags, listSkippedPackages, countSkippedPackages } from "@/lib/telegram/queries";
|
||||||
import { StlTable } from "./_components/stl-table";
|
import { StlTable } from "./_components/stl-table";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -20,9 +20,10 @@ export default async function StlFilesPage({ searchParams }: Props) {
|
|||||||
const search = (params.search as string) ?? "";
|
const search = (params.search as string) ?? "";
|
||||||
const creator = (params.creator as string) || undefined;
|
const creator = (params.creator as string) || undefined;
|
||||||
const tag = (params.tag as string) || undefined;
|
const tag = (params.tag as string) || undefined;
|
||||||
|
const tab = (params.tab as string) ?? "packages";
|
||||||
|
|
||||||
// Fetch packages, ingestion status, and available tags in parallel
|
// Fetch packages, ingestion status, tags, and skipped count in parallel
|
||||||
const [result, ingestionStatus, availableTags] = await Promise.all([
|
const [result, ingestionStatus, availableTags, skippedCount] = await Promise.all([
|
||||||
search
|
search
|
||||||
? searchPackages({
|
? searchPackages({
|
||||||
query: search,
|
query: search,
|
||||||
@@ -40,8 +41,14 @@ export default async function StlFilesPage({ searchParams }: Props) {
|
|||||||
}),
|
}),
|
||||||
getIngestionStatus(),
|
getIngestionStatus(),
|
||||||
getAllPackageTags(),
|
getAllPackageTags(),
|
||||||
|
countSkippedPackages(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Fetch skipped packages only if on that tab
|
||||||
|
const skippedResult = tab === "skipped"
|
||||||
|
? await listSkippedPackages({ page, limit: perPage })
|
||||||
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StlTable
|
<StlTable
|
||||||
data={result.items}
|
data={result.items}
|
||||||
@@ -50,6 +57,9 @@ export default async function StlFilesPage({ searchParams }: Props) {
|
|||||||
ingestionStatus={ingestionStatus}
|
ingestionStatus={ingestionStatus}
|
||||||
availableTags={availableTags}
|
availableTags={availableTags}
|
||||||
searchTerm={search}
|
searchTerm={search}
|
||||||
|
skippedData={skippedResult?.items ?? []}
|
||||||
|
skippedPageCount={skippedResult?.pagination.totalPages ?? 0}
|
||||||
|
skippedTotalCount={skippedCount}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user