"use client" import type { KeyboardEvent, ReactNode } from "react" import { RefreshCwIcon, SearchIcon } from "lucide-react" import { DashboardPage, DashboardTableShell, DashboardTableStateRow, DashboardToolbar, } from "@/components/dashboard-page" import { ListPagination } from "@/components/list-pagination" import { OptionCombobox } from "@/components/option-combobox" import { TagSelector } from "@/components/tag-selector" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import type { TagTree } from "@/lib/api/admin" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import type { DashboardCrudPageResult, DashboardCrudQueryFilter, } from "@/components/dashboard/crud" import { useDashboardPagedList, type DashboardPagedListOptions, } from "./use-dashboard-paged-list" export type DashboardListFilter = DashboardCrudQueryFilter & { label: string placeholder?: string defaultValue: string | number type?: "text" | "select" | "segment" | "tag" className?: string inputClassName?: string options?: ReadonlyArray<{ value: string; label: string }> tags?: TagTree[] searchPlaceholder?: string emptyText?: string icon?: ReactNode } export type DashboardListColumn = { key: string label: ReactNode className?: string render: (item: TItem, context: DashboardListRenderContext) => ReactNode } export type DashboardListRenderContext = { result: DashboardCrudPageResult loading: boolean reload: () => Promise resetFilters: () => void } export type DashboardListPageProps = { filters?: DashboardListFilter[] fetchList: DashboardPagedListOptions["fetchList"] columns?: DashboardListColumn[] getItemId?: (item: TItem) => string | number renderContent?: (context: DashboardListRenderContext) => ReactNode renderToolbarActions?: (context: DashboardListRenderContext) => ReactNode getRowClassName?: (item: TItem) => string | undefined onRowClick?: (item: TItem) => void pageSize?: number enabled?: boolean reloadKey?: string | number | null layout?: "page" | "fragment" tableShellClassName?: string labels: { refresh?: string query?: string loading: string empty: string loadFailed: string } } export function DashboardListPage({ filters = [], fetchList, columns, getItemId, renderContent, renderToolbarActions, getRowClassName, onRowClick, pageSize, enabled, reloadKey, layout = "page", tableShellClassName, labels, }: DashboardListPageProps) { const list = useDashboardPagedList({ filters, fetchList, pageSize, enabled, reloadKey, loadFailed: labels.loadFailed, }) const renderContext: DashboardListRenderContext = { result: list.result, loading: list.loading, reload: list.loadData, resetFilters: list.resetFilters, } function handleFilterKeyDown(event: KeyboardEvent) { if (event.key !== "Enter") return event.preventDefault() list.applyFilters() } const content = ( <> {labels.refresh ? ( ) : null} {renderToolbarActions?.(renderContext)} } > {filters.map((filter) => { const value = list.draftFilters[filter.name] if (filter.type === "segment") { return (
{(filter.options ?? []).map((option) => ( ))}
) } if (filter.type === "select") { return (
list.setDraftFilter(filter.name, nextValue || filter.defaultValue) } placeholder={filter.placeholder ?? filter.label} searchPlaceholder={filter.searchPlaceholder} emptyText={filter.emptyText} options={[...(filter.options ?? [])]} />
) } if (filter.type === "tag") { return (
list.setDraftFilter(filter.name, nextValue)} tags={filter.tags ?? []} placeholder={filter.placeholder ?? filter.label} searchPlaceholder={filter.searchPlaceholder} emptyText={filter.emptyText} rootOption={{ value: Number(filter.allValue ?? filter.defaultValue), label: filter.placeholder ?? filter.label, }} />
) } return (
{filter.icon ? (
{filter.icon}
) : null} list.setDraftFilter(filter.name, event.target.value) } onKeyDown={handleFilterKeyDown} placeholder={filter.placeholder ?? filter.label} className={filter.inputClassName} />
) })} {filters.some((filter) => filter.type !== "segment") ? ( ) : null}
} > {renderContent ? ( renderContent(renderContext) ) : columns ? ( {columns.map((column) => ( {column.label} ))} {list.result.results.map((item, index) => { const key = getItemId ? getItemId(item) : index return ( onRowClick(item) : undefined} > {columns.map((column) => ( {column.render(item, renderContext)} ))} ) })} {list.loading || list.result.results.length === 0 ? ( ) : null}
) : null}
) if (layout === "fragment") { return content } return {content} }