@hed-hog/catalog 0.0.276 → 0.0.279
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/README.md +377 -0
- package/hedhog/frontend/app/[resource]/page.tsx.ejs +217 -265
- package/hedhog/frontend/app/_components/catalog-resource-form-sheet.tsx.ejs +964 -0
- package/hedhog/frontend/app/_lib/catalog-resources.tsx.ejs +736 -151
- package/hedhog/frontend/app/dashboard/page.tsx.ejs +5 -9
- package/package.json +6 -6
- package/hedhog/frontend/app/_components/catalog-nav.tsx.ejs +0 -85
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
|
|
3
|
-
import { CatalogNav } from '../_components/catalog-nav';
|
|
4
2
|
import {
|
|
5
3
|
catalogResourceMap,
|
|
6
4
|
type CatalogFieldDefinition,
|
|
7
5
|
type CatalogFilterOptionDefinition,
|
|
6
|
+
getCatalogLocalizedText,
|
|
8
7
|
getCatalogRecordLabel,
|
|
9
8
|
} from '../_lib/catalog-resources';
|
|
9
|
+
import { CatalogResourceFormSheet } from '../_components/catalog-resource-form-sheet';
|
|
10
10
|
import {
|
|
11
11
|
EntityCard,
|
|
12
12
|
Page,
|
|
@@ -25,24 +25,8 @@ import {
|
|
|
25
25
|
AlertDialogHeader,
|
|
26
26
|
AlertDialogTitle,
|
|
27
27
|
} from '@/components/ui/alert-dialog';
|
|
28
|
-
import { Badge } from '@/components/ui/badge';
|
|
29
28
|
import { Button } from '@/components/ui/button';
|
|
30
|
-
import {
|
|
31
|
-
Card,
|
|
32
|
-
CardContent,
|
|
33
|
-
CardDescription,
|
|
34
|
-
CardHeader,
|
|
35
|
-
CardTitle,
|
|
36
|
-
} from '@/components/ui/card';
|
|
37
|
-
import {
|
|
38
|
-
Dialog,
|
|
39
|
-
DialogContent,
|
|
40
|
-
DialogDescription,
|
|
41
|
-
DialogFooter,
|
|
42
|
-
DialogHeader,
|
|
43
|
-
DialogTitle,
|
|
44
|
-
} from '@/components/ui/dialog';
|
|
45
|
-
import { Label } from '@/components/ui/label';
|
|
29
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
46
30
|
import {
|
|
47
31
|
Table,
|
|
48
32
|
TableBody,
|
|
@@ -51,15 +35,13 @@ import {
|
|
|
51
35
|
TableHeader,
|
|
52
36
|
TableRow,
|
|
53
37
|
} from '@/components/ui/table';
|
|
54
|
-
import { Textarea } from '@/components/ui/textarea';
|
|
55
38
|
import { useDebounce } from '@/hooks/use-debounce';
|
|
56
|
-
import { cn } from '@/lib/utils';
|
|
57
39
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
58
|
-
import { Eye, Pencil, Plus,
|
|
40
|
+
import { Eye, Pencil, Plus, Search, Trash2 } from 'lucide-react';
|
|
59
41
|
import Link from 'next/link';
|
|
60
42
|
import { useParams } from 'next/navigation';
|
|
61
43
|
import { useTranslations } from 'next-intl';
|
|
62
|
-
import {
|
|
44
|
+
import { useMemo, useState } from 'react';
|
|
63
45
|
import { toast } from 'sonner';
|
|
64
46
|
|
|
65
47
|
type PaginationResult<T> = {
|
|
@@ -87,24 +69,15 @@ export default function CatalogResourcePage() {
|
|
|
87
69
|
const resource = params?.resource ?? '';
|
|
88
70
|
const config = catalogResourceMap.get(resource);
|
|
89
71
|
const t = useTranslations('catalog');
|
|
90
|
-
const { request, currentLocaleCode } = useApp();
|
|
72
|
+
const { accessToken, request, currentLocaleCode } = useApp();
|
|
91
73
|
const [search, setSearch] = useState('');
|
|
92
74
|
const debouncedSearch = useDebounce(search, 400);
|
|
93
75
|
const [filterValue, setFilterValue] = useState('all');
|
|
94
76
|
const [page, setPage] = useState(1);
|
|
95
77
|
const [pageSize, setPageSize] = useState(12);
|
|
96
|
-
const [
|
|
78
|
+
const [sheetOpen, setSheetOpen] = useState(false);
|
|
97
79
|
const [deleteId, setDeleteId] = useState<number | null>(null);
|
|
98
|
-
const [
|
|
99
|
-
null
|
|
100
|
-
);
|
|
101
|
-
const [payload, setPayload] = useState('{}');
|
|
102
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
103
|
-
|
|
104
|
-
const initialPayload = useMemo(
|
|
105
|
-
() => JSON.stringify(config?.template ?? {}, null, 2),
|
|
106
|
-
[config]
|
|
107
|
-
);
|
|
80
|
+
const [editingRecordId, setEditingRecordId] = useState<number | null>(null);
|
|
108
81
|
const resourceTitle = config
|
|
109
82
|
? t(`resources.${config.translationKey}.title`)
|
|
110
83
|
: t('unsupportedResource');
|
|
@@ -122,17 +95,34 @@ export default function CatalogResourcePage() {
|
|
|
122
95
|
}),
|
|
123
96
|
[currentLocaleCode]
|
|
124
97
|
);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
98
|
+
const isAppReady = accessToken.trim().length > 0;
|
|
99
|
+
const translatableValueFields = useMemo(
|
|
100
|
+
() =>
|
|
101
|
+
new Set([
|
|
102
|
+
'status',
|
|
103
|
+
'comparison_status',
|
|
104
|
+
'availability_status',
|
|
105
|
+
'site_type',
|
|
106
|
+
'merchant_type',
|
|
107
|
+
'network_type',
|
|
108
|
+
'commission_type',
|
|
109
|
+
'page_type',
|
|
110
|
+
'canonical_strategy',
|
|
111
|
+
'source_type',
|
|
112
|
+
'data_type',
|
|
113
|
+
'comparison_mode',
|
|
114
|
+
'facet_mode',
|
|
115
|
+
]),
|
|
116
|
+
[]
|
|
117
|
+
);
|
|
133
118
|
|
|
134
119
|
const { data: stats } = useQuery<ResourceStats>({
|
|
135
|
-
queryKey: [
|
|
120
|
+
queryKey: [
|
|
121
|
+
'catalog-resource-stats',
|
|
122
|
+
resource,
|
|
123
|
+
accessToken,
|
|
124
|
+
currentLocaleCode,
|
|
125
|
+
],
|
|
136
126
|
queryFn: async () => {
|
|
137
127
|
if (!resource || !config) {
|
|
138
128
|
return { total: 0 };
|
|
@@ -152,17 +142,19 @@ export default function CatalogResourcePage() {
|
|
|
152
142
|
statsData.active !== undefined ? Number(statsData.active) : undefined,
|
|
153
143
|
};
|
|
154
144
|
},
|
|
155
|
-
|
|
145
|
+
enabled: Boolean(resource && config && isAppReady),
|
|
156
146
|
});
|
|
157
|
-
const resourceStats = stats as ResourceStats;
|
|
147
|
+
const resourceStats = (stats ?? { total: 0 }) as ResourceStats;
|
|
158
148
|
|
|
159
149
|
const { data, refetch } = useQuery<PaginationResult<CatalogRecord>>({
|
|
160
150
|
queryKey: [
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
151
|
+
'catalog-resource',
|
|
152
|
+
resource,
|
|
153
|
+
accessToken,
|
|
154
|
+
currentLocaleCode,
|
|
155
|
+
debouncedSearch,
|
|
156
|
+
filterValue,
|
|
157
|
+
page,
|
|
166
158
|
pageSize,
|
|
167
159
|
],
|
|
168
160
|
queryFn: async () => {
|
|
@@ -199,16 +191,34 @@ export default function CatalogResourcePage() {
|
|
|
199
191
|
pageSize: Number(responseData.pageSize ?? pageSize),
|
|
200
192
|
};
|
|
201
193
|
},
|
|
202
|
-
|
|
194
|
+
enabled: Boolean(resource && config && isAppReady),
|
|
203
195
|
});
|
|
204
|
-
const resourceData = data
|
|
196
|
+
const resourceData = (data ?? {
|
|
197
|
+
data: [],
|
|
198
|
+
total: 0,
|
|
199
|
+
page: 1,
|
|
200
|
+
pageSize: 12,
|
|
201
|
+
}) as PaginationResult<CatalogRecord>;
|
|
205
202
|
|
|
206
203
|
const safeTranslate = (key: string, fallback: string) => {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
204
|
+
const translated = t(key);
|
|
205
|
+
|
|
206
|
+
if (translated === key) {
|
|
210
207
|
return fallback;
|
|
211
208
|
}
|
|
209
|
+
|
|
210
|
+
return translated;
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const resolveDynamicValueLabel = (fieldKey: string, value: string) => {
|
|
214
|
+
if (!translatableValueFields.has(fieldKey)) {
|
|
215
|
+
return value;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return safeTranslate(
|
|
219
|
+
`resource.valueLabels.${fieldKey}.${value}`,
|
|
220
|
+
humanizeValue(value)
|
|
221
|
+
);
|
|
212
222
|
};
|
|
213
223
|
|
|
214
224
|
const getFirstValue = (record: CatalogRecord, keys: string[]) => {
|
|
@@ -246,10 +256,7 @@ export default function CatalogResourcePage() {
|
|
|
246
256
|
}
|
|
247
257
|
|
|
248
258
|
if (typeof value === 'string') {
|
|
249
|
-
return
|
|
250
|
-
`resource.valueLabels.${field?.key}.${value}`,
|
|
251
|
-
humanizeValue(value)
|
|
252
|
-
);
|
|
259
|
+
return field?.key ? resolveDynamicValueLabel(field.key, value) : value;
|
|
253
260
|
}
|
|
254
261
|
|
|
255
262
|
return String(value);
|
|
@@ -329,7 +336,6 @@ export default function CatalogResourcePage() {
|
|
|
329
336
|
{ label: t('resource.notFound') },
|
|
330
337
|
]}
|
|
331
338
|
/>
|
|
332
|
-
<CatalogNav currentHref="/catalog/dashboard" />
|
|
333
339
|
<Card>
|
|
334
340
|
<CardHeader>
|
|
335
341
|
<CardTitle>{t('unsupportedResource')}</CardTitle>
|
|
@@ -348,46 +354,13 @@ export default function CatalogResourcePage() {
|
|
|
348
354
|
}
|
|
349
355
|
|
|
350
356
|
const openCreate = () => {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
setDialogOpen(true);
|
|
357
|
+
setEditingRecordId(null);
|
|
358
|
+
setSheetOpen(true);
|
|
354
359
|
};
|
|
355
360
|
|
|
356
361
|
const openEdit = (record: CatalogRecord) => {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
setDialogOpen(true);
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
const submit = async () => {
|
|
363
|
-
try {
|
|
364
|
-
setIsSubmitting(true);
|
|
365
|
-
const body = JSON.parse(payload);
|
|
366
|
-
|
|
367
|
-
if (editingRecord?.id) {
|
|
368
|
-
await request({
|
|
369
|
-
url: `/catalog/${resource}/${editingRecord.id}`,
|
|
370
|
-
method: 'PATCH',
|
|
371
|
-
data: body,
|
|
372
|
-
});
|
|
373
|
-
} else {
|
|
374
|
-
await request({
|
|
375
|
-
url: `/catalog/${resource}`,
|
|
376
|
-
method: 'POST',
|
|
377
|
-
data: body,
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
toast.success(t('toasts.saveSuccess', { resource: resourceTitle }));
|
|
382
|
-
setDialogOpen(false);
|
|
383
|
-
await refetch();
|
|
384
|
-
} catch (error) {
|
|
385
|
-
toast.error(
|
|
386
|
-
error instanceof Error ? error.message : t('toasts.saveError')
|
|
387
|
-
);
|
|
388
|
-
} finally {
|
|
389
|
-
setIsSubmitting(false);
|
|
390
|
-
}
|
|
362
|
+
setEditingRecordId(Number(record.id));
|
|
363
|
+
setSheetOpen(true);
|
|
391
364
|
};
|
|
392
365
|
|
|
393
366
|
const handleDelete = async () => {
|
|
@@ -473,191 +446,170 @@ export default function CatalogResourcePage() {
|
|
|
473
446
|
]}
|
|
474
447
|
actions={[
|
|
475
448
|
{
|
|
476
|
-
label:
|
|
449
|
+
label: getCatalogLocalizedText(
|
|
450
|
+
config.createActionLabel,
|
|
451
|
+
currentLocaleCode
|
|
452
|
+
),
|
|
477
453
|
onClick: () => {
|
|
478
|
-
|
|
454
|
+
openCreate();
|
|
479
455
|
},
|
|
480
|
-
|
|
481
|
-
icon: <RefreshCcw className="size-4" />,
|
|
456
|
+
icon: <Plus className="size-4" />,
|
|
482
457
|
},
|
|
483
458
|
]}
|
|
484
459
|
/>
|
|
485
460
|
|
|
486
461
|
<div className="min-w-0 space-y-4 overflow-x-hidden">
|
|
487
|
-
<CatalogNav currentHref={config.href} />
|
|
488
|
-
|
|
489
462
|
<StatsCards stats={statsCards} className="sm:grid-cols-3" />
|
|
490
463
|
|
|
491
|
-
<
|
|
492
|
-
<
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
{t('newRecord')}
|
|
519
|
-
</Button>
|
|
464
|
+
<div className="min-w-0 space-y-4">
|
|
465
|
+
<SearchBar
|
|
466
|
+
searchQuery={search}
|
|
467
|
+
onSearchChange={(value) => {
|
|
468
|
+
setSearch(value);
|
|
469
|
+
setPage(1);
|
|
470
|
+
}}
|
|
471
|
+
onSearch={() => {
|
|
472
|
+
setPage(1);
|
|
473
|
+
void refetch();
|
|
474
|
+
}}
|
|
475
|
+
placeholder={t('searchPlaceholder')}
|
|
476
|
+
className="min-w-0"
|
|
477
|
+
filters={{
|
|
478
|
+
value: filterValue,
|
|
479
|
+
options: filterOptions,
|
|
480
|
+
onChange: (value) => {
|
|
481
|
+
setFilterValue(value);
|
|
482
|
+
setPage(1);
|
|
483
|
+
},
|
|
484
|
+
placeholder: t(`resource.filters.${config.primaryFilterField}`),
|
|
485
|
+
}}
|
|
486
|
+
/>
|
|
487
|
+
|
|
488
|
+
{resourceData.data.length === 0 ? (
|
|
489
|
+
<div className="rounded-lg border border-dashed px-6 py-12 text-center text-sm text-muted-foreground">
|
|
490
|
+
{t('resource.emptyState')}
|
|
520
491
|
</div>
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
<div className="
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
492
|
+
) : config.listVariant === 'table' ? (
|
|
493
|
+
<div className="min-w-0 space-y-4">
|
|
494
|
+
<div className="overflow-x-auto rounded-md border">
|
|
495
|
+
<Table>
|
|
496
|
+
<TableHeader>
|
|
497
|
+
<TableRow>
|
|
498
|
+
{config.tableColumns?.map((column) => (
|
|
499
|
+
<TableHead key={column.key}>
|
|
500
|
+
{t(`resource.fields.${column.labelKey}`)}
|
|
501
|
+
</TableHead>
|
|
502
|
+
))}
|
|
503
|
+
<TableHead className="w-[110px] text-right">
|
|
504
|
+
{t('resource.fields.actions')}
|
|
505
|
+
</TableHead>
|
|
506
|
+
</TableRow>
|
|
507
|
+
</TableHeader>
|
|
508
|
+
<TableBody>
|
|
509
|
+
{resourceData.data.map((record) => (
|
|
510
|
+
<TableRow key={String(record.id)}>
|
|
532
511
|
{config.tableColumns?.map((column) => (
|
|
533
|
-
<
|
|
534
|
-
{
|
|
535
|
-
</
|
|
512
|
+
<TableCell key={`${record.id}-${column.key}`}>
|
|
513
|
+
{formatFieldValue(record[column.key], column)}
|
|
514
|
+
</TableCell>
|
|
536
515
|
))}
|
|
537
|
-
<
|
|
538
|
-
|
|
539
|
-
|
|
516
|
+
<TableCell className="text-right">
|
|
517
|
+
<div className="flex justify-end gap-2">
|
|
518
|
+
<Button
|
|
519
|
+
variant="outline"
|
|
520
|
+
size="icon"
|
|
521
|
+
onClick={() => openEdit(record)}
|
|
522
|
+
>
|
|
523
|
+
<Pencil className="size-4" />
|
|
524
|
+
</Button>
|
|
525
|
+
<Button
|
|
526
|
+
variant="destructive"
|
|
527
|
+
size="icon"
|
|
528
|
+
onClick={() => setDeleteId(Number(record.id))}
|
|
529
|
+
>
|
|
530
|
+
<Trash2 className="size-4" />
|
|
531
|
+
</Button>
|
|
532
|
+
</div>
|
|
533
|
+
</TableCell>
|
|
540
534
|
</TableRow>
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
<TableRow key={String(record.id)}>
|
|
545
|
-
{config.tableColumns?.map((column) => (
|
|
546
|
-
<TableCell key={`${record.id}-${column.key}`}>
|
|
547
|
-
{formatFieldValue(record[column.key], column)}
|
|
548
|
-
</TableCell>
|
|
549
|
-
))}
|
|
550
|
-
<TableCell className="text-right">
|
|
551
|
-
<div className="flex justify-end gap-2">
|
|
552
|
-
<Button
|
|
553
|
-
variant="outline"
|
|
554
|
-
size="icon"
|
|
555
|
-
onClick={() => openEdit(record)}
|
|
556
|
-
>
|
|
557
|
-
<Pencil className="size-4" />
|
|
558
|
-
</Button>
|
|
559
|
-
<Button
|
|
560
|
-
variant="destructive"
|
|
561
|
-
size="icon"
|
|
562
|
-
onClick={() => setDeleteId(Number(record.id))}
|
|
563
|
-
>
|
|
564
|
-
<Trash2 className="size-4" />
|
|
565
|
-
</Button>
|
|
566
|
-
</div>
|
|
567
|
-
</TableCell>
|
|
568
|
-
</TableRow>
|
|
569
|
-
))}
|
|
570
|
-
</TableBody>
|
|
571
|
-
</Table>
|
|
572
|
-
</div>
|
|
573
|
-
|
|
574
|
-
<PaginationFooter
|
|
575
|
-
currentPage={page}
|
|
576
|
-
pageSize={pageSize}
|
|
577
|
-
totalItems={resourceData.total}
|
|
578
|
-
onPageChange={setPage}
|
|
579
|
-
onPageSizeChange={(nextPageSize) => {
|
|
580
|
-
setPageSize(nextPageSize);
|
|
581
|
-
setPage(1);
|
|
582
|
-
}}
|
|
583
|
-
/>
|
|
535
|
+
))}
|
|
536
|
+
</TableBody>
|
|
537
|
+
</Table>
|
|
584
538
|
</div>
|
|
585
|
-
|
|
586
|
-
<
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
539
|
+
|
|
540
|
+
<PaginationFooter
|
|
541
|
+
currentPage={page}
|
|
542
|
+
pageSize={pageSize}
|
|
543
|
+
totalItems={resourceData.total}
|
|
544
|
+
onPageChange={setPage}
|
|
545
|
+
onPageSizeChange={(nextPageSize) => {
|
|
546
|
+
setPageSize(nextPageSize);
|
|
547
|
+
setPage(1);
|
|
548
|
+
}}
|
|
549
|
+
/>
|
|
550
|
+
</div>
|
|
551
|
+
) : (
|
|
552
|
+
<div className="min-w-0 space-y-4">
|
|
553
|
+
<div className="grid min-w-0 gap-4 md:grid-cols-2 2xl:grid-cols-3">
|
|
554
|
+
{resourceData.data.map((record) => (
|
|
555
|
+
<EntityCard
|
|
556
|
+
key={String(record.id)}
|
|
557
|
+
title={String(
|
|
558
|
+
getFirstValue(record, config.titleFields) ??
|
|
559
|
+
getCatalogRecordLabel(record)
|
|
560
|
+
)}
|
|
561
|
+
description={getCardDescription(record)}
|
|
562
|
+
badges={getBadges(record)}
|
|
563
|
+
metadata={getCardMetadata(record)}
|
|
564
|
+
actions={[
|
|
565
|
+
{
|
|
566
|
+
label: t('edit'),
|
|
567
|
+
onClick: () => openEdit(record),
|
|
568
|
+
variant: 'outline',
|
|
569
|
+
icon: <Pencil className="size-4" />,
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
label: t('delete'),
|
|
573
|
+
onClick: () => setDeleteId(Number(record.id)),
|
|
574
|
+
variant: 'destructive',
|
|
575
|
+
icon: <Trash2 className="size-4" />,
|
|
576
|
+
},
|
|
577
|
+
]}
|
|
578
|
+
/>
|
|
579
|
+
))}
|
|
626
580
|
</div>
|
|
627
|
-
)}
|
|
628
|
-
</CardContent>
|
|
629
|
-
</Card>
|
|
630
581
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
{
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
<Label htmlFor="payload">{t('payload')}</Label>
|
|
641
|
-
<Textarea
|
|
642
|
-
id="payload"
|
|
643
|
-
value={payload}
|
|
644
|
-
onChange={(event) => setPayload(event.target.value)}
|
|
645
|
-
className="min-h-[360px] font-mono text-xs"
|
|
582
|
+
<PaginationFooter
|
|
583
|
+
currentPage={page}
|
|
584
|
+
pageSize={pageSize}
|
|
585
|
+
totalItems={resourceData.total}
|
|
586
|
+
onPageChange={setPage}
|
|
587
|
+
onPageSizeChange={(nextPageSize) => {
|
|
588
|
+
setPageSize(nextPageSize);
|
|
589
|
+
setPage(1);
|
|
590
|
+
}}
|
|
646
591
|
/>
|
|
647
|
-
<p className="text-sm text-muted-foreground">
|
|
648
|
-
{t('resource.payloadHintDescription')}
|
|
649
|
-
</p>
|
|
650
592
|
</div>
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
593
|
+
)}
|
|
594
|
+
</div>
|
|
595
|
+
<CatalogResourceFormSheet
|
|
596
|
+
open={sheetOpen}
|
|
597
|
+
onOpenChange={(open) => {
|
|
598
|
+
setSheetOpen(open);
|
|
599
|
+
if (!open) {
|
|
600
|
+
setEditingRecordId(null);
|
|
601
|
+
}
|
|
602
|
+
}}
|
|
603
|
+
resource={resource}
|
|
604
|
+
resourceConfig={config}
|
|
605
|
+
resourceTitle={resourceTitle}
|
|
606
|
+
resourceDescription={resourceDescription}
|
|
607
|
+
recordId={editingRecordId}
|
|
608
|
+
onSuccess={async () => {
|
|
609
|
+
setEditingRecordId(null);
|
|
610
|
+
await refetch();
|
|
611
|
+
}}
|
|
612
|
+
/>
|
|
661
613
|
|
|
662
614
|
<AlertDialog
|
|
663
615
|
open={deleteId !== null}
|