import { Button } from "@repo/shadcn-ui/components/button"; import { Input } from "@repo/shadcn-ui/components/input"; import type { ReactNode } from "react"; import { useId } from "react"; import { useEntityTable } from "../hooks/use-entity-table"; import { entityListLabels } from "../lib/entity-list"; import { EntityListPagination } from "./entity-list-controls"; import { EntityTableConfiguration } from "./entity-table-configuration"; import { EntityTableContent } from "./entity-table-content"; export { EntityAction, EntityRowActions, EntityStatus } from "./entity-list-controls"; export function EntityListHeader({ title, description, actions, locale = "es", }: { title: string; description?: string; actions?: ReactNode; locale?: string; }) { return ( {title} {description ?? `${entityListLabels(locale).description} ${title.toLocaleLowerCase(locale)}.`} {actions && {actions}} ); } export type EntityColumn = { id: string; label: string; render: (item: T) => ReactNode; value?: (item: T) => string | number | boolean | null | undefined; defaultSort?: "asc" | "desc"; hideable?: boolean; }; export function EntityTable({ items, columns, title, entityLabel = title, locale = "es", search, onSearchChange, searchPlaceholder, filters, filtersApplied = false, onClearFilters, filterKey = "", loading = false, unavailable = false, emptyMessage, rowClassName, onRowClick, layout = "content", }: { items: readonly T[]; columns: readonly EntityColumn[]; title: string; entityLabel?: string; locale?: string; search?: string; onSearchChange?: (value: string) => void; searchPlaceholder?: string; filters?: ReactNode; filtersApplied?: boolean; onClearFilters?: () => void; filterKey?: string; loading?: boolean; unavailable?: boolean; emptyMessage?: string; rowClassName?: (item: T) => string; onRowClick?: ((item: T) => void) | undefined; layout?: "content" | "fill"; }) { const id = useId(); const labels = entityListLabels(locale); const { table, total, range, pagination, setPagination, currentSearch, setLocalSearch } = useEntityTable({ items, columns, search, onSearchChange, filterKey }); const info = entityTableInfo( loading, unavailable, labels.loading, labels.range(range.from, range.to, total, entityLabel.toLocaleLowerCase(locale)) ); const message = entityTableMessage( loading, unavailable, Boolean(currentSearch || filtersApplied), labels, emptyMessage ); const clear = () => { (onSearchChange ?? setLocalSearch)(""); onClearFilters?.(); setPagination((previous) => ({ ...previous, pageIndex: 0 })); }; return ( [data-slot=table-container]]:min-h-0 [&>[data-slot=table-container]]:flex-1 [&>[data-slot=table-container]]:overflow-auto [&>[data-slot=table-container]]:[scrollbar-gutter:stable] [&_[data-slot=table-header]]:sticky [&_[data-slot=table-header]]:top-0 [&_[data-slot=table-header]]:z-20 [&_[data-slot=table-header]]:bg-background" : ""}`} > {searchPlaceholder ?? labels.search} (onSearchChange ?? setLocalSearch)(event.target.value)} placeholder={searchPlaceholder ?? labels.placeholder} value={currentSearch} /> {filters} {(currentSearch || filtersApplied) && ( {labels.clear} )} {info} setPagination((previous) => ({ ...previous, pageIndex: 0 }))} table={table} /> setPagination((previous) => ({ ...previous, pageIndex: 0 }))} rowClassName={rowClassName} table={table} title={title} unavailable={unavailable} /> ); } function entityTableInfo( loading: boolean, unavailable: boolean, loadingLabel: string, info: string ) { if (loading) { return loadingLabel; } return unavailable ? "—" : info; } function entityTableMessage( loading: boolean, unavailable: boolean, filtered: boolean, labels: ReturnType, emptyMessage: string | undefined ) { if (loading) { return labels.loading; } if (unavailable) { return labels.unavailable; } return filtered ? labels.noResults : (emptyMessage ?? labels.empty); }
{description ?? `${entityListLabels(locale).description} ${title.toLocaleLowerCase(locale)}.`}
{info}