@postxl/generators 1.4.2 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend-rest-api/generators/model-controller.generator.js +109 -11
- package/dist/backend-rest-api/generators/model-controller.generator.js.map +1 -1
- package/dist/backend-rest-api/rest-api.generator.js +4 -0
- package/dist/backend-rest-api/rest-api.generator.js.map +1 -1
- package/dist/backend-rest-api/template/utils/src/fieldSelection.spec.ts +252 -0
- package/dist/backend-rest-api/template/utils/src/fieldSelection.ts +66 -0
- package/dist/backend-router-trpc/generators/model-routes.generator.js +27 -2
- package/dist/backend-router-trpc/generators/model-routes.generator.js.map +1 -1
- package/dist/backend-router-trpc/router-trpc.generator.d.ts +1 -0
- package/dist/backend-router-trpc/router-trpc.generator.js +1 -0
- package/dist/backend-router-trpc/router-trpc.generator.js.map +1 -1
- package/dist/backend-update/model-update-service.generator.js +145 -8
- package/dist/backend-update/model-update-service.generator.js.map +1 -1
- package/dist/backend-view/model-view-service.generator.js +276 -19
- package/dist/backend-view/model-view-service.generator.js.map +1 -1
- package/dist/backend-view/template/{filter.utils.test.ts → query.utils.test.ts} +101 -1
- package/dist/backend-view/template/query.utils.ts +444 -0
- package/dist/frontend-admin/generators/model-admin-page.generator.js +50 -32
- package/dist/frontend-admin/generators/model-admin-page.generator.js.map +1 -1
- package/dist/frontend-core/template/src/components/admin/column-header-state-icon.tsx +72 -0
- package/dist/frontend-core/template/src/components/admin/table-filter.tsx +195 -43
- package/dist/frontend-tables/generators/model-table.generator.js +108 -29
- package/dist/frontend-tables/generators/model-table.generator.js.map +1 -1
- package/dist/frontend-trpc-client/generators/model-hook.generator.js +130 -16
- package/dist/frontend-trpc-client/generators/model-hook.generator.js.map +1 -1
- package/dist/types/generators/model-type.generator.js +97 -31
- package/dist/types/generators/model-type.generator.js.map +1 -1
- package/dist/types/template/query.types.ts +151 -0
- package/dist/types/types.generator.d.ts +15 -0
- package/dist/types/types.generator.js +5 -3
- package/dist/types/types.generator.js.map +1 -1
- package/package.json +2 -2
- package/dist/backend-rest-api/generators/zod-exception-filter.generator.d.ts +0 -1
- package/dist/backend-rest-api/generators/zod-exception-filter.generator.js +0 -28
- package/dist/backend-rest-api/generators/zod-exception-filter.generator.js.map +0 -1
- package/dist/backend-view/template/filter.utils.ts +0 -218
- package/dist/frontend-core/template/src/components/admin/table-filter-header-icon.tsx +0 -30
- package/dist/types/template/filter.types.ts +0 -70
|
@@ -17,24 +17,25 @@ function generateModelTableComponent({ model, context, }) {
|
|
|
17
17
|
alias: filterFieldArrayName,
|
|
18
18
|
location: model.types.filter.field.location,
|
|
19
19
|
})
|
|
20
|
-
.addImport({ from: (0, generator_1.toPackageName)('@types'), items: [(0, generator_1.toTypeName)('FilterValue')] });
|
|
21
|
-
const hookDependencies = [];
|
|
20
|
+
.addImport({ from: (0, generator_1.toPackageName)('@types'), items: [(0, generator_1.toTypeName)('FilterValue'), (0, generator_1.toTypeName)('SortState')] });
|
|
22
21
|
const relatedModels = Array.from(model.relatedModelNames).map((n) => context.models.get(n));
|
|
22
|
+
// Generate label map names for column dependencies
|
|
23
|
+
const labelMapDependencies = [];
|
|
23
24
|
for (const relatedModel of relatedModels) {
|
|
24
25
|
imports.addType(relatedModel.types);
|
|
25
26
|
imports.addType(relatedModel.types.id);
|
|
26
|
-
|
|
27
|
+
labelMapDependencies.push(`${relatedModel.table.propName}LabelMap`);
|
|
27
28
|
}
|
|
28
29
|
const fieldDefinitions = generateFieldsDefinitions({ model, context });
|
|
29
30
|
return `
|
|
30
|
-
import { TrashIcon } from '@radix-ui/react-icons'
|
|
31
31
|
import { ColumnDef } from '@tanstack/react-table'
|
|
32
32
|
|
|
33
|
+
import { TrashIcon } from 'lucide-react'
|
|
33
34
|
import { useMemo, useEffect, useRef } from 'react'
|
|
34
35
|
|
|
35
36
|
import { TableFilter } from '@components/admin/table-filter'
|
|
36
|
-
import {
|
|
37
|
-
import { Button, Checkbox, DataGrid, DataGridViewMenu, useDataGrid } from '@postxl/ui-components'
|
|
37
|
+
import { ColumnHeaderStateIcon } from '@components/admin/column-header-state-icon'
|
|
38
|
+
import { Button, Checkbox, DataGrid, DataGridViewMenu, Input, useDataGrid } from '@postxl/ui-components'
|
|
38
39
|
import { useTRPC } from '@lib/trpc'
|
|
39
40
|
import { ParentSize } from '@visx/responsive'
|
|
40
41
|
|
|
@@ -48,25 +49,50 @@ export function ${model.table.component.name}({
|
|
|
48
49
|
data,
|
|
49
50
|
filters,
|
|
50
51
|
onFilterChange,
|
|
52
|
+
searchInput,
|
|
53
|
+
onSearchInputChange,
|
|
54
|
+
sort,
|
|
55
|
+
onSortChange,
|
|
51
56
|
onDataEdit,
|
|
57
|
+
onCellUpdate,
|
|
52
58
|
handleAddRow,
|
|
53
59
|
handleRemoveRows,
|
|
54
60
|
onCellChange,
|
|
55
61
|
onRowSelectionChange,
|
|
62
|
+
hasNextPage,
|
|
63
|
+
fetchNextPage,
|
|
64
|
+
total,
|
|
56
65
|
${relatedModels.map((rm) => `${rm.table.propName}`).join(',\n ')}
|
|
57
66
|
}: Readonly<{
|
|
58
67
|
tableHeight?: number
|
|
59
68
|
data: ${model.types.name}[]
|
|
60
69
|
filters: ${model.types.filter.type.name}
|
|
61
70
|
onFilterChange: (filterKey: ${model.types.filter.fieldType.name}, value: FilterValue) => void
|
|
62
|
-
|
|
71
|
+
searchInput: string
|
|
72
|
+
onSearchInputChange: (value: string) => void
|
|
73
|
+
sort?: SortState<${model.types.filter.fieldType.name}>
|
|
74
|
+
onSortChange?: (sort: SortState<${model.types.filter.fieldType.name}>) => void
|
|
75
|
+
onDataEdit?: (data: ${model.types.name}[]) => void
|
|
76
|
+
onCellUpdate?: (args: { rowIndex: number; columnId: string; value: unknown }) => void
|
|
63
77
|
${relatedModels.map((rm) => `${rm.table.propName}?: Map<${rm.types.id.name}, ${rm.types.name}>`).join('\n ')}
|
|
64
78
|
handleAddRow?: () => void
|
|
65
79
|
handleRemoveRows?: (countries: ${model.types.id.name}[]) => void
|
|
66
80
|
onCellChange?: (args: { rowIndex: number; columnId: string }) => void
|
|
67
81
|
onRowSelectionChange?: (rowIds: string[]) => void
|
|
82
|
+
// Infinite scroll props
|
|
83
|
+
hasNextPage?: boolean
|
|
84
|
+
fetchNextPage?: () => Promise<unknown>
|
|
85
|
+
total?: number
|
|
68
86
|
}>) {
|
|
69
87
|
const trpc = useTRPC()
|
|
88
|
+
|
|
89
|
+
${relatedModels.length > 0 ? '// Create label maps for relation fields (O(1) lookups in cells)' : ''}
|
|
90
|
+
${relatedModels
|
|
91
|
+
.map((rm) => ` const ${rm.table.propName}LabelMap = useMemo(
|
|
92
|
+
() => new Map(Array.from(${rm.table.propName}?.entries() ?? []).map(([key, entity]) => [key, entity.${rm.labelField.name}])),
|
|
93
|
+
[${rm.table.propName}],
|
|
94
|
+
)`)
|
|
95
|
+
.join('\n')}
|
|
70
96
|
|
|
71
97
|
const columns = useMemo<ColumnDef<${model.types.name}>[]>(
|
|
72
98
|
() => [
|
|
@@ -114,24 +140,57 @@ export function ${model.table.component.name}({
|
|
|
114
140
|
},
|
|
115
141
|
${fieldDefinitions.map((def) => generateColumn({ ...def, filterFieldArrayName, filterConfigName: model.types.filter.config.name, trpcRouteName: model.trpcRoute.name })).join(',\n')}
|
|
116
142
|
],
|
|
117
|
-
[filters, onFilterChange, handleRemoveRows, trpc.${model.trpcRoute.name}, ${
|
|
143
|
+
[filters, onFilterChange, handleRemoveRows, trpc.${model.trpcRoute.name}, sort, ${labelMapDependencies.join(', ')}],
|
|
118
144
|
)
|
|
119
145
|
|
|
120
146
|
const onRowAdd = () => {
|
|
121
147
|
handleAddRow?.()
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
148
|
+
// Return null to prevent scrolling - the modal handles the create flow
|
|
149
|
+
// and the table will refresh via query invalidation when the item is created
|
|
150
|
+
return null
|
|
126
151
|
}
|
|
127
152
|
|
|
153
|
+
// Convert server sort state to TanStack Table format (only when server-side sorting is enabled)
|
|
154
|
+
const tanstackSorting = useMemo(
|
|
155
|
+
() => sort?.map((s) => ({ id: s.field, desc: s.direction === 'desc' })) ?? [],
|
|
156
|
+
[sort],
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
// Handle sorting change - convert TanStack format back to server format
|
|
160
|
+
const handleSortingChange = useMemo(() => {
|
|
161
|
+
if (!onSortChange) return undefined
|
|
162
|
+
return (updater: any) => {
|
|
163
|
+
const newSorting = typeof updater === 'function' ? updater(tanstackSorting) : updater
|
|
164
|
+
onSortChange(
|
|
165
|
+
newSorting.map((s: { id: string; desc: boolean }) => ({
|
|
166
|
+
field: s.id as ${model.types.filter.fieldType.name},
|
|
167
|
+
direction: s.desc ? 'desc' : 'asc',
|
|
168
|
+
})),
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
}, [onSortChange, tanstackSorting])
|
|
172
|
+
|
|
128
173
|
const { table, ...dataGridProps } = useDataGrid({
|
|
129
174
|
columns,
|
|
130
175
|
data,
|
|
131
|
-
|
|
176
|
+
// Only use onDataChange for bulk operations (paste) when onCellUpdate is not provided
|
|
177
|
+
onDataChange: onCellUpdate ? undefined : onDataEdit, // TODO: how do we make sure filtered data is also directly updated, so not the data loading view is displayed after editing until the update is confirmed from the backend?
|
|
132
178
|
onCellFocus: onCellChange,
|
|
133
179
|
onRowAdd: handleAddRow ? onRowAdd : undefined,
|
|
134
180
|
enableSearch: true,
|
|
181
|
+
// Infinite scroll: trigger loading more when scrolling near the end
|
|
182
|
+
onLoadMore: hasNextPage ? fetchNextPage : undefined,
|
|
183
|
+
// Total row count for proper scrollbar sizing (reflects full dataset)
|
|
184
|
+
totalRowCount: total,
|
|
185
|
+
// Server-side sorting: only enable when onSortChange is provided
|
|
186
|
+
// Otherwise, keep default client-side sorting behavior
|
|
187
|
+
...(onSortChange && {
|
|
188
|
+
manualSorting: true,
|
|
189
|
+
sorting: tanstackSorting,
|
|
190
|
+
onSortingChange: handleSortingChange,
|
|
191
|
+
}),
|
|
192
|
+
// Pass individual cell update handler via meta for direct mutations
|
|
193
|
+
meta: onCellUpdate ? { onCellChange: onCellUpdate } : undefined,
|
|
135
194
|
})
|
|
136
195
|
|
|
137
196
|
// Track row selection changes and notify parent
|
|
@@ -161,7 +220,17 @@ export function ${model.table.component.name}({
|
|
|
161
220
|
|
|
162
221
|
return (
|
|
163
222
|
<div className="flex flex-col h-full min-h-0 gap-4">
|
|
164
|
-
<
|
|
223
|
+
<div className="flex items-center gap-4">
|
|
224
|
+
<div className="flex-1" />
|
|
225
|
+
<Input
|
|
226
|
+
type="text"
|
|
227
|
+
placeholder="Search..."
|
|
228
|
+
value={searchInput}
|
|
229
|
+
onChange={(e) => onSearchInputChange(e.target.value)}
|
|
230
|
+
className="w-64 h-8"
|
|
231
|
+
/>
|
|
232
|
+
<DataGridViewMenu table={table} />
|
|
233
|
+
</div>
|
|
165
234
|
<div className="h-full overflow-hidden">
|
|
166
235
|
<ParentSize>
|
|
167
236
|
{({ height }) => <DataGrid {...dataGridProps} table={table} height={tableHeight ?? height} />}
|
|
@@ -183,41 +252,48 @@ function quote(str) {
|
|
|
183
252
|
const singleQuote = String.raw `\'`;
|
|
184
253
|
return `'${str.replaceAll("'", singleQuote)}'`;
|
|
185
254
|
}
|
|
186
|
-
function generateColumn({ filterFieldArrayName, filterConfigName, trpcRouteName, column: { id, accessor, header, variant, size, readonly
|
|
187
|
-
let optionsString =
|
|
255
|
+
function generateColumn({ filterFieldArrayName, filterConfigName, trpcRouteName, column: { id, accessor, header, variant, size, readonly }, options, }) {
|
|
256
|
+
let optionsString = '';
|
|
257
|
+
let hasSearchString = '';
|
|
188
258
|
switch (options?.kind) {
|
|
189
259
|
case 'static': {
|
|
190
|
-
|
|
260
|
+
const optionsArr = `[${options.options
|
|
191
261
|
.map((o) => {
|
|
192
262
|
const label = quote(o.label);
|
|
193
263
|
const value = quote(o.value);
|
|
194
264
|
return `{ label: ${label}, value: ${value} }`;
|
|
195
265
|
})
|
|
196
266
|
.join(', ')}]`;
|
|
267
|
+
optionsString = `, options: ${optionsArr}`;
|
|
197
268
|
break;
|
|
198
269
|
}
|
|
199
270
|
case 'dynamic': {
|
|
200
|
-
optionsString = options.expression
|
|
271
|
+
optionsString = `, options: ${options.expression}`;
|
|
272
|
+
hasSearchString = variant === 'select' ? ', hasSearch: true' : '';
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
case 'optionsMap': {
|
|
276
|
+
// Use optionsMap for O(1) lookups - pass the pre-built label Map directly
|
|
277
|
+
optionsString = `, optionsMap: ${options.mapExpression}`;
|
|
278
|
+
hasSearchString = variant === 'select' ? ', hasSearch: true' : '';
|
|
201
279
|
break;
|
|
202
280
|
}
|
|
203
281
|
}
|
|
204
|
-
optionsString = optionsString ? `, options: ${optionsString}` : '';
|
|
205
|
-
const hasSearchString = variant === 'select' && options?.kind === 'dynamic' ? ', hasSearch: true' : '';
|
|
206
282
|
const cellString = ` cell: { variant: '${variant}'${optionsString}${hasSearchString} },\n`;
|
|
207
283
|
const editableString = readonly ? ' editable: false, \n' : '';
|
|
208
|
-
|
|
209
|
-
const filterString = ` ...${filterFieldArrayName}.includes('${
|
|
284
|
+
// Column ID is already the filter field name (e.g., 'authorEmail' not 'email')
|
|
285
|
+
const filterString = ` ...${filterFieldArrayName}.includes('${id}' as any) && {
|
|
210
286
|
headerMenuFooter: ({ open }) => (
|
|
211
287
|
<TableFilter
|
|
212
288
|
open={open}
|
|
213
|
-
field={'${
|
|
289
|
+
field={'${id}'}
|
|
214
290
|
filters={filters}
|
|
215
|
-
onChange={(val) => onFilterChange('${
|
|
291
|
+
onChange={(val) => onFilterChange('${id}', val)}
|
|
216
292
|
config={${filterConfigName}}
|
|
217
293
|
getFilterOptions={(args) => trpc.${trpcRouteName}.getFilterOptions.queryOptions(args)}
|
|
218
294
|
/>
|
|
219
295
|
),
|
|
220
|
-
headerCustomComponent: <
|
|
296
|
+
headerCustomComponent: <ColumnHeaderStateIcon filterValue={filters.${id}} sortState={sort?.find(s => s.field === '${id}')} />,
|
|
221
297
|
}\n`;
|
|
222
298
|
return `{
|
|
223
299
|
id: '${id}',
|
|
@@ -251,7 +327,7 @@ function generateCellVariant({ field, }) {
|
|
|
251
327
|
case 'enum':
|
|
252
328
|
return 'select';
|
|
253
329
|
case 'discriminatedUnion':
|
|
254
|
-
return '
|
|
330
|
+
return 'select';
|
|
255
331
|
default:
|
|
256
332
|
throw new utils_1.ExhaustiveSwitchCheck(field);
|
|
257
333
|
}
|
|
@@ -277,9 +353,11 @@ function getFieldDefinitionsForField(params) {
|
|
|
277
353
|
return [getFieldDefinitionForField(params)];
|
|
278
354
|
}
|
|
279
355
|
function getFieldDefinitionForField(params) {
|
|
356
|
+
// Use filter field name as column ID so sorting maps correctly to backend filter fields
|
|
357
|
+
const columnId = (0, model_type_generator_1.getFilterFieldName)(params.field.name, params.duParentField?.name);
|
|
280
358
|
return {
|
|
281
359
|
column: {
|
|
282
|
-
id:
|
|
360
|
+
id: columnId,
|
|
283
361
|
accessor: generateFieldAccessor(params),
|
|
284
362
|
header: generateColumnHeader(params),
|
|
285
363
|
variant: generateCellVariant(params),
|
|
@@ -339,8 +417,9 @@ function getOptions({ field, context }) {
|
|
|
339
417
|
}
|
|
340
418
|
case 'relation': {
|
|
341
419
|
const relatedModel = context.models.get(field.referencedModelName);
|
|
342
|
-
|
|
343
|
-
|
|
420
|
+
// Use optionsMap for O(1) lookups - reference the pre-built label map
|
|
421
|
+
const mapExpression = `${relatedModel.table.propName}LabelMap`;
|
|
422
|
+
return { kind: 'optionsMap', mapExpression };
|
|
344
423
|
}
|
|
345
424
|
default:
|
|
346
425
|
return undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-table.generator.js","sourceRoot":"","sources":["../../../src/frontend-tables/generators/model-table.generator.ts"],"names":[],"mappings":";;AAOA,
|
|
1
|
+
{"version":3,"file":"model-table.generator.js","sourceRoot":"","sources":["../../../src/frontend-tables/generators/model-table.generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,9 +10,11 @@ function generateModelHook({ model, context }) {
|
|
|
10
10
|
.addType(model.types)
|
|
11
11
|
.addType(model.types.id)
|
|
12
12
|
.addType(model.types.filter.type)
|
|
13
|
+
.addType(model.types.filter.fieldType)
|
|
13
14
|
.add(context.trpcClient)
|
|
14
15
|
.addImports({
|
|
15
|
-
[(0, generator_1.toPackageName)('@tanstack/react-query')]: [(0, generator_1.toFunctionName)('useQuery')],
|
|
16
|
+
[(0, generator_1.toPackageName)('@tanstack/react-query')]: [(0, generator_1.toFunctionName)('useQuery'), (0, generator_1.toFunctionName)('useInfiniteQuery')],
|
|
17
|
+
[(0, generator_1.toPackageName)('@types')]: [(0, generator_1.toTypeName)('SortState')],
|
|
16
18
|
});
|
|
17
19
|
let itemsCrudHook = { types: '', hooks: '', exports: '' };
|
|
18
20
|
if (model.update.kind === 'exists') {
|
|
@@ -37,9 +39,24 @@ function generateModelHook({ model, context }) {
|
|
|
37
39
|
${itemsHook.clone.name}: (data: ClonePayload) => Promise<${model.types.name}>
|
|
38
40
|
${itemsHook.cloneMany.name}: (data: ClonePayload[]) => Promise<${model.types.name}[]>`,
|
|
39
41
|
hooks: `
|
|
40
|
-
const invalidate = () => {
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
const invalidate = async () => {
|
|
43
|
+
const paginatedPredicate = (query: { queryKey: unknown }) => {
|
|
44
|
+
const key = query.queryKey
|
|
45
|
+
// tRPC query keys are arrays where first element contains the procedure path
|
|
46
|
+
// e.g., [['posts', 'getFilteredPaginated'], { input, type }]
|
|
47
|
+
if (Array.isArray(key) && Array.isArray(key[0])) {
|
|
48
|
+
const path = key[0]
|
|
49
|
+
return path[0] === '${trpcRoute.name}' && path[1] === 'getFilteredPaginated'
|
|
50
|
+
}
|
|
51
|
+
return false
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
await Promise.all([
|
|
55
|
+
queryClient.invalidateQueries({ queryKey: trpc.${trpcRoute.name}.getMap.queryKey() }),
|
|
56
|
+
queryClient.invalidateQueries({ queryKey: trpc.${trpcRoute.name}.getFiltered.queryKey() }),
|
|
57
|
+
// For infinite query, just refetch without resetting - keeps data visible during refetch
|
|
58
|
+
queryClient.refetchQueries({ predicate: paginatedPredicate }),
|
|
59
|
+
])
|
|
43
60
|
}
|
|
44
61
|
|
|
45
62
|
const updateMutation = useMutation(trpc.${trpcRoute.name}.update.mutationOptions({ onSuccess: invalidate }))
|
|
@@ -68,12 +85,59 @@ function generateModelHook({ model, context }) {
|
|
|
68
85
|
import { useMemo } from 'react'
|
|
69
86
|
${imports.generate()}
|
|
70
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Options for the ${itemsHook.name} hook.
|
|
90
|
+
*/
|
|
91
|
+
export type ${itemsHook.type.name}Options = {
|
|
92
|
+
/**
|
|
93
|
+
* When true, uses paginated fetching with infinite scroll support.
|
|
94
|
+
* When false, fetches all filtered data at once (original behavior).
|
|
95
|
+
* @default true
|
|
96
|
+
*/
|
|
97
|
+
paginated?: boolean
|
|
98
|
+
/**
|
|
99
|
+
* Number of items per page when using paginated mode.
|
|
100
|
+
* @default 100
|
|
101
|
+
*/
|
|
102
|
+
pageSize?: number
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Return type for the ${itemsHook.name} hook.
|
|
107
|
+
*/
|
|
71
108
|
export type ${itemsHook.type.name} = {
|
|
109
|
+
/** All items (unfiltered) as an array */
|
|
72
110
|
${itemsHook.list.name}: ${model.types.name}[]
|
|
111
|
+
/** All items (unfiltered) as a Map keyed by ID */
|
|
73
112
|
${itemsHook.map.name}: Map<${model.types.id.name}, ${model.types.name}>
|
|
113
|
+
/** Filtered (+ sorted) items as an array. When paginated, contains all loaded pages flattened. */
|
|
74
114
|
${itemsHook.filteredList.name}: ${model.types.name}[]
|
|
115
|
+
/** Filtered (+ sorted) items as a Map keyed by ID */
|
|
75
116
|
${itemsHook.filteredMap.name}: Map<${model.types.id.name}, ${model.types.name}>
|
|
117
|
+
/** Whether the initial data has been loaded */
|
|
76
118
|
${itemsHook.isLoaded.name}: boolean
|
|
119
|
+
/**
|
|
120
|
+
* Total count of ALL matching items in the database (not just loaded ones).
|
|
121
|
+
* Useful for displaying "Showing X of Y items".
|
|
122
|
+
* When not paginated, equals filteredList.length.
|
|
123
|
+
*/
|
|
124
|
+
total: number
|
|
125
|
+
/**
|
|
126
|
+
* Whether there are more pages available to load.
|
|
127
|
+
* Only relevant when using paginated mode.
|
|
128
|
+
*/
|
|
129
|
+
hasNextPage: boolean
|
|
130
|
+
/**
|
|
131
|
+
* Triggers loading the next page of data.
|
|
132
|
+
* Call this when the user scrolls near the end of the list.
|
|
133
|
+
* Only relevant when using paginated mode.
|
|
134
|
+
*/
|
|
135
|
+
fetchNextPage: () => Promise<unknown>
|
|
136
|
+
/**
|
|
137
|
+
* Whether the next page is currently being fetched.
|
|
138
|
+
* Use this to show a loading indicator during infinite scroll.
|
|
139
|
+
*/
|
|
140
|
+
isFetchingNextPage: boolean
|
|
77
141
|
|
|
78
142
|
${itemsCrudHook.types}
|
|
79
143
|
}
|
|
@@ -87,14 +151,48 @@ export const ${itemHook.name} = (id: ${model.types.id.name} | null): ${model.ty
|
|
|
87
151
|
return useMemo((): ${model.types.name} | null => (isPending || !data) ? null : data, [data, isPending])
|
|
88
152
|
}
|
|
89
153
|
|
|
90
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Hook for fetching and managing ${model.userFriendlyNamePlural}.
|
|
156
|
+
*
|
|
157
|
+
* @param filters - Optional filters to apply
|
|
158
|
+
* @param sort - Optional sort configuration
|
|
159
|
+
* @param options - Configuration options
|
|
160
|
+
* @param options.paginated - When true (default), uses infinite scroll pagination. When false, fetches all data at once.
|
|
161
|
+
* @param options.pageSize - Items per page when paginated (default: 100)
|
|
162
|
+
*/
|
|
163
|
+
export const ${itemsHook.name} = (
|
|
164
|
+
filters?: ${model.types.filter.type.name},
|
|
165
|
+
sort?: SortState<${model.types.filter.fieldType.name}>,
|
|
166
|
+
options: ${itemsHook.type.name}Options = {},
|
|
167
|
+
): ${itemsHook.type.name} => {
|
|
168
|
+
const { paginated = true, pageSize = 100 } = options
|
|
91
169
|
const trpc = ${context.trpcClient.name}()
|
|
92
|
-
|
|
170
|
+
|
|
171
|
+
// Determine if we need to fetch filtered data (when filters or sort are provided)
|
|
172
|
+
const isFiltered = useMemo(
|
|
173
|
+
() => (filters && Object.keys(filters).length > 0) || sort !== undefined,
|
|
174
|
+
[filters, sort],
|
|
175
|
+
)
|
|
93
176
|
|
|
94
177
|
const mapQuery = useQuery(trpc.${trpcRoute.name}.getMap.queryOptions())
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
178
|
+
|
|
179
|
+
// Non-paginated query: fetches all filtered data at once
|
|
180
|
+
const filteredQuery = useQuery({
|
|
181
|
+
...trpc.${trpcRoute.name}.${model.trpcRoute.getFiltered.name}.queryOptions({ filters: filters ?? {}, sort }),
|
|
182
|
+
enabled: !!isFiltered && !paginated,
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
// Paginated query: fetches data in pages for infinite scroll
|
|
186
|
+
// Each page contains { data: T[], total: number, cursor: number | null }
|
|
187
|
+
// Pages are accumulated and flattened into a single list
|
|
188
|
+
// Note: cursor is at top level for tRPC infinite query support - tRPC adds cursor automatically
|
|
189
|
+
const infiniteQuery = useInfiniteQuery({
|
|
190
|
+
...trpc.${trpcRoute.name}.${model.trpcRoute.getFilteredPaginated.name}.infiniteQueryOptions(
|
|
191
|
+
{ filters: filters ?? {}, sort, limit: pageSize },
|
|
192
|
+
{ getNextPageParam: (lastPage) => lastPage.cursor }
|
|
193
|
+
),
|
|
194
|
+
initialPageParam: 1,
|
|
195
|
+
enabled: !!isFiltered && paginated,
|
|
98
196
|
})
|
|
99
197
|
|
|
100
198
|
${itemsCrudHook.hooks}
|
|
@@ -103,26 +201,42 @@ export const ${itemsHook.name} = (filters?: ${model.types.filter.type.name}): ${
|
|
|
103
201
|
|
|
104
202
|
const ${itemsHook.list.name} = useMemo((): ${model.types.name}[] => Array.from(${itemsHook.map.name}.values()), [${itemsHook.map.name}])
|
|
105
203
|
|
|
204
|
+
// Build filtered list based on mode:
|
|
205
|
+
// - Not filtered: return full list
|
|
206
|
+
// - Paginated: flatten all loaded pages into single array
|
|
207
|
+
// - Non-paginated: return the single query result
|
|
106
208
|
const ${itemsHook.filteredList.name} = useMemo((): ${model.types.name}[] => {
|
|
107
|
-
if (isFiltered) {
|
|
108
|
-
return
|
|
209
|
+
if (!isFiltered) {
|
|
210
|
+
return ${itemsHook.list.name}
|
|
109
211
|
}
|
|
110
|
-
|
|
111
|
-
|
|
212
|
+
if (paginated) {
|
|
213
|
+
return infiniteQuery.data?.pages.flatMap(page => page.data) ?? []
|
|
214
|
+
}
|
|
215
|
+
return filteredQuery.data ?? []
|
|
216
|
+
}, [isFiltered, paginated, infiniteQuery.data, filteredQuery.data, ${itemsHook.list.name}])
|
|
112
217
|
|
|
113
218
|
const ${itemsHook.filteredMap.name} = useMemo((): Map<${model.types.id.name}, ${model.types.name}> => {
|
|
114
|
-
if (isFiltered) {
|
|
115
|
-
return
|
|
219
|
+
if (!isFiltered) {
|
|
220
|
+
return ${itemsHook.map.name}
|
|
116
221
|
}
|
|
117
|
-
return ${itemsHook.map.name}
|
|
222
|
+
return new Map(${itemsHook.filteredList.name}.map((i) => [i.${model.idField.name}, i]))
|
|
118
223
|
}, [isFiltered, ${itemsHook.filteredList.name}, ${itemsHook.map.name}])
|
|
119
224
|
|
|
225
|
+
// Total count: from paginated response, or just the list length
|
|
226
|
+
const total = paginated
|
|
227
|
+
? (infiniteQuery.data?.pages[0]?.total ?? ${itemsHook.list.name}.length)
|
|
228
|
+
: ${itemsHook.filteredList.name}.length
|
|
229
|
+
|
|
120
230
|
return {
|
|
121
231
|
${itemsHook.list.name},
|
|
122
232
|
${itemsHook.map.name},
|
|
123
233
|
${itemsHook.filteredList.name},
|
|
124
234
|
${itemsHook.filteredMap.name},
|
|
125
235
|
${itemsHook.isLoaded.name}: !mapQuery.isPending && !mapQuery.isError,
|
|
236
|
+
total,
|
|
237
|
+
hasNextPage: paginated ? (infiniteQuery.hasNextPage ?? false) : false,
|
|
238
|
+
fetchNextPage: paginated ? infiniteQuery.fetchNextPage : () => Promise.resolve(),
|
|
239
|
+
isFetchingNextPage: paginated ? infiniteQuery.isFetchingNextPage : false,
|
|
126
240
|
|
|
127
241
|
${itemsCrudHook.exports}
|
|
128
242
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-hook.generator.js","sourceRoot":"","sources":["../../../src/frontend-trpc-client/generators/model-hook.generator.ts"],"names":[],"mappings":";;AASA,
|
|
1
|
+
{"version":3,"file":"model-hook.generator.js","sourceRoot":"","sources":["../../../src/frontend-trpc-client/generators/model-hook.generator.ts"],"names":[],"mappings":"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"}
|