diff --git a/src/app/(app)/stls/_components/creator-filter.tsx b/src/app/(app)/stls/_components/creator-filter.tsx new file mode 100644 index 0000000..e744bc3 --- /dev/null +++ b/src/app/(app)/stls/_components/creator-filter.tsx @@ -0,0 +1,82 @@ +"use client"; + +import { useState } from "react"; +import { Check, ChevronsUpDown } from "lucide-react"; +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; + +interface CreatorFilterProps { + creators: string[]; + value: string; // active creator, "" when none + onChange: (creator: string) => void; // "" clears the filter +} + +export function CreatorFilter({ creators, value, onChange }: CreatorFilterProps) { + const [open, setOpen] = useState(false); + + return ( + + + + + + + + + No creators found. + + { + onChange(""); + setOpen(false); + }} + > + + All Creators + + {creators.map((creator) => ( + { + onChange(creator); + setOpen(false); + }} + > + + {creator} + + ))} + + + + + + ); +} diff --git a/src/app/(app)/stls/_components/stl-table.tsx b/src/app/(app)/stls/_components/stl-table.tsx index 9235e4d..d914bb0 100644 --- a/src/app/(app)/stls/_components/stl-table.tsx +++ b/src/app/(app)/stls/_components/stl-table.tsx @@ -3,7 +3,7 @@ import { useState, useCallback, useTransition, useMemo, useRef } from "react"; import { useRouter, usePathname, useSearchParams } from "next/navigation"; import { toast } from "sonner"; -import { Search, Layers, Upload } from "lucide-react"; +import { Search, Layers, Upload, Send } from "lucide-react"; import { UploadDialog } from "./upload-dialog"; import { useDataTable } from "@/hooks/use-data-table"; import { @@ -16,6 +16,7 @@ import { import { PackageFilesDrawer } from "./package-files-drawer"; import { IngestionStatus } from "./ingestion-status"; import { SkippedPackagesTab } from "./skipped-packages-tab"; +import { CreatorFilter } from "./creator-filter"; import { DataTable } from "@/components/shared/data-table"; import { DataTablePagination } from "@/components/shared/data-table-pagination"; import { DataTableViewOptions } from "@/components/shared/data-table-view-options"; @@ -49,6 +50,7 @@ import { createGroupAction, removeFromGroupAction, sendAllInGroupAction, + sendAllFromCreatorAction, updateGroupPreviewAction, mergeGroupsAction, } from "../actions"; @@ -59,6 +61,7 @@ interface StlTableProps { totalCount: number; ingestionStatus: IngestionAccountStatus[]; availableTags: string[]; + availableCreators: string[]; searchTerm: string; skippedData: SkippedRow[]; skippedPageCount: number; @@ -74,6 +77,7 @@ export function StlTable({ totalCount, ingestionStatus, availableTags, + availableCreators, searchTerm, skippedData, skippedPageCount, @@ -85,6 +89,7 @@ export function StlTable({ const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); + const activeCreator = searchParams.get("creator") ?? ""; const [searchValue, setSearchValue] = useState(searchParams.get("search") ?? ""); const [viewPkg, setViewPkg] = useState(null); @@ -207,6 +212,20 @@ export function StlTable({ [router, pathname, searchParams] ); + const updateCreatorFilter = useCallback( + (value: string) => { + const params = new URLSearchParams(searchParams.toString()); + if (value) { + params.set("creator", value); + params.set("page", "1"); + } else { + params.delete("creator"); + } + router.push(`${pathname}?${params.toString()}`, { scroll: false }); + }, + [router, pathname, searchParams] + ); + const activeTab = searchParams.get("tab") ?? "packages"; const updateTab = useCallback( @@ -277,6 +296,23 @@ export function StlTable({ [router] ); + const handleSendAllFromCreator = useCallback(() => { + if (!confirm(`Send all packages from "${activeCreator}" to your Telegram?`)) return; + startTransition(async () => { + const result = await sendAllFromCreatorAction(activeCreator); + if (result.success) { + const { queued, skipped } = result.data; + toast.success( + `Queued ${queued} package${queued === 1 ? "" : "s"} from ${activeCreator}` + + (skipped ? ` (${skipped} already queued)` : "") + ); + router.refresh(); + } else { + toast.error(result.error); + } + }); + }, [activeCreator, router]); + const handleRemoveFromGroup = useCallback( (packageId: string) => { startTransition(async () => { @@ -500,11 +536,29 @@ export function StlTable({ )} + {availableCreators.length > 0 && ( + + )} + {activeCreator && ( + + )} {selectedPackages.size >= 2 && (