import { PgTable } from "drizzle-orm/pg-core"; import { DrizzleTableAdapter, DrizzleTableState } from "./drizzle-table-adapter"; import { ColumnDef } from "@tanstack/react-table"; import { SQL, and, count } from "drizzle-orm"; // Define a minimal DB interface that we need // Adjust this to match your actual db instance type interface DbInstance { select: (args?: any) => any; } export interface CreateTableServiceConfig { /** * Drizzle Database Instance */ db: DbInstance; /** * Drizzle Table Schema (e.g. users, orders) */ schema: PgTable; // Using PgTable as base, works for most Drizzle tables /** * React Table Columns Definition * Used to map accessorKeys to DB columns */ columns: ColumnDef[]; /** * Optional: Custom WHERE clause to always apply (e.g. deleted_at IS NULL) */ defaultWhere?: SQL; /** * Optional: Custom query modifier * Allows joining other tables or selecting specific fields */ customQuery?: (queryBuilder: any) => any; } /** * Factory function to create a standardized server action for a table. * * @example * export const getUsers = createTableService({ * db, * schema: users, * columns: userColumns * }); */ export function createTableService(config: CreateTableServiceConfig) { const { db, schema, columns, defaultWhere, customQuery } = config; // Return the actual Server Action function return async function getTableData(tableState: DrizzleTableState) { const adapter = new DrizzleTableAdapter(schema, columns); const { where, orderBy, limit, offset, groupBy } = adapter.getQueryParts(tableState); // Merge defaultWhere with dynamic where const finalWhere = defaultWhere ? (where ? and(defaultWhere, where) : defaultWhere) : where; // 1. Build Data Query let dataQuery = db.select() .from(schema) .where(finalWhere) .orderBy(...orderBy) .limit(limit) .offset(offset); if (groupBy && groupBy.length > 0) { dataQuery = dataQuery.groupBy(...groupBy); } // Apply custom query modifications (joins, etc) if (customQuery) { dataQuery = customQuery(dataQuery); } // 2. Build Count Query const countQuery = db.select({ count: count() }) .from(schema) .where(finalWhere); // Execute queries // We use Promise.all to run them in parallel const [data, countResult] = await Promise.all([ dataQuery, countQuery ]); const totalRows = Number(countResult[0]?.count ?? 0); return { data: data as TData[], totalRows, pageCount: Math.ceil(totalRows / (tableState.pagination?.pageSize ?? 10)) }; }; }