finish bills , po , jobcards relational fields creation

This commit is contained in:
2026-04-13 18:48:15 +03:00
parent 3eb471a604
commit 020ffccfd6
41 changed files with 11973 additions and 467 deletions
@@ -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>
}