finish bills , po , jobcards relational fields creation
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import React from "react"
|
||||
import { DataTable, type ActionsColumnOptions } from "@/shared/data-view/table-view"
|
||||
import { DataTable, type ActionsColumnOptions, type DataViewProps } from "@/shared/data-view/table-view"
|
||||
import { useResourcePage, type UseResourcePageOptions, type ResourceItem, type ResourcePageClient } from "./use-resource-page"
|
||||
import type { ColumnDef } from "@tanstack/react-table"
|
||||
|
||||
@@ -28,10 +28,13 @@ type ReactNodeOrRender<TClient extends ResourcePageClient> =
|
||||
| React.ReactNode
|
||||
| ((context: CrudResourceContext<TClient>) => React.ReactNode)
|
||||
|
||||
type ManagedTableProps = "columns" | "data" | "pagination" | "sorting" | "onChange" | "isLoading"
|
||||
|
||||
export type CrudResourceProps<TClient extends ResourcePageClient> = UseResourcePageOptions<TClient> & {
|
||||
columns: ColumnDef<ResourceItem<TClient>>[] | ((helpers: CrudResourceColumnHelpers<TClient>) => ColumnDef<ResourceItem<TClient>>[])
|
||||
onRowClick?: (row: ResourceItem<TClient>) => void
|
||||
tableHeader?: ReactNodeOrRender<TClient>
|
||||
tableProps?: Omit<Partial<DataViewProps<ResourceItem<TClient>>>, ManagedTableProps>
|
||||
render?: (table: React.ReactElement, context: CrudResourceContext<TClient>) => React.ReactElement
|
||||
}
|
||||
|
||||
@@ -44,6 +47,7 @@ export function CrudResource<TClient extends ResourcePageClient>({
|
||||
extraParams,
|
||||
onRowClick,
|
||||
tableHeader,
|
||||
tableProps,
|
||||
render,
|
||||
}: CrudResourceProps<TClient>) {
|
||||
type TItem = ResourceItem<TClient>
|
||||
@@ -79,13 +83,14 @@ export function CrudResource<TClient extends ResourcePageClient>({
|
||||
<>
|
||||
{tableHeader && (typeof tableHeader === "function" ? tableHeader(context) : tableHeader)}
|
||||
<DataTable
|
||||
{...tableProps}
|
||||
columns={columns}
|
||||
data={items}
|
||||
pagination={page.pagination}
|
||||
sorting={page.sorting}
|
||||
onChange={page.handleChange}
|
||||
isLoading={page.isLoading}
|
||||
onRowClick={onRowClick}
|
||||
onRowClick={onRowClick ?? tableProps?.onRowClick}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useMemo, useEffect, useRef } from "react"
|
||||
import {
|
||||
useReactTable,
|
||||
getCoreRowModel,
|
||||
flexRender,
|
||||
type ColumnDef,
|
||||
type RowSelectionState,
|
||||
} from "@tanstack/react-table"
|
||||
import {
|
||||
Table,
|
||||
@@ -15,8 +17,9 @@ import {
|
||||
TableRow,
|
||||
TableCell,
|
||||
} from "@/shared/components/ui/table"
|
||||
import { Checkbox } from "@/shared/components/ui/checkbox"
|
||||
import { DataViewProvider } from "./data-view-context"
|
||||
import type { DataViewProps } from "./types"
|
||||
import type { DataViewProps } from "./types"
|
||||
import { DataViewPagination } from "./data-view-pagination"
|
||||
import { Skeleton } from "@/shared/components/ui/skeleton"
|
||||
|
||||
@@ -29,20 +32,83 @@ export function DataTable<TData>({
|
||||
isLoading = false,
|
||||
onRowClick,
|
||||
slots,
|
||||
selection,
|
||||
}: DataViewProps<TData>) {
|
||||
const rowKeyStr = (selection?.rowKey as string) ?? "id"
|
||||
|
||||
// Persisted map of id → original row data across all pages
|
||||
const persistedMap = useRef<Map<string, TData>>(new Map())
|
||||
|
||||
// Current-page selection state that TanStack Table controls
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
|
||||
|
||||
// When the page/data changes, restore selection state for the new page from the map
|
||||
useEffect(() => {
|
||||
if (!selection) return
|
||||
const restored: RowSelectionState = {}
|
||||
data.forEach((row) => {
|
||||
const id = String((row as Record<string, unknown>)[rowKeyStr])
|
||||
if (persistedMap.current.has(id)) restored[id] = true
|
||||
})
|
||||
setRowSelection(restored)
|
||||
}, [data]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const selectionColumn: ColumnDef<TData, unknown> = useMemo(
|
||||
() => ({
|
||||
id: "__select__",
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected()
|
||||
? true
|
||||
: table.getIsSomePageRowsSelected()
|
||||
? "indeterminate"
|
||||
: false
|
||||
}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
),
|
||||
size: 40,
|
||||
enableSorting: false,
|
||||
}),
|
||||
[],
|
||||
)
|
||||
|
||||
const resolvedColumns = useMemo(
|
||||
() =>
|
||||
selection
|
||||
? [selectionColumn, ...(columns as ColumnDef<TData, unknown>[])]
|
||||
: (columns as ColumnDef<TData, unknown>[]),
|
||||
[selection, columns, selectionColumn],
|
||||
)
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns: columns as ColumnDef<TData, unknown>[],
|
||||
columns: resolvedColumns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
manualPagination: true,
|
||||
manualSorting: true,
|
||||
pageCount: pagination.pageCount,
|
||||
enableRowSelection: !!selection,
|
||||
getRowId: selection
|
||||
? (row) => String((row as Record<string, unknown>)[(selection.rowKey as string) ?? "id"])
|
||||
: undefined,
|
||||
state: {
|
||||
sorting,
|
||||
pagination: {
|
||||
pageIndex: pagination.page - 1,
|
||||
pageSize: pagination.pageSize,
|
||||
},
|
||||
rowSelection,
|
||||
},
|
||||
onSortingChange: (updater) => {
|
||||
const next = typeof updater === "function" ? updater(sorting) : updater
|
||||
@@ -61,6 +127,22 @@ export function DataTable<TData>({
|
||||
},
|
||||
})
|
||||
},
|
||||
onRowSelectionChange: (updater) => {
|
||||
const next = typeof updater === "function" ? updater(rowSelection) : updater
|
||||
setRowSelection(next)
|
||||
if (selection) {
|
||||
// Sync current page into the persisted map
|
||||
data.forEach((row) => {
|
||||
const id = String((row as Record<string, unknown>)[rowKeyStr])
|
||||
if (next[id]) {
|
||||
persistedMap.current.set(id, row)
|
||||
} else {
|
||||
persistedMap.current.delete(id)
|
||||
}
|
||||
})
|
||||
selection.onSelectionChange(Array.from(persistedMap.current.values()))
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
|
||||
@@ -26,6 +26,12 @@ export type DataViewSlots = {
|
||||
footer?: ReactNode
|
||||
}
|
||||
|
||||
export type DataViewSelection<TData> = {
|
||||
onSelectionChange: (selectedRows: TData[]) => void
|
||||
/** Field used as the unique row identifier. Defaults to "id". */
|
||||
rowKey?: keyof TData
|
||||
}
|
||||
|
||||
export type DataViewProps<TData> = {
|
||||
columns: ColumnDef<TData, any>[]
|
||||
data: TData[]
|
||||
@@ -35,4 +41,5 @@ export type DataViewProps<TData> = {
|
||||
isLoading?: boolean
|
||||
onRowClick?: (row: TData) => void
|
||||
slots?: DataViewSlots
|
||||
selection?: DataViewSelection<TData>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user