@hed-hog/category 0.0.361 → 0.0.364
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.
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
PageHeader,
|
|
7
7
|
PaginationFooter,
|
|
8
8
|
SearchBar,
|
|
9
|
-
StatsCards,
|
|
10
9
|
} from '@/components/entity-list';
|
|
11
10
|
import {
|
|
12
11
|
AlertDialog,
|
|
@@ -31,6 +30,8 @@ import {
|
|
|
31
30
|
FormMessage,
|
|
32
31
|
} from '@/components/ui/form';
|
|
33
32
|
import { Input } from '@/components/ui/input';
|
|
33
|
+
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
34
|
+
import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
|
|
34
35
|
import {
|
|
35
36
|
Select,
|
|
36
37
|
SelectContent,
|
|
@@ -38,7 +39,6 @@ import {
|
|
|
38
39
|
SelectTrigger,
|
|
39
40
|
SelectValue,
|
|
40
41
|
} from '@/components/ui/select';
|
|
41
|
-
import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
|
|
42
42
|
import {
|
|
43
43
|
Sheet,
|
|
44
44
|
SheetDescription,
|
|
@@ -46,6 +46,15 @@ import {
|
|
|
46
46
|
SheetHeader,
|
|
47
47
|
SheetTitle,
|
|
48
48
|
} from '@/components/ui/sheet';
|
|
49
|
+
import {
|
|
50
|
+
Table,
|
|
51
|
+
TableBody,
|
|
52
|
+
TableCell,
|
|
53
|
+
TableHead,
|
|
54
|
+
TableHeader,
|
|
55
|
+
TableRow,
|
|
56
|
+
} from '@/components/ui/table';
|
|
57
|
+
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
|
49
58
|
import { useDebounce } from '@/hooks/use-debounce';
|
|
50
59
|
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
51
60
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
@@ -56,6 +65,8 @@ import {
|
|
|
56
65
|
FolderTree,
|
|
57
66
|
Globe,
|
|
58
67
|
Layers,
|
|
68
|
+
LayoutGrid,
|
|
69
|
+
List,
|
|
59
70
|
Plus,
|
|
60
71
|
Save,
|
|
61
72
|
Tag,
|
|
@@ -105,6 +116,9 @@ type CategoryFormValues = {
|
|
|
105
116
|
status: 'active' | 'inactive';
|
|
106
117
|
};
|
|
107
118
|
|
|
119
|
+
const CATEGORY_VIEW_STORAGE_KEY = 'category-view-mode';
|
|
120
|
+
type CategoryViewMode = 'table' | 'cards';
|
|
121
|
+
|
|
108
122
|
export default function CategoryPage() {
|
|
109
123
|
const t = useTranslations('category.Category');
|
|
110
124
|
const [editingCategoryId, setEditingCategoryId] = useState<number | null>(
|
|
@@ -123,6 +137,7 @@ export default function CategoryPage() {
|
|
|
123
137
|
defaultValue: 10,
|
|
124
138
|
allowedValues: [10, 20, 30, 40, 50],
|
|
125
139
|
});
|
|
140
|
+
const [viewMode, setViewMode] = useState<CategoryViewMode>('table');
|
|
126
141
|
const [statusFilter, setStatusFilter] = useState<string>('all');
|
|
127
142
|
const [parentFilter, setParentFilter] = useState<string>('all');
|
|
128
143
|
const [selectedLocale, setSelectedLocale] = useState<string>('');
|
|
@@ -175,6 +190,17 @@ export default function CategoryPage() {
|
|
|
175
190
|
}
|
|
176
191
|
}, [currentLocaleCode]);
|
|
177
192
|
|
|
193
|
+
useEffect(() => {
|
|
194
|
+
try {
|
|
195
|
+
const saved = window.localStorage.getItem(CATEGORY_VIEW_STORAGE_KEY);
|
|
196
|
+
if (saved === 'table' || saved === 'cards') {
|
|
197
|
+
setViewMode(saved);
|
|
198
|
+
}
|
|
199
|
+
} catch {
|
|
200
|
+
// Ignore storage read failures.
|
|
201
|
+
}
|
|
202
|
+
}, []);
|
|
203
|
+
|
|
178
204
|
const { data: categoriesResult, refetch: refetchCategories } = useQuery<
|
|
179
205
|
PaginationResult<Category>
|
|
180
206
|
>({
|
|
@@ -275,7 +301,7 @@ export default function CategoryPage() {
|
|
|
275
301
|
const handleEditCategory = async (category: Category): Promise<void> => {
|
|
276
302
|
try {
|
|
277
303
|
const response = await request({
|
|
278
|
-
url: `/category/${category.
|
|
304
|
+
url: `/category/${category.id}`,
|
|
279
305
|
method: 'GET',
|
|
280
306
|
});
|
|
281
307
|
|
|
@@ -402,6 +428,19 @@ export default function CategoryPage() {
|
|
|
402
428
|
setPage(1);
|
|
403
429
|
};
|
|
404
430
|
|
|
431
|
+
const handleViewModeChange = (value: string): void => {
|
|
432
|
+
if (value !== 'table' && value !== 'cards') {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
setViewMode(value);
|
|
437
|
+
try {
|
|
438
|
+
window.localStorage.setItem(CATEGORY_VIEW_STORAGE_KEY, value);
|
|
439
|
+
} catch {
|
|
440
|
+
// Ignore storage write failures.
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
|
|
405
444
|
useEffect(() => {
|
|
406
445
|
if (!isEditDialogOpen || !selectedLocale) {
|
|
407
446
|
return;
|
|
@@ -422,35 +461,83 @@ export default function CategoryPage() {
|
|
|
422
461
|
|
|
423
462
|
const statsCards = [
|
|
424
463
|
{
|
|
464
|
+
key: 'total',
|
|
425
465
|
title: t('totalCategories'),
|
|
426
466
|
value: statsData?.total || 0,
|
|
427
|
-
icon:
|
|
428
|
-
|
|
429
|
-
|
|
467
|
+
icon: Layers,
|
|
468
|
+
accentClassName: 'from-sky-500/20 via-blue-400/10 to-transparent',
|
|
469
|
+
iconContainerClassName: 'bg-sky-50 text-sky-600',
|
|
430
470
|
},
|
|
431
471
|
{
|
|
472
|
+
key: 'active',
|
|
432
473
|
title: t('actives'),
|
|
433
474
|
value: statsData?.totalActive || 0,
|
|
434
|
-
icon:
|
|
435
|
-
|
|
436
|
-
|
|
475
|
+
icon: Tag,
|
|
476
|
+
accentClassName: 'from-emerald-500/20 via-green-400/10 to-transparent',
|
|
477
|
+
iconContainerClassName: 'bg-emerald-50 text-emerald-600',
|
|
437
478
|
},
|
|
438
479
|
{
|
|
480
|
+
key: 'inactive',
|
|
439
481
|
title: t('inactives'),
|
|
440
482
|
value: statsData?.totalInactive || 0,
|
|
441
|
-
icon:
|
|
442
|
-
|
|
443
|
-
|
|
483
|
+
icon: Tag,
|
|
484
|
+
accentClassName: 'from-amber-500/20 via-orange-400/10 to-transparent',
|
|
485
|
+
iconContainerClassName: 'bg-amber-50 text-amber-600',
|
|
444
486
|
},
|
|
445
487
|
{
|
|
488
|
+
key: 'root',
|
|
446
489
|
title: t('root'),
|
|
447
490
|
value: statsData?.totalRoot || 0,
|
|
448
|
-
icon:
|
|
449
|
-
|
|
450
|
-
|
|
491
|
+
icon: FolderTree,
|
|
492
|
+
accentClassName: 'from-violet-500/20 via-purple-400/10 to-transparent',
|
|
493
|
+
iconContainerClassName: 'bg-violet-50 text-violet-600',
|
|
451
494
|
},
|
|
452
495
|
];
|
|
453
496
|
|
|
497
|
+
const renderCategoryActions = (category: Category) => (
|
|
498
|
+
<div className="flex items-center justify-end gap-2">
|
|
499
|
+
<Button
|
|
500
|
+
variant="outline"
|
|
501
|
+
size="sm"
|
|
502
|
+
onClick={() => handleEditCategory(category)}
|
|
503
|
+
className="transition-colors hover:border-blue-200 hover:bg-blue-50 hover:text-blue-600 dark:hover:bg-blue-950"
|
|
504
|
+
>
|
|
505
|
+
<Edit className="mr-1 h-4 w-4" />
|
|
506
|
+
{t('edit')}
|
|
507
|
+
</Button>
|
|
508
|
+
|
|
509
|
+
<AlertDialog>
|
|
510
|
+
<AlertDialogTrigger asChild>
|
|
511
|
+
<Button
|
|
512
|
+
variant="outline"
|
|
513
|
+
size="sm"
|
|
514
|
+
className="bg-transparent transition-colors hover:border-red-200 hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-950"
|
|
515
|
+
>
|
|
516
|
+
<Trash2 className="mr-1 h-4 w-4" />
|
|
517
|
+
{t('delete')}
|
|
518
|
+
</Button>
|
|
519
|
+
</AlertDialogTrigger>
|
|
520
|
+
<AlertDialogContent>
|
|
521
|
+
<AlertDialogHeader>
|
|
522
|
+
<AlertDialogTitle>{t('confirmDelete')}</AlertDialogTitle>
|
|
523
|
+
<AlertDialogDescription>
|
|
524
|
+
{t('deleteDescription')}
|
|
525
|
+
</AlertDialogDescription>
|
|
526
|
+
</AlertDialogHeader>
|
|
527
|
+
<AlertDialogFooter>
|
|
528
|
+
<AlertDialogCancel>{t('cancel')}</AlertDialogCancel>
|
|
529
|
+
<AlertDialogAction
|
|
530
|
+
onClick={() => handleDeleteCategory(category.id)}
|
|
531
|
+
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
532
|
+
>
|
|
533
|
+
{t('delete')}
|
|
534
|
+
</AlertDialogAction>
|
|
535
|
+
</AlertDialogFooter>
|
|
536
|
+
</AlertDialogContent>
|
|
537
|
+
</AlertDialog>
|
|
538
|
+
</div>
|
|
539
|
+
);
|
|
540
|
+
|
|
454
541
|
return (
|
|
455
542
|
<Page>
|
|
456
543
|
<PageHeader
|
|
@@ -466,154 +553,229 @@ export default function CategoryPage() {
|
|
|
466
553
|
description={t('description')}
|
|
467
554
|
/>
|
|
468
555
|
|
|
469
|
-
<
|
|
556
|
+
<KpiCardsGrid items={statsCards} />
|
|
470
557
|
|
|
471
|
-
<
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
558
|
+
<div className="flex flex-col gap-4 xl:flex-row xl:items-center">
|
|
559
|
+
<div className="flex-1">
|
|
560
|
+
<SearchBar
|
|
561
|
+
searchQuery={searchTerm}
|
|
562
|
+
onSearchChange={handleSearchChange}
|
|
563
|
+
onSearch={() => setPage(1)}
|
|
564
|
+
placeholder={t('searchPlaceholder')}
|
|
565
|
+
controls={[
|
|
566
|
+
{
|
|
567
|
+
id: 'status-filter',
|
|
568
|
+
type: 'select',
|
|
569
|
+
value: statusFilter,
|
|
570
|
+
onChange: (value) => {
|
|
571
|
+
setStatusFilter(value);
|
|
572
|
+
setPage(1);
|
|
573
|
+
},
|
|
574
|
+
placeholder: t('status'),
|
|
575
|
+
options: [
|
|
576
|
+
{ value: 'all', label: t('all') },
|
|
577
|
+
{ value: 'active', label: t('actives') },
|
|
578
|
+
{ value: 'inactive', label: t('inactives') },
|
|
579
|
+
],
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
id: 'parent-filter',
|
|
583
|
+
type: 'select',
|
|
584
|
+
value: parentFilter,
|
|
585
|
+
onChange: (value) => {
|
|
586
|
+
setParentFilter(value);
|
|
587
|
+
setPage(1);
|
|
588
|
+
},
|
|
589
|
+
placeholder: t('hierarchy'),
|
|
590
|
+
options: [
|
|
591
|
+
{ value: 'all', label: t('allHierarchy') },
|
|
592
|
+
{ value: 'root', label: t('rootHierarchy') },
|
|
593
|
+
],
|
|
594
|
+
},
|
|
595
|
+
]}
|
|
596
|
+
/>
|
|
597
|
+
</div>
|
|
598
|
+
|
|
599
|
+
<div className="flex items-center gap-3 xl:justify-end">
|
|
600
|
+
<span className="text-xs font-medium text-muted-foreground">
|
|
601
|
+
{t('viewMode')}
|
|
602
|
+
</span>
|
|
603
|
+
<ToggleGroup
|
|
604
|
+
type="single"
|
|
605
|
+
value={viewMode}
|
|
606
|
+
onValueChange={handleViewModeChange}
|
|
607
|
+
variant="outline"
|
|
608
|
+
size="sm"
|
|
609
|
+
aria-label={t('viewMode')}
|
|
610
|
+
>
|
|
611
|
+
<ToggleGroupItem
|
|
612
|
+
value="table"
|
|
613
|
+
className="gap-1.5 px-2.5"
|
|
614
|
+
aria-label={t('viewModeTable')}
|
|
615
|
+
>
|
|
616
|
+
<List className="h-4 w-4" />
|
|
617
|
+
<span className="hidden sm:inline">{t('viewModeTable')}</span>
|
|
618
|
+
</ToggleGroupItem>
|
|
619
|
+
<ToggleGroupItem
|
|
620
|
+
value="cards"
|
|
621
|
+
className="gap-1.5 px-2.5"
|
|
622
|
+
aria-label={t('viewModeCards')}
|
|
623
|
+
>
|
|
624
|
+
<LayoutGrid className="h-4 w-4" />
|
|
625
|
+
<span className="hidden sm:inline">{t('viewModeCards')}</span>
|
|
626
|
+
</ToggleGroupItem>
|
|
627
|
+
</ToggleGroup>
|
|
628
|
+
</div>
|
|
629
|
+
</div>
|
|
502
630
|
|
|
503
631
|
<div className="space-y-4">
|
|
504
632
|
{categories.length > 0 ? (
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
<
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
<
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
633
|
+
viewMode === 'table' ? (
|
|
634
|
+
<div className="overflow-x-auto">
|
|
635
|
+
<Table>
|
|
636
|
+
<TableHeader>
|
|
637
|
+
<TableRow>
|
|
638
|
+
<TableHead>{t('name')}</TableHead>
|
|
639
|
+
<TableHead>{t('slug')}</TableHead>
|
|
640
|
+
<TableHead>{t('color')}</TableHead>
|
|
641
|
+
<TableHead>{t('icon')}</TableHead>
|
|
642
|
+
<TableHead>{t('status')}</TableHead>
|
|
643
|
+
<TableHead className="text-right">{t('edit')}</TableHead>
|
|
644
|
+
</TableRow>
|
|
645
|
+
</TableHeader>
|
|
646
|
+
<TableBody>
|
|
647
|
+
{categories.map((category) => (
|
|
648
|
+
<TableRow
|
|
649
|
+
key={category.id}
|
|
650
|
+
onDoubleClick={() => handleEditCategory(category)}
|
|
651
|
+
className="cursor-pointer"
|
|
652
|
+
>
|
|
653
|
+
<TableCell>
|
|
654
|
+
<div className="flex min-w-0 items-center gap-3">
|
|
655
|
+
<div
|
|
656
|
+
className="rounded-full p-2"
|
|
657
|
+
style={{
|
|
658
|
+
backgroundColor: category.color
|
|
659
|
+
? `${category.color}20`
|
|
660
|
+
: '#00000020',
|
|
661
|
+
}}
|
|
662
|
+
>
|
|
663
|
+
<div style={{ color: category.color || '#000000' }}>
|
|
664
|
+
{renderIcon(category.icon)}
|
|
665
|
+
</div>
|
|
666
|
+
</div>
|
|
667
|
+
<div className="min-w-0">
|
|
668
|
+
<div className="truncate font-medium">
|
|
669
|
+
{category.name}
|
|
670
|
+
</div>
|
|
671
|
+
<div className="text-xs text-muted-foreground">
|
|
672
|
+
{category.category_id
|
|
673
|
+
? t('hierarchy')
|
|
674
|
+
: t('root')}
|
|
675
|
+
</div>
|
|
676
|
+
</div>
|
|
525
677
|
</div>
|
|
526
|
-
</
|
|
527
|
-
<
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
678
|
+
</TableCell>
|
|
679
|
+
<TableCell className="font-mono text-sm">
|
|
680
|
+
{category.slug}
|
|
681
|
+
</TableCell>
|
|
682
|
+
<TableCell>
|
|
683
|
+
{category.color ? (
|
|
684
|
+
<div className="flex items-center gap-2">
|
|
685
|
+
<span
|
|
686
|
+
className="inline-block h-3.5 w-3.5 rounded border"
|
|
687
|
+
style={{ backgroundColor: category.color }}
|
|
688
|
+
/>
|
|
689
|
+
<span>{category.color}</span>
|
|
690
|
+
</div>
|
|
691
|
+
) : (
|
|
692
|
+
'—'
|
|
693
|
+
)}
|
|
694
|
+
</TableCell>
|
|
695
|
+
<TableCell>{category.icon || '—'}</TableCell>
|
|
696
|
+
<TableCell>{getStatusBadge(category.status)}</TableCell>
|
|
697
|
+
<TableCell className="text-right">
|
|
698
|
+
{renderCategoryActions(category)}
|
|
699
|
+
</TableCell>
|
|
700
|
+
</TableRow>
|
|
701
|
+
))}
|
|
702
|
+
</TableBody>
|
|
703
|
+
</Table>
|
|
704
|
+
</div>
|
|
705
|
+
) : (
|
|
706
|
+
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
|
707
|
+
{categories.map((category) => (
|
|
708
|
+
<Card
|
|
709
|
+
key={category.id}
|
|
710
|
+
onDoubleClick={() => handleEditCategory(category)}
|
|
711
|
+
className="cursor-pointer transition-all duration-200 hover:border-primary/20 hover:shadow-md"
|
|
712
|
+
>
|
|
713
|
+
<CardContent className="p-4">
|
|
714
|
+
<div className="flex items-start justify-between gap-3">
|
|
715
|
+
<div className="flex min-w-0 items-start gap-3">
|
|
716
|
+
<div
|
|
717
|
+
className="mt-0.5 rounded-full p-2"
|
|
718
|
+
style={{
|
|
719
|
+
backgroundColor: category.color
|
|
720
|
+
? `${category.color}20`
|
|
721
|
+
: '#00000020',
|
|
722
|
+
}}
|
|
723
|
+
>
|
|
724
|
+
<div style={{ color: category.color || '#000000' }}>
|
|
725
|
+
{renderIcon(category.icon)}
|
|
726
|
+
</div>
|
|
533
727
|
</div>
|
|
534
|
-
<div className="
|
|
535
|
-
<
|
|
536
|
-
<
|
|
537
|
-
{
|
|
538
|
-
</
|
|
539
|
-
{category.
|
|
540
|
-
</
|
|
541
|
-
|
|
542
|
-
<p className="flex items-center gap-2">
|
|
543
|
-
<span className="font-medium text-foreground">
|
|
544
|
-
{t('color')}:
|
|
545
|
-
</span>
|
|
546
|
-
<span
|
|
547
|
-
className="inline-block h-3.5 w-3.5 rounded border"
|
|
548
|
-
style={{
|
|
549
|
-
backgroundColor: category.color,
|
|
550
|
-
}}
|
|
551
|
-
/>
|
|
552
|
-
<span className="truncate">{category.color}</span>
|
|
553
|
-
</p>
|
|
554
|
-
)}
|
|
555
|
-
{category.icon && (
|
|
728
|
+
<div className="min-w-0 space-y-2">
|
|
729
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
730
|
+
<h3 className="truncate text-base font-semibold leading-tight">
|
|
731
|
+
{category.name}
|
|
732
|
+
</h3>
|
|
733
|
+
{getStatusBadge(category.status)}
|
|
734
|
+
</div>
|
|
735
|
+
<div className="space-y-1 text-xs text-muted-foreground">
|
|
556
736
|
<p className="truncate">
|
|
557
737
|
<span className="font-medium text-foreground">
|
|
558
|
-
{t('
|
|
738
|
+
{t('slug')}:
|
|
559
739
|
</span>{' '}
|
|
560
|
-
{category.
|
|
740
|
+
{category.slug}
|
|
561
741
|
</p>
|
|
562
|
-
|
|
742
|
+
{category.color && (
|
|
743
|
+
<p className="flex items-center gap-2">
|
|
744
|
+
<span className="font-medium text-foreground">
|
|
745
|
+
{t('color')}:
|
|
746
|
+
</span>
|
|
747
|
+
<span
|
|
748
|
+
className="inline-block h-3.5 w-3.5 rounded border"
|
|
749
|
+
style={{
|
|
750
|
+
backgroundColor: category.color,
|
|
751
|
+
}}
|
|
752
|
+
/>
|
|
753
|
+
<span className="truncate">
|
|
754
|
+
{category.color}
|
|
755
|
+
</span>
|
|
756
|
+
</p>
|
|
757
|
+
)}
|
|
758
|
+
{category.icon && (
|
|
759
|
+
<p className="truncate">
|
|
760
|
+
<span className="font-medium text-foreground">
|
|
761
|
+
{t('icon')}:
|
|
762
|
+
</span>{' '}
|
|
763
|
+
{category.icon}
|
|
764
|
+
</p>
|
|
765
|
+
)}
|
|
766
|
+
</div>
|
|
563
767
|
</div>
|
|
564
768
|
</div>
|
|
565
769
|
</div>
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
<Edit className="mr-1 h-4 w-4" />
|
|
576
|
-
{t('edit')}
|
|
577
|
-
</Button>
|
|
578
|
-
|
|
579
|
-
<AlertDialog>
|
|
580
|
-
<AlertDialogTrigger asChild>
|
|
581
|
-
<Button
|
|
582
|
-
variant="outline"
|
|
583
|
-
size="sm"
|
|
584
|
-
className="bg-transparent transition-colors hover:border-red-200 hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-950"
|
|
585
|
-
>
|
|
586
|
-
<Trash2 className="mr-1 h-4 w-4" />
|
|
587
|
-
{t('delete')}
|
|
588
|
-
</Button>
|
|
589
|
-
</AlertDialogTrigger>
|
|
590
|
-
<AlertDialogContent>
|
|
591
|
-
<AlertDialogHeader>
|
|
592
|
-
<AlertDialogTitle>
|
|
593
|
-
{t('confirmDelete')}
|
|
594
|
-
</AlertDialogTitle>
|
|
595
|
-
<AlertDialogDescription>
|
|
596
|
-
{t('deleteDescription')}
|
|
597
|
-
</AlertDialogDescription>
|
|
598
|
-
</AlertDialogHeader>
|
|
599
|
-
<AlertDialogFooter>
|
|
600
|
-
<AlertDialogCancel>{t('cancel')}</AlertDialogCancel>
|
|
601
|
-
<AlertDialogAction
|
|
602
|
-
onClick={() =>
|
|
603
|
-
handleDeleteCategory(Number(category.category_id))
|
|
604
|
-
}
|
|
605
|
-
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
606
|
-
>
|
|
607
|
-
{t('delete')}
|
|
608
|
-
</AlertDialogAction>
|
|
609
|
-
</AlertDialogFooter>
|
|
610
|
-
</AlertDialogContent>
|
|
611
|
-
</AlertDialog>
|
|
612
|
-
</div>
|
|
613
|
-
</CardContent>
|
|
614
|
-
</Card>
|
|
615
|
-
))}
|
|
616
|
-
</div>
|
|
770
|
+
|
|
771
|
+
<div className="mt-4">
|
|
772
|
+
{renderCategoryActions(category)}
|
|
773
|
+
</div>
|
|
774
|
+
</CardContent>
|
|
775
|
+
</Card>
|
|
776
|
+
))}
|
|
777
|
+
</div>
|
|
778
|
+
)
|
|
617
779
|
) : (
|
|
618
780
|
<EmptyState
|
|
619
781
|
icon={<Layers className="h-12 w-12" />}
|
|
@@ -818,10 +980,7 @@ export default function CategoryPage() {
|
|
|
818
980
|
<SelectItem value="none">{t('noneRoot')}</SelectItem>
|
|
819
981
|
{Array.isArray(rootCategories) &&
|
|
820
982
|
rootCategories.map((cat: Category) => (
|
|
821
|
-
<SelectItem
|
|
822
|
-
key={cat.id}
|
|
823
|
-
value={String(cat.category_id)}
|
|
824
|
-
>
|
|
983
|
+
<SelectItem key={cat.id} value={String(cat.id)}>
|
|
825
984
|
{cat.name}
|
|
826
985
|
</SelectItem>
|
|
827
986
|
))}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hed-hog/category",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.364",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"dependencies": {
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"@nestjs/core": "^11",
|
|
10
10
|
"@nestjs/jwt": "^11",
|
|
11
11
|
"@nestjs/mapped-types": "*",
|
|
12
|
-
"@hed-hog/core": "0.0.361",
|
|
13
12
|
"@hed-hog/api-locale": "0.0.14",
|
|
14
|
-
"@hed-hog/api-prisma": "0.0.6",
|
|
15
13
|
"@hed-hog/api": "0.0.8",
|
|
16
|
-
"@hed-hog/api-
|
|
14
|
+
"@hed-hog/api-prisma": "0.0.6",
|
|
15
|
+
"@hed-hog/api-pagination": "0.0.7",
|
|
16
|
+
"@hed-hog/core": "0.0.364"
|
|
17
17
|
},
|
|
18
18
|
"exports": {
|
|
19
19
|
".": {
|