@svadmin/ui 0.38.2 → 0.38.7
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/app.css +8 -4
- package/dist/app.theme.css +8 -4
- package/dist/components/AICommandBar.svelte +16 -15
- package/dist/components/AboutSettings.svelte +12 -11
- package/dist/components/AdminApp.svelte +144 -70
- package/dist/components/AdminApp.svelte.d.ts +6 -3
- package/dist/components/ApiSettings.svelte +19 -16
- package/dist/components/AppearanceSettings.svelte +18 -16
- package/dist/components/AuditLogDrawer.svelte +6 -3
- package/dist/components/AuditLogViewer.svelte +18 -15
- package/dist/components/Authenticated.svelte +4 -4
- package/dist/components/AutoForm.svelte +19 -16
- package/dist/components/AutoSaveIndicator.svelte +7 -5
- package/dist/components/AutoTable.svelte +185 -159
- package/dist/components/AutoTable.svelte.d.ts +1 -1
- package/dist/components/Breadcrumbs.svelte +17 -13
- package/dist/components/CancelTaskButton.svelte +5 -2
- package/dist/components/CatchAllNavigate.svelte +3 -3
- package/dist/components/ChatDialog.svelte +22 -19
- package/dist/components/ComboboxField.svelte +6 -4
- package/dist/components/CommandPalette.svelte +20 -20
- package/dist/components/ConfigErrorScreen.svelte +12 -10
- package/dist/components/ConfirmDialog.svelte +6 -4
- package/dist/components/CreatePage.svelte +4 -2
- package/dist/components/DevTools.svelte +11 -14
- package/dist/components/DrawerForm.svelte +5 -2
- package/dist/components/EditPage.svelte +8 -5
- package/dist/components/EmptyState.svelte +5 -3
- package/dist/components/ErrorBoundary.svelte +5 -3
- package/dist/components/ErrorComponent.svelte +6 -4
- package/dist/components/ErrorPage.svelte +12 -10
- package/dist/components/FieldRenderer.svelte +17 -15
- package/dist/components/ForgotPasswordPage.svelte +22 -19
- package/dist/components/Header.svelte +7 -4
- package/dist/components/InfiniteList.svelte +6 -3
- package/dist/components/IntegrationsSettings.svelte +9 -6
- package/dist/components/KeyboardShortcuts.svelte +4 -2
- package/dist/components/Layout.svelte +12 -13
- package/dist/components/LazyPage.svelte +47 -0
- package/dist/components/LazyPage.svelte.d.ts +14 -0
- package/dist/components/ListPage.svelte +8 -5
- package/dist/components/LoginPage.svelte +24 -22
- package/dist/components/MarkdownRenderer.svelte +89 -35
- package/dist/components/ModalForm.svelte +4 -2
- package/dist/components/NavigateToResource.svelte +3 -3
- package/dist/components/NotificationsSettings.svelte +26 -24
- package/dist/components/PasswordInput.svelte +4 -2
- package/dist/components/PermissionMatrix.svelte +15 -12
- package/dist/components/ProfilePage.svelte +23 -21
- package/dist/components/RegisterPage.svelte +21 -19
- package/dist/components/ResourceOperationsPage.svelte +3 -2
- package/dist/components/RetryTaskButton.svelte +5 -2
- package/dist/components/RolesSettings.svelte +10 -8
- package/dist/components/SecuritySettings.svelte +26 -23
- package/dist/components/SettingsPage.svelte +14 -11
- package/dist/components/ShowPage.svelte +9 -7
- package/dist/components/Sidebar.svelte +36 -28
- package/dist/components/SidebarItem.svelte +4 -4
- package/dist/components/SidebarItem.svelte.d.ts +1 -1
- package/dist/components/StepsForm.svelte +11 -8
- package/dist/components/TaskDetails.svelte +14 -11
- package/dist/components/TaskList.svelte +14 -11
- package/dist/components/TaskQueueDrawer.svelte +34 -32
- package/dist/components/TaskStatusBadge.svelte +5 -2
- package/dist/components/Toast.svelte +18 -4
- package/dist/components/TooltipButton.svelte +25 -3
- package/dist/components/TwoFactorAuthPage.svelte +26 -24
- package/dist/components/UndoableNotification.svelte +5 -2
- package/dist/components/UpdatePasswordPage.svelte +18 -15
- package/dist/components/VirtualTable.svelte +0 -4
- package/dist/components/account/CompanyProfilePage.svelte +13 -11
- package/dist/components/account/GetStartedPage.svelte +15 -13
- package/dist/components/account/ImportMembersPage.svelte +14 -12
- package/dist/components/account/MembersStarterPage.svelte +10 -8
- package/dist/components/account/SecurityLogPage.svelte +24 -22
- package/dist/components/account/SettingsEnterprisePage.svelte +6 -4
- package/dist/components/account/SettingsPlainPage.svelte +14 -12
- package/dist/components/account/SettingsSidebarPage.svelte +11 -9
- package/dist/components/account/TeamMembersPage.svelte +10 -8
- package/dist/components/buttons/CloneButton.svelte +4 -2
- package/dist/components/buttons/CreateButton.svelte +4 -2
- package/dist/components/buttons/DeleteButton.svelte +6 -4
- package/dist/components/buttons/EditButton.svelte +4 -2
- package/dist/components/buttons/ExportButton.svelte +4 -2
- package/dist/components/buttons/ImportButton.svelte +4 -2
- package/dist/components/buttons/RefreshButton.svelte +4 -2
- package/dist/components/buttons/SaveButton.svelte +4 -2
- package/dist/components/buttons/ShowButton.svelte +4 -2
- package/dist/components/fieldComponentMap.js +0 -3
- package/dist/components/fields/ArrayField.svelte +6 -4
- package/dist/components/fields/JsonField.svelte +5 -3
- package/dist/components/fields/RelationField.svelte +3 -3
- package/dist/components/fields/RichTextField.svelte +4 -2
- package/dist/components/network/TeamCrewTablePage.svelte +18 -16
- package/dist/components/network/UserCardsNFTPage.svelte +11 -9
- package/dist/components/profile/ActivityTimeline.svelte +10 -8
- package/dist/components/profile/ProfileCard.svelte +13 -11
- package/dist/components/profile/ProjectsGrid.svelte +7 -5
- package/dist/components/profile/PublicProfilePage.svelte +18 -16
- package/dist/components/profile/TeamsShowcase.svelte +7 -5
- package/dist/components/toast-host.svelte.d.ts +5 -0
- package/dist/components/toast-host.svelte.js +21 -0
- package/dist/components/ui/command/command-dialog.svelte +29 -13
- package/dist/components/ui/command/command-dialog.svelte.d.ts +4 -3
- package/dist/components/ui/command/command-empty.svelte +18 -0
- package/dist/components/ui/command/command-empty.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-group.svelte +41 -0
- package/dist/components/ui/command/command-group.svelte.d.ts +8 -0
- package/dist/components/ui/command/command-input.svelte +23 -0
- package/dist/components/ui/command/command-input.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-item.svelte +43 -0
- package/dist/components/ui/command/command-item.svelte.d.ts +11 -0
- package/dist/components/ui/command/command-list.svelte +18 -0
- package/dist/components/ui/command/command-list.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-loading.svelte +12 -0
- package/dist/components/ui/command/command-loading.svelte.d.ts +4 -0
- package/dist/components/ui/command/command-separator.svelte +23 -0
- package/dist/components/ui/command/command-separator.svelte.d.ts +7 -0
- package/dist/components/ui/command/command.svelte +26 -0
- package/dist/components/ui/command/command.svelte.d.ts +4 -0
- package/dist/components/ui/command/index.d.ts +55 -2
- package/dist/components/ui/command/index.js +21 -2
- package/dist/components/ui/sheet/sheet-content.svelte +1 -4
- package/dist/components/ui/sheet/sheet-content.svelte.d.ts +1 -3
- package/dist/components/ui/tabs/tabs-content.svelte +6 -2
- package/dist/components/ui/tabs/tabs-context.svelte.d.ts +5 -0
- package/dist/components/ui/tabs/tabs-context.svelte.js +2 -0
- package/dist/components/ui/tabs/tabs-trigger.svelte +49 -4
- package/dist/components/ui/tabs/tabs.svelte +12 -1
- package/dist/components/ui/tooltip/index.d.ts +4 -1
- package/dist/components/ui/tooltip/index.js +3 -1
- package/dist/components/ui/tooltip/tooltip-content.svelte +308 -35
- package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +42 -3
- package/dist/components/ui/tooltip/tooltip-context.svelte.d.ts +37 -0
- package/dist/components/ui/tooltip/tooltip-context.svelte.js +220 -0
- package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.d.ts +11 -0
- package/dist/components/ui/tooltip/tooltip-layer-registry.svelte.js +93 -0
- package/dist/components/ui/tooltip/tooltip-trigger.svelte +56 -3
- package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +23 -2
- package/dist/components/ui/tooltip/tooltip.svelte +63 -3
- package/dist/components/ui/tooltip/tooltip.svelte.d.ts +23 -2
- package/dist/router-state.svelte.d.ts +13 -0
- package/dist/router-state.svelte.js +72 -37
- package/package.json +14 -11
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { untrack } from 'svelte';
|
|
2
|
+
import { onDestroy, untrack } from 'svelte';
|
|
3
3
|
import { cn } from '../utils.js';
|
|
4
4
|
import {
|
|
5
5
|
createTable,
|
|
@@ -10,33 +10,38 @@
|
|
|
10
10
|
type ColumnVisibilityState,
|
|
11
11
|
type ExpandedState,
|
|
12
12
|
} from '@tanstack/svelte-table';
|
|
13
|
-
import type {
|
|
13
|
+
import type { Column, Header, TableFeatures } from '@tanstack/table-core';
|
|
14
|
+
import { createAtom, useSelector } from '@tanstack/svelte-store';
|
|
14
15
|
import {
|
|
15
16
|
column_getCanSort,
|
|
16
17
|
column_getIsSorted,
|
|
17
18
|
column_toggleSorting,
|
|
18
|
-
column_getIsVisible,
|
|
19
19
|
column_toggleVisibility,
|
|
20
20
|
header_getSize,
|
|
21
21
|
table_getRowModel,
|
|
22
|
-
table_getHeaderGroups,
|
|
23
22
|
table_getAllLeafColumns,
|
|
24
23
|
table_getIsAllRowsSelected,
|
|
25
24
|
table_toggleAllRowsSelected,
|
|
26
25
|
row_getIsSelected,
|
|
27
26
|
row_toggleSelected,
|
|
28
27
|
row_getVisibleCells,
|
|
29
|
-
row_getIsExpanded,
|
|
30
28
|
row_toggleExpanded,
|
|
31
29
|
cell_getValue,
|
|
32
30
|
} from '@tanstack/table-core/static-functions';
|
|
33
31
|
|
|
34
|
-
import { useList, useDelete, useDeleteMany, getResource, notify } from '@svadmin/core';
|
|
35
|
-
import type {
|
|
36
|
-
|
|
32
|
+
import { captureAdminContext, useList, useDelete, useDeleteMany, getResource, notify } from '@svadmin/core';
|
|
33
|
+
import type {
|
|
34
|
+
BaseRecord,
|
|
35
|
+
FieldDefinition,
|
|
36
|
+
Filter,
|
|
37
|
+
LogicalFilter,
|
|
38
|
+
Pagination as PaginationState,
|
|
39
|
+
Sort,
|
|
40
|
+
} from '@svadmin/core';
|
|
37
41
|
import { useCan, getAccessControlProvider } from '@svadmin/core';
|
|
38
42
|
import { readURLState, writeURLState } from '@svadmin/core';
|
|
39
|
-
import {
|
|
43
|
+
import { useTranslation } from '@svadmin/core/i18n';
|
|
44
|
+
|
|
40
45
|
import { fade } from 'svelte/transition';
|
|
41
46
|
import { Button } from './ui/button/index.js';
|
|
42
47
|
import { Input } from './ui/input/index.js';
|
|
@@ -50,7 +55,7 @@
|
|
|
50
55
|
import * as ContextMenu from './ui/context-menu/index.js';
|
|
51
56
|
import {
|
|
52
57
|
Plus, Pencil, Trash2,
|
|
53
|
-
Search, Download,
|
|
58
|
+
Search, Download, ChevronDown, ChevronUp, SlidersHorizontal, Filter as FilterIcon,
|
|
54
59
|
Eye, Copy
|
|
55
60
|
} from '@lucide/svelte';
|
|
56
61
|
import ConfirmDialog from './ConfirmDialog.svelte';
|
|
@@ -59,6 +64,8 @@
|
|
|
59
64
|
import DraggableHeader from './DraggableHeader.svelte';
|
|
60
65
|
import type { Snippet } from 'svelte';
|
|
61
66
|
|
|
67
|
+
const i18n = useTranslation();
|
|
68
|
+
|
|
62
69
|
// ─── Props with Snippet composability ─────────────────────────
|
|
63
70
|
interface Props {
|
|
64
71
|
resourceName: string;
|
|
@@ -96,12 +103,13 @@
|
|
|
96
103
|
pagination: externalPagination,
|
|
97
104
|
sorters: externalSorters,
|
|
98
105
|
}: Props = $props();
|
|
106
|
+
const adminContext = captureAdminContext();
|
|
99
107
|
|
|
100
108
|
const resource = $derived(getResource(resourceName));
|
|
101
109
|
const primaryKey = $derived(resource.primaryKey ?? 'id');
|
|
102
110
|
|
|
103
111
|
// ─── URL state + server-side state ────────────────────────────
|
|
104
|
-
const urlState = readURLState();
|
|
112
|
+
const urlState = readURLState(adminContext);
|
|
105
113
|
|
|
106
114
|
// Snapshot resource values for initial state (untrack to avoid reactive tracking)
|
|
107
115
|
const storedPageSize = typeof window !== 'undefined' ? parseInt(localStorage.getItem('svadmin-default-page-size') ?? '', 10) : NaN;
|
|
@@ -120,6 +128,22 @@
|
|
|
120
128
|
);
|
|
121
129
|
let filters = $state<Filter[]>([]);
|
|
122
130
|
let searchText = $state(urlState.search ?? '');
|
|
131
|
+
let appliedSearchText = $state(untrack(() => searchText));
|
|
132
|
+
let searchDebounceTimer: ReturnType<typeof setTimeout> | undefined;
|
|
133
|
+
|
|
134
|
+
function scheduleSearch(event: Event) {
|
|
135
|
+
searchText = (event.currentTarget as HTMLInputElement).value;
|
|
136
|
+
if (searchDebounceTimer) clearTimeout(searchDebounceTimer);
|
|
137
|
+
searchDebounceTimer = setTimeout(() => {
|
|
138
|
+
appliedSearchText = searchText;
|
|
139
|
+
pagination = { ...pagination, current: 1 };
|
|
140
|
+
searchDebounceTimer = undefined;
|
|
141
|
+
}, 300);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
onDestroy(() => {
|
|
145
|
+
if (searchDebounceTimer) clearTimeout(searchDebounceTimer);
|
|
146
|
+
});
|
|
123
147
|
|
|
124
148
|
// Sync external controlled state
|
|
125
149
|
$effect(() => {
|
|
@@ -141,8 +165,8 @@
|
|
|
141
165
|
pageSize: pagination.pageSize,
|
|
142
166
|
sortField: sorters[0]?.field,
|
|
143
167
|
sortOrder: sorters[0]?.order,
|
|
144
|
-
search:
|
|
145
|
-
});
|
|
168
|
+
search: appliedSearchText || undefined,
|
|
169
|
+
}, adminContext);
|
|
146
170
|
});
|
|
147
171
|
|
|
148
172
|
// ─── Build active filters with search ─────────────────────────
|
|
@@ -152,18 +176,19 @@
|
|
|
152
176
|
const activeFilterCount = $derived(Object.values(filterValues).filter(v => v.trim()).length);
|
|
153
177
|
const activeFilters = $derived.by(() => {
|
|
154
178
|
const result: Filter[] = [...filters];
|
|
155
|
-
if (
|
|
179
|
+
if (appliedSearchText.trim() && searchableFields.length > 0) {
|
|
156
180
|
if (searchableFields.length === 1) {
|
|
157
|
-
result.push({ field: searchableFields[0].key, operator: 'contains', value:
|
|
181
|
+
result.push({ field: searchableFields[0].key, operator: 'contains', value: appliedSearchText });
|
|
158
182
|
} else {
|
|
159
|
-
|
|
183
|
+
const searchFilter: LogicalFilter = {
|
|
160
184
|
operator: 'or',
|
|
161
185
|
value: searchableFields.map(f => ({
|
|
162
186
|
field: f.key,
|
|
163
187
|
operator: 'contains',
|
|
164
|
-
value:
|
|
188
|
+
value: appliedSearchText
|
|
165
189
|
}))
|
|
166
|
-
}
|
|
190
|
+
};
|
|
191
|
+
result.push(searchFilter);
|
|
167
192
|
}
|
|
168
193
|
}
|
|
169
194
|
for (const [key, value] of Object.entries(filterValues)) {
|
|
@@ -240,10 +265,10 @@
|
|
|
240
265
|
const canExport = $derived(canExportPerm.allowed);
|
|
241
266
|
|
|
242
267
|
// ─── TanStack Table state ────────────────────────────────────
|
|
243
|
-
|
|
268
|
+
const initialSorting = untrack<SortingState>(() =>
|
|
244
269
|
sorters.map(s => ({ id: s.field, desc: s.order === 'desc' }))
|
|
245
|
-
)
|
|
246
|
-
|
|
270
|
+
);
|
|
271
|
+
const initialColumnVisibility = (() => {
|
|
247
272
|
// Try to restore from localStorage first
|
|
248
273
|
const storageKey = `svadmin-columns-${resourceName}`;
|
|
249
274
|
if (typeof window !== 'undefined') {
|
|
@@ -258,79 +283,41 @@
|
|
|
258
283
|
if (f.showInList === false) vis[f.key] = false;
|
|
259
284
|
}
|
|
260
285
|
return vis;
|
|
261
|
-
})()
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
286
|
+
})();
|
|
287
|
+
const sortingAtom = createAtom(initialSorting);
|
|
288
|
+
const columnVisibilityAtom = createAtom(initialColumnVisibility);
|
|
289
|
+
const rowSelectionAtom = createAtom({} as RowSelectionState);
|
|
290
|
+
const expandedAtom = createAtom({} as ExpandedState);
|
|
291
|
+
const columnOrderAtom = createAtom([] as string[]);
|
|
292
|
+
|
|
293
|
+
const tableSorting = useSelector(sortingAtom);
|
|
294
|
+
const tableColumnVisibility = useSelector(columnVisibilityAtom);
|
|
295
|
+
const tableRowSelection = useSelector(rowSelectionAtom);
|
|
296
|
+
const tableExpanded = useSelector(expandedAtom);
|
|
297
|
+
const tableColumnOrder = useSelector(columnOrderAtom);
|
|
298
|
+
|
|
299
|
+
function rowIsExpanded(rowId: string): boolean {
|
|
300
|
+
const expanded = tableExpanded.current;
|
|
301
|
+
return expanded === true || expanded[rowId] === true;
|
|
268
302
|
}
|
|
269
303
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const tableRowSelection = $derived<RowSelectionState>({ ...rowSelection });
|
|
276
|
-
const tableExpanded = $derived<ExpandedState>(expanded === true ? true : { ...expanded });
|
|
277
|
-
const tableColumnOrder = $derived<string[]>([...columnOrder]);
|
|
278
|
-
|
|
279
|
-
const sortingAtom = {
|
|
280
|
-
get: () => tableSorting,
|
|
281
|
-
set: (updater: Updater<SortingState>) => {
|
|
282
|
-
const nextSorting = applyTableUpdater(tableSorting, updater);
|
|
283
|
-
sorting = nextSorting;
|
|
284
|
-
|
|
285
|
-
const nextSorters: Sort[] = nextSorting.map((s) => ({
|
|
286
|
-
field: s.id,
|
|
287
|
-
order: s.desc ? 'desc' as const : 'asc' as const,
|
|
288
|
-
}));
|
|
289
|
-
|
|
290
|
-
if (JSON.stringify(nextSorters) !== JSON.stringify(sorters)) {
|
|
291
|
-
sorters = nextSorters;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
if ((pagination.current ?? 1) !== 1) {
|
|
295
|
-
pagination = { ...pagination, current: 1 };
|
|
296
|
-
}
|
|
297
|
-
},
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
const columnVisibilityAtom = {
|
|
301
|
-
get: () => tableColumnVisibility,
|
|
302
|
-
set: (updater: Updater<ColumnVisibilityState>) => {
|
|
303
|
-
columnVisibility = applyTableUpdater(tableColumnVisibility, updater);
|
|
304
|
-
},
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
const rowSelectionAtom = {
|
|
308
|
-
get: () => tableRowSelection,
|
|
309
|
-
set: (updater: Updater<RowSelectionState>) => {
|
|
310
|
-
rowSelection = applyTableUpdater(tableRowSelection, updater);
|
|
311
|
-
},
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
const expandedAtom = {
|
|
315
|
-
get: () => tableExpanded,
|
|
316
|
-
set: (updater: Updater<ExpandedState>) => {
|
|
317
|
-
expanded = applyTableUpdater(tableExpanded, updater);
|
|
318
|
-
},
|
|
319
|
-
};
|
|
304
|
+
$effect(() => {
|
|
305
|
+
const nextSorters: Sort[] = tableSorting.current.map((sorter) => ({
|
|
306
|
+
field: sorter.id,
|
|
307
|
+
order: sorter.desc ? 'desc' as const : 'asc' as const,
|
|
308
|
+
}));
|
|
320
309
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
},
|
|
326
|
-
};
|
|
310
|
+
if (JSON.stringify(nextSorters) !== JSON.stringify(sorters)) {
|
|
311
|
+
sorters = nextSorters;
|
|
312
|
+
}
|
|
313
|
+
});
|
|
327
314
|
|
|
328
315
|
// Persist column visibility to localStorage
|
|
329
316
|
$effect(() => {
|
|
330
317
|
const storageKey = `svadmin-columns-${resourceName}`;
|
|
331
318
|
if (typeof window !== 'undefined') {
|
|
332
319
|
try {
|
|
333
|
-
localStorage.setItem(storageKey, JSON.stringify(
|
|
320
|
+
localStorage.setItem(storageKey, JSON.stringify(tableColumnVisibility.current));
|
|
334
321
|
} catch { /* ignore quota errors */ }
|
|
335
322
|
}
|
|
336
323
|
});
|
|
@@ -342,8 +329,8 @@
|
|
|
342
329
|
sorters = externalSorters;
|
|
343
330
|
}
|
|
344
331
|
const nextSorting = externalSorters.map(s => ({ id: s.field, desc: s.order === 'desc' }));
|
|
345
|
-
if (JSON.stringify(nextSorting) !== JSON.stringify(
|
|
346
|
-
|
|
332
|
+
if (JSON.stringify(nextSorting) !== JSON.stringify(tableSorting.current)) {
|
|
333
|
+
sortingAtom.set(nextSorting);
|
|
347
334
|
}
|
|
348
335
|
});
|
|
349
336
|
|
|
@@ -353,7 +340,7 @@
|
|
|
353
340
|
resource.fields.filter(f => f.showInList !== false)
|
|
354
341
|
);
|
|
355
342
|
|
|
356
|
-
const columns = $derived<ColumnDef<
|
|
343
|
+
const columns = $derived<ColumnDef<TableFeatures, BaseRecord, unknown>[]>([
|
|
357
344
|
// Selection column
|
|
358
345
|
...(selectable && (canDelete || batchActions) ? [{
|
|
359
346
|
id: '_select',
|
|
@@ -361,7 +348,7 @@
|
|
|
361
348
|
cell: () => '',
|
|
362
349
|
size: 40,
|
|
363
350
|
enableSorting: false,
|
|
364
|
-
} satisfies ColumnDef<
|
|
351
|
+
} satisfies ColumnDef<TableFeatures, BaseRecord, unknown>] : []),
|
|
365
352
|
// Expand column
|
|
366
353
|
...(expandedRowRender ? [{
|
|
367
354
|
id: '_expand',
|
|
@@ -369,9 +356,9 @@
|
|
|
369
356
|
cell: () => '',
|
|
370
357
|
size: 40,
|
|
371
358
|
enableSorting: false,
|
|
372
|
-
} satisfies ColumnDef<
|
|
359
|
+
} satisfies ColumnDef<TableFeatures, BaseRecord, unknown>] : []),
|
|
373
360
|
// Data columns
|
|
374
|
-
...visibleFields.map((field): ColumnDef<
|
|
361
|
+
...visibleFields.map((field): ColumnDef<TableFeatures, BaseRecord, unknown> => ({
|
|
375
362
|
id: field.key,
|
|
376
363
|
accessorKey: field.key,
|
|
377
364
|
header: () => field.label,
|
|
@@ -380,33 +367,72 @@
|
|
|
380
367
|
// Actions column
|
|
381
368
|
{
|
|
382
369
|
id: '_actions',
|
|
383
|
-
header: () => t('common.actions'),
|
|
370
|
+
header: () => i18n.t('common.actions'),
|
|
384
371
|
cell: () => '',
|
|
385
372
|
size: 100,
|
|
386
373
|
enableSorting: false,
|
|
387
374
|
},
|
|
388
375
|
]);
|
|
376
|
+
const orderedColumns = $derived.by(() => {
|
|
377
|
+
const columnOrder = tableColumnOrder.current;
|
|
378
|
+
if (!columnOrder.length) return columns;
|
|
379
|
+
|
|
380
|
+
const columnsById = new Map(columns.map((column) => [column.id, column]));
|
|
381
|
+
return [
|
|
382
|
+
...columnOrder.flatMap((id) => {
|
|
383
|
+
const column = columnsById.get(id);
|
|
384
|
+
return column ? [column] : [];
|
|
385
|
+
}),
|
|
386
|
+
...columns.filter((column) => !columnOrder.includes(column.id ?? '')),
|
|
387
|
+
];
|
|
388
|
+
});
|
|
389
389
|
|
|
390
390
|
// ─── Create TanStack Table ────────────────────────────────────
|
|
391
|
-
const tbl = createTable(
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
391
|
+
const tbl = createTable(
|
|
392
|
+
{
|
|
393
|
+
get data() { return query.data?.data ?? []; },
|
|
394
|
+
get columns() { return orderedColumns; },
|
|
395
|
+
getCoreRowModel: createCoreRowModel(),
|
|
396
|
+
manualPagination: true,
|
|
397
|
+
manualSorting: true,
|
|
398
|
+
getRowId: (row: BaseRecord) => String(row[primaryKey]),
|
|
399
|
+
atoms: {
|
|
400
|
+
sorting: sortingAtom,
|
|
401
|
+
columnVisibility: columnVisibilityAtom,
|
|
402
|
+
rowSelection: rowSelectionAtom,
|
|
403
|
+
expanded: expandedAtom,
|
|
404
|
+
},
|
|
405
|
+
onSortingChange: sortingAtom.set,
|
|
406
|
+
onColumnVisibilityChange: columnVisibilityAtom.set,
|
|
407
|
+
onRowSelectionChange: rowSelectionAtom.set,
|
|
408
|
+
onExpandedChange: expandedAtom.set,
|
|
409
|
+
get enableRowSelection() { return selectable && (canDelete || batchActions); },
|
|
410
|
+
get enableExpanding() { return !!expandedRowRender; },
|
|
411
|
+
getRowCanExpand: () => !!expandedRowRender,
|
|
404
412
|
},
|
|
405
|
-
|
|
406
|
-
|
|
413
|
+
// AutoTable reads focused atoms directly; selecting the full state triggers Svelte proxy equality warnings.
|
|
414
|
+
() => undefined,
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
$effect.pre(() => {
|
|
418
|
+
const nextColumns = orderedColumns;
|
|
419
|
+
untrack(() => {
|
|
420
|
+
tbl.setOptions({ ...tbl.options, columns: nextColumns });
|
|
421
|
+
});
|
|
407
422
|
});
|
|
408
423
|
|
|
409
|
-
const selectedCount = $derived(Object.keys(
|
|
424
|
+
const selectedCount = $derived(Object.keys(tableRowSelection.current).length);
|
|
425
|
+
const tableView = $derived.by(() => {
|
|
426
|
+
void tableSorting.current;
|
|
427
|
+
void tableColumnVisibility.current;
|
|
428
|
+
void tableRowSelection.current;
|
|
429
|
+
void tableExpanded.current;
|
|
430
|
+
void tableColumnOrder.current;
|
|
431
|
+
return {
|
|
432
|
+
headerGroups: tbl.getHeaderGroups(),
|
|
433
|
+
rows: table_getRowModel(tbl).rows,
|
|
434
|
+
};
|
|
435
|
+
});
|
|
410
436
|
const totalPages = $derived(Math.ceil((query.data?.total ?? 0) / (pagination.pageSize ?? 10)));
|
|
411
437
|
|
|
412
438
|
// ─── Pagination helpers ───────────────────────────────────────
|
|
@@ -433,7 +459,7 @@
|
|
|
433
459
|
let confirmAction = $state<() => void>(() => {});
|
|
434
460
|
|
|
435
461
|
function confirmDelete(id: string | number) {
|
|
436
|
-
confirmMessage = t('common.deleteConfirm');
|
|
462
|
+
confirmMessage = i18n.t('common.deleteConfirm');
|
|
437
463
|
confirmAction = async () => {
|
|
438
464
|
await deleteMutation.mutateAsync({ id, resource: resourceName });
|
|
439
465
|
confirmOpen = false;
|
|
@@ -442,16 +468,16 @@
|
|
|
442
468
|
}
|
|
443
469
|
|
|
444
470
|
function confirmBatchDelete() {
|
|
445
|
-
const ids = Object.keys(
|
|
446
|
-
confirmMessage = t('common.batchDeleteConfirm', { count: ids.length });
|
|
471
|
+
const ids = Object.keys(tableRowSelection.current);
|
|
472
|
+
confirmMessage = i18n.t('common.batchDeleteConfirm', { count: ids.length });
|
|
447
473
|
confirmAction = async () => {
|
|
448
474
|
try {
|
|
449
475
|
await deleteManyMutation.mutateAsync({ ids, resource: resourceName });
|
|
450
|
-
notify({ type: 'success', message: t('common.batchDeleteSuccess', { count: ids.length }) });
|
|
476
|
+
notify({ type: 'success', message: i18n.t('common.batchDeleteSuccess', { count: ids.length }) });
|
|
451
477
|
} catch {
|
|
452
|
-
notify({ type: 'error', message: t('common.batchDeletePartialFail', { failed: 1, total: ids.length }) });
|
|
478
|
+
notify({ type: 'error', message: i18n.t('common.batchDeletePartialFail', { failed: 1, total: ids.length }) });
|
|
453
479
|
}
|
|
454
|
-
|
|
480
|
+
rowSelectionAtom.set({});
|
|
455
481
|
confirmOpen = false;
|
|
456
482
|
};
|
|
457
483
|
confirmOpen = true;
|
|
@@ -491,7 +517,7 @@
|
|
|
491
517
|
<div class="flex flex-wrap items-center gap-2">
|
|
492
518
|
{#if canExport}
|
|
493
519
|
<Button variant="outline" size="sm" onclick={exportCSV}>
|
|
494
|
-
<Download class="h-4 w-4" data-icon="inline-start" /> {t('common.export')}
|
|
520
|
+
<Download class="h-4 w-4" data-icon="inline-start" /> {i18n.t('common.export')}
|
|
495
521
|
</Button>
|
|
496
522
|
{/if}
|
|
497
523
|
{#if selectedCount > 0 && canDelete}
|
|
@@ -501,12 +527,12 @@
|
|
|
501
527
|
class="h-9 whitespace-nowrap"
|
|
502
528
|
onclick={confirmBatchDelete}
|
|
503
529
|
>
|
|
504
|
-
<Trash2 class="h-4 w-4" data-icon="inline-start" /> {t('common.batchDelete', { count: selectedCount })}
|
|
530
|
+
<Trash2 class="h-4 w-4" data-icon="inline-start" /> {i18n.t('common.batchDelete', { count: selectedCount })}
|
|
505
531
|
</Button>
|
|
506
532
|
{/if}
|
|
507
533
|
|
|
508
534
|
{#if selectedCount > 0 && batchActions}
|
|
509
|
-
{@render batchActions({ selectedIds: Object.keys(
|
|
535
|
+
{@render batchActions({ selectedIds: Object.keys(tableRowSelection.current) })}
|
|
510
536
|
{/if}
|
|
511
537
|
|
|
512
538
|
<!-- Column Visibility Picker (DropdownMenu) -->
|
|
@@ -514,14 +540,14 @@
|
|
|
514
540
|
<DropdownMenu.Trigger>
|
|
515
541
|
{#snippet child({ props })}
|
|
516
542
|
<Button variant="outline" size="sm" {...props}>
|
|
517
|
-
<SlidersHorizontal class="h-4 w-4" data-icon="inline-start" /> {t('common.columns')
|
|
543
|
+
<SlidersHorizontal class="h-4 w-4" data-icon="inline-start" /> {i18n.t('common.columns')}
|
|
518
544
|
</Button>
|
|
519
545
|
{/snippet}
|
|
520
546
|
</DropdownMenu.Trigger>
|
|
521
547
|
<DropdownMenu.Content align="end" class="w-48">
|
|
522
|
-
{#each table_getAllLeafColumns(tbl).filter((
|
|
548
|
+
{#each table_getAllLeafColumns(tbl).filter((column: Column<TableFeatures, BaseRecord, unknown>) => !column.id.startsWith('_')) as column, _i (_i)}
|
|
523
549
|
<DropdownMenu.CheckboxItem
|
|
524
|
-
checked={
|
|
550
|
+
checked={tableColumnVisibility.current[column.id] ?? true}
|
|
525
551
|
onCheckedChange={(v) => column_toggleVisibility(column, !!v)}
|
|
526
552
|
>
|
|
527
553
|
{visibleFields.find(f => f.key === column.id)?.label ?? column.id}
|
|
@@ -533,8 +559,8 @@
|
|
|
533
559
|
{@render headerActions()}
|
|
534
560
|
{/if}
|
|
535
561
|
{#if canCreate}
|
|
536
|
-
<Button onclick={() => navigate(`/${resourceName}/create`)}>
|
|
537
|
-
<Plus class="h-4 w-4" data-icon="inline-start" /> {t('common.create')}
|
|
562
|
+
<Button onclick={() => adminContext.navigate(`/${resourceName}/create`)}>
|
|
563
|
+
<Plus class="h-4 w-4" data-icon="inline-start" /> {i18n.t('common.create')}
|
|
538
564
|
</Button>
|
|
539
565
|
{/if}
|
|
540
566
|
</div>
|
|
@@ -547,9 +573,9 @@
|
|
|
547
573
|
<Search class="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
|
|
548
574
|
<Input
|
|
549
575
|
type="text"
|
|
550
|
-
|
|
551
|
-
oninput={
|
|
552
|
-
placeholder={t('common.search')}
|
|
576
|
+
value={searchText}
|
|
577
|
+
oninput={scheduleSearch}
|
|
578
|
+
placeholder={i18n.t('common.search')}
|
|
553
579
|
class="pl-10 h-9"
|
|
554
580
|
/>
|
|
555
581
|
</div>
|
|
@@ -561,7 +587,7 @@
|
|
|
561
587
|
{#snippet child({ props })}
|
|
562
588
|
<Button variant="outline" size="sm" class="h-9 px-3" {...props}>
|
|
563
589
|
<FilterIcon class="h-4 w-4" data-icon="inline-start" />
|
|
564
|
-
{t('common.filter')}
|
|
590
|
+
{i18n.t('common.filter')}
|
|
565
591
|
{#if activeFilterCount > 0}
|
|
566
592
|
<Badge variant="secondary" class="ml-1 h-5 min-w-5 px-1">{activeFilterCount}</Badge>
|
|
567
593
|
{/if}
|
|
@@ -570,7 +596,7 @@
|
|
|
570
596
|
</Popover.Trigger>
|
|
571
597
|
<Popover.Content class="w-80">
|
|
572
598
|
<div class="space-y-3">
|
|
573
|
-
<h4 class="font-medium text-sm">{t('common.filter')}</h4>
|
|
599
|
+
<h4 class="font-medium text-sm">{i18n.t('common.filter')}</h4>
|
|
574
600
|
{#each filterableFields as field, _i (_i)}
|
|
575
601
|
<div class="space-y-1">
|
|
576
602
|
<label class="text-xs text-muted-foreground" for="filter-{field.key}">{field.label}</label>
|
|
@@ -581,7 +607,7 @@
|
|
|
581
607
|
value={filterValues[field.key] ?? ''}
|
|
582
608
|
onchange={(e) => filterValues[field.key] = (e.currentTarget as HTMLSelectElement).value}
|
|
583
609
|
>
|
|
584
|
-
<option value="">{t('common.all')}</option>
|
|
610
|
+
<option value="">{i18n.t('common.all')}</option>
|
|
585
611
|
{#each field.options as opt, _i (_i)}
|
|
586
612
|
<option value={opt.value}>{opt.label}</option>
|
|
587
613
|
{/each}
|
|
@@ -600,13 +626,13 @@
|
|
|
600
626
|
{/each}
|
|
601
627
|
<div class="flex gap-2 pt-2">
|
|
602
628
|
<Button size="sm" class="flex-1" onclick={() => { pagination = { ...pagination, current: 1 }; }}>
|
|
603
|
-
{t('common.confirm')}
|
|
629
|
+
{i18n.t('common.confirm')}
|
|
604
630
|
</Button>
|
|
605
631
|
<Button variant="outline" size="sm" onclick={() => {
|
|
606
632
|
filterValues = {};
|
|
607
633
|
pagination = { ...pagination, current: 1 };
|
|
608
634
|
}}>
|
|
609
|
-
{t('common.reset')}
|
|
635
|
+
{i18n.t('common.reset')}
|
|
610
636
|
</Button>
|
|
611
637
|
</div>
|
|
612
638
|
</div>
|
|
@@ -616,7 +642,7 @@
|
|
|
616
642
|
</div>
|
|
617
643
|
|
|
618
644
|
<!-- Table (TanStack-powered) -->
|
|
619
|
-
<div class="rounded-[24px] bg-card/80 backdrop-blur-3xl shadow-[0_8px_30px_rgb(0,0,0,0.04)] overflow-hidden ring-1 ring-border/30" role="region" aria-label="{resource.label} {t('common.list')}">
|
|
645
|
+
<div class="rounded-[24px] bg-card/80 backdrop-blur-3xl shadow-[0_8px_30px_rgb(0,0,0,0.04)] overflow-hidden ring-1 ring-border/30" role="region" aria-label="{resource.label} {i18n.t('common.list')}">
|
|
620
646
|
{#if query.isLoading}
|
|
621
647
|
<div class="p-4 space-y-3">
|
|
622
648
|
<div class="flex gap-4 mb-2">
|
|
@@ -634,7 +660,7 @@
|
|
|
634
660
|
</div>
|
|
635
661
|
{:else if query.error}
|
|
636
662
|
<div class="flex h-64 items-center justify-center text-destructive text-sm">
|
|
637
|
-
{t('common.loadFailed', { message: (query.error as Error).message })}
|
|
663
|
+
{i18n.t('common.loadFailed', { message: (query.error as Error).message })}
|
|
638
664
|
</div>
|
|
639
665
|
{:else}
|
|
640
666
|
<div in:fade={{ duration: 150 }}>
|
|
@@ -642,11 +668,11 @@
|
|
|
642
668
|
<div class="hidden md:block">
|
|
643
669
|
<Table.Root>
|
|
644
670
|
<Table.Header>
|
|
645
|
-
{#each
|
|
671
|
+
{#each tableView.headerGroups as headerGroup, _i (_i)}
|
|
646
672
|
<DraggableHeader
|
|
647
|
-
columns={headerGroup.headers.map((
|
|
673
|
+
columns={headerGroup.headers.map((header: Header<TableFeatures, BaseRecord, unknown>) => ({ id: header.column.id, header }))}
|
|
648
674
|
resourceName={resourceName}
|
|
649
|
-
onReorder={(newOrder) =>
|
|
675
|
+
onReorder={(newOrder) => columnOrderAtom.set(newOrder.map((column) => column.id))}
|
|
650
676
|
>
|
|
651
677
|
{#snippet header(col, _index, dragProps)}
|
|
652
678
|
{@const header = col.header as typeof headerGroup.headers[0]}
|
|
@@ -663,7 +689,7 @@
|
|
|
663
689
|
{:else if header.id === '_expand'}
|
|
664
690
|
<!-- empty -->
|
|
665
691
|
{:else if header.id === '_actions'}
|
|
666
|
-
<span class="text-right block">{t('common.actions')}</span>
|
|
692
|
+
<span class="text-right block">{i18n.t('common.actions')}</span>
|
|
667
693
|
{:else if column_getCanSort(header.column)}
|
|
668
694
|
<Button
|
|
669
695
|
variant="ghost"
|
|
@@ -688,7 +714,7 @@
|
|
|
688
714
|
{/each}
|
|
689
715
|
</Table.Header>
|
|
690
716
|
<Table.Body>
|
|
691
|
-
{#each
|
|
717
|
+
{#each tableView.rows as row, _i (_i)}
|
|
692
718
|
{@const record = row.original}
|
|
693
719
|
{@const id = record[primaryKey] as string | number}
|
|
694
720
|
<ContextMenu.Root>
|
|
@@ -703,8 +729,8 @@
|
|
|
703
729
|
onCheckedChange={() => row_toggleSelected(row)}
|
|
704
730
|
/>
|
|
705
731
|
{:else if cell.column.id === '_expand'}
|
|
706
|
-
<TooltipButton tooltip={
|
|
707
|
-
{#if
|
|
732
|
+
<TooltipButton tooltip={rowIsExpanded(row.id) ? i18n.t('common.collapse') : i18n.t('common.expand')} variant="ghost" size="icon" class="h-7 w-7" onclick={() => row_toggleExpanded(row)}>
|
|
733
|
+
{#if rowIsExpanded(row.id)}
|
|
708
734
|
<ChevronUp class="h-4 w-4" />
|
|
709
735
|
{:else}
|
|
710
736
|
<ChevronDown class="h-4 w-4" />
|
|
@@ -716,12 +742,12 @@
|
|
|
716
742
|
{@render rowActions({ record, id })}
|
|
717
743
|
{:else}
|
|
718
744
|
{#if canEdit}
|
|
719
|
-
<TooltipButton tooltip={t('common.edit')} variant="ghost" size="icon-sm" onclick={() => navigate(`/${resourceName}/edit/${id}`)}>
|
|
745
|
+
<TooltipButton tooltip={i18n.t('common.edit')} variant="ghost" size="icon-sm" onclick={() => adminContext.navigate(`/${resourceName}/edit/${id}`)}>
|
|
720
746
|
<Pencil class="h-4 w-4" />
|
|
721
747
|
</TooltipButton>
|
|
722
748
|
{/if}
|
|
723
749
|
{#if canDelete}
|
|
724
|
-
<TooltipButton tooltip={t('common.delete')} variant="ghost" size="icon-sm" onclick={() => confirmDelete(id)} class="hover:text-destructive">
|
|
750
|
+
<TooltipButton tooltip={i18n.t('common.delete')} variant="ghost" size="icon-sm" onclick={() => confirmDelete(id)} class="hover:text-destructive">
|
|
725
751
|
<Trash2 class="h-4 w-4" />
|
|
726
752
|
</TooltipButton>
|
|
727
753
|
{/if}
|
|
@@ -767,25 +793,25 @@
|
|
|
767
793
|
</ContextMenu.Trigger>
|
|
768
794
|
<ContextMenu.Content class="w-48">
|
|
769
795
|
{#if canEdit}
|
|
770
|
-
<ContextMenu.Item onclick={() => navigate(`/${resourceName}/edit/${id}`)} class="gap-2">
|
|
771
|
-
<Pencil class="h-4 w-4" /> {t('common.edit')}
|
|
796
|
+
<ContextMenu.Item onclick={() => adminContext.navigate(`/${resourceName}/edit/${id}`)} class="gap-2">
|
|
797
|
+
<Pencil class="h-4 w-4" /> {i18n.t('common.edit')}
|
|
772
798
|
</ContextMenu.Item>
|
|
773
799
|
{/if}
|
|
774
|
-
<ContextMenu.Item onclick={() => navigate(`/${resourceName}/show/${id}`)} class="gap-2">
|
|
775
|
-
<Eye class="h-4 w-4" /> {t('common.detail')}
|
|
800
|
+
<ContextMenu.Item onclick={() => adminContext.navigate(`/${resourceName}/show/${id}`)} class="gap-2">
|
|
801
|
+
<Eye class="h-4 w-4" /> {i18n.t('common.detail')}
|
|
776
802
|
</ContextMenu.Item>
|
|
777
803
|
<ContextMenu.Item onclick={() => navigator.clipboard?.writeText(String(id))} class="gap-2">
|
|
778
|
-
<Copy class="h-4 w-4" /> {t('common.copyId')}
|
|
804
|
+
<Copy class="h-4 w-4" /> {i18n.t('common.copyId')}
|
|
779
805
|
</ContextMenu.Item>
|
|
780
806
|
{#if canDelete}
|
|
781
807
|
<ContextMenu.Separator />
|
|
782
808
|
<ContextMenu.Item onclick={() => confirmDelete(id)} class="gap-2 text-destructive">
|
|
783
|
-
<Trash2 class="h-4 w-4" /> {t('common.delete')}
|
|
809
|
+
<Trash2 class="h-4 w-4" /> {i18n.t('common.delete')}
|
|
784
810
|
</ContextMenu.Item>
|
|
785
811
|
{/if}
|
|
786
812
|
</ContextMenu.Content>
|
|
787
813
|
</ContextMenu.Root>
|
|
788
|
-
{#if expandedRowRender &&
|
|
814
|
+
{#if expandedRowRender && rowIsExpanded(row.id)}
|
|
789
815
|
<Table.Row class="bg-muted/10 border-b border-border/10 transition-all">
|
|
790
816
|
<Table.Cell colspan={row_getVisibleCells(row).length}>
|
|
791
817
|
{@render expandedRowRender({ record })}
|
|
@@ -802,12 +828,12 @@
|
|
|
802
828
|
<svg class="h-16 w-16 text-muted-foreground/30 mb-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor">
|
|
803
829
|
<path stroke-linecap="round" stroke-linejoin="round" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
|
804
830
|
</svg>
|
|
805
|
-
<p class="text-sm font-medium text-muted-foreground mb-1">{t('common.noData')}</p>
|
|
806
|
-
<p class="text-xs text-muted-foreground/60 mb-4">{t('common.noDataHint')
|
|
831
|
+
<p class="text-sm font-medium text-muted-foreground mb-1">{i18n.t('common.noData')}</p>
|
|
832
|
+
<p class="text-xs text-muted-foreground/60 mb-4">{i18n.t('common.noDataHint')}</p>
|
|
807
833
|
{#if canCreate}
|
|
808
|
-
<Button variant="outline" size="sm" class="gap-2" onclick={() => navigate(`/${resourceName}/create`)}>
|
|
834
|
+
<Button variant="outline" size="sm" class="gap-2" onclick={() => adminContext.navigate(`/${resourceName}/create`)}>
|
|
809
835
|
<Plus class="h-3.5 w-3.5" />
|
|
810
|
-
{t('common.create')}
|
|
836
|
+
{i18n.t('common.create')}
|
|
811
837
|
</Button>
|
|
812
838
|
{/if}
|
|
813
839
|
</div>
|
|
@@ -821,7 +847,7 @@
|
|
|
821
847
|
|
|
822
848
|
<!-- Mobile Card View (visible only on small screens) -->
|
|
823
849
|
<div class="md:hidden space-y-3 p-2">
|
|
824
|
-
{#each
|
|
850
|
+
{#each tableView.rows as row, _i (_i)}
|
|
825
851
|
{@const record = row.original}
|
|
826
852
|
{@const id = record[primaryKey] as string | number}
|
|
827
853
|
<div
|
|
@@ -843,15 +869,15 @@
|
|
|
843
869
|
{@render rowActions({ record, id })}
|
|
844
870
|
{:else}
|
|
845
871
|
{#if canEdit}
|
|
846
|
-
<TooltipButton tooltip={t('common.edit')} variant="ghost" size="icon-sm" onclick={() => navigate(`/${resourceName}/edit/${id}`)}>
|
|
872
|
+
<TooltipButton tooltip={i18n.t('common.edit')} variant="ghost" size="icon-sm" onclick={() => adminContext.navigate(`/${resourceName}/edit/${id}`)}>
|
|
847
873
|
<Pencil class="h-4 w-4" />
|
|
848
874
|
</TooltipButton>
|
|
849
875
|
{/if}
|
|
850
|
-
<TooltipButton tooltip={t('common.detail')} variant="ghost" size="icon-sm" onclick={() => navigate(`/${resourceName}/show/${id}`)}>
|
|
876
|
+
<TooltipButton tooltip={i18n.t('common.detail')} variant="ghost" size="icon-sm" onclick={() => adminContext.navigate(`/${resourceName}/show/${id}`)}>
|
|
851
877
|
<Eye class="h-4 w-4" />
|
|
852
878
|
</TooltipButton>
|
|
853
879
|
{#if canDelete}
|
|
854
|
-
<TooltipButton tooltip={t('common.delete')} variant="ghost" size="icon-sm" onclick={() => confirmDelete(id)} class="hover:text-destructive">
|
|
880
|
+
<TooltipButton tooltip={i18n.t('common.delete')} variant="ghost" size="icon-sm" onclick={() => confirmDelete(id)} class="hover:text-destructive">
|
|
855
881
|
<Trash2 class="h-4 w-4" />
|
|
856
882
|
</TooltipButton>
|
|
857
883
|
{/if}
|
|
@@ -895,7 +921,7 @@
|
|
|
895
921
|
{#if emptyState}
|
|
896
922
|
{@render emptyState()}
|
|
897
923
|
{:else}
|
|
898
|
-
{t('common.noData')}
|
|
924
|
+
{i18n.t('common.noData')}
|
|
899
925
|
{/if}
|
|
900
926
|
</div>
|
|
901
927
|
{/each}
|
|
@@ -908,7 +934,7 @@
|
|
|
908
934
|
{#if totalPages > 0}
|
|
909
935
|
<div class="flex flex-col sm:flex-row items-center justify-between gap-2 text-sm text-muted-foreground">
|
|
910
936
|
<div class="flex items-center gap-2">
|
|
911
|
-
<span>{t('common.total', { total: query.data?.total ?? 0 })}</span>
|
|
937
|
+
<span>{i18n.t('common.total', { total: query.data?.total ?? 0 })}</span>
|
|
912
938
|
<select
|
|
913
939
|
class="h-8 w-[70px] px-1 py-1 flex items-center justify-between rounded-md border border-input bg-background text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
914
940
|
value={String(pagination.pageSize ?? 10)}
|
|
@@ -962,7 +988,7 @@
|
|
|
962
988
|
<ConfirmDialog
|
|
963
989
|
open={confirmOpen}
|
|
964
990
|
message={confirmMessage}
|
|
965
|
-
confirmText={t('common.delete')}
|
|
991
|
+
confirmText={i18n.t('common.delete')}
|
|
966
992
|
onconfirm={confirmAction}
|
|
967
993
|
oncancel={() => { confirmOpen = false; }}
|
|
968
994
|
/>
|