@svadmin/ui 0.29.1 → 0.29.3

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.
Files changed (224) hide show
  1. package/package.json +20 -9
  2. package/src/README.md +0 -18
  3. package/src/actions.ts +0 -110
  4. package/src/app.css +0 -673
  5. package/src/component-registry.svelte.ts +0 -60
  6. package/src/components/AICommandBar.svelte +0 -180
  7. package/src/components/AboutSettings.svelte +0 -106
  8. package/src/components/AdminApp.svelte +0 -199
  9. package/src/components/AnimatedCounter.svelte +0 -46
  10. package/src/components/AnomalyBadge.svelte +0 -65
  11. package/src/components/AppearanceSettings.svelte +0 -183
  12. package/src/components/AuditLogDrawer.svelte +0 -93
  13. package/src/components/AuditLogViewer.svelte +0 -226
  14. package/src/components/Authenticated.svelte +0 -51
  15. package/src/components/AutoForm.svelte +0 -242
  16. package/src/components/AutoSaveIndicator.svelte +0 -35
  17. package/src/components/AutoTable.svelte +0 -839
  18. package/src/components/Breadcrumbs.svelte +0 -58
  19. package/src/components/CanAccess.svelte +0 -23
  20. package/src/components/CatchAllNavigate.svelte +0 -21
  21. package/src/components/ChatDialog.svelte +0 -535
  22. package/src/components/ComboboxField.svelte +0 -129
  23. package/src/components/CommandPalette.svelte +0 -135
  24. package/src/components/ConfigErrorScreen.svelte +0 -126
  25. package/src/components/ConfirmDialog.svelte +0 -53
  26. package/src/components/CopilotPanel.svelte +0 -163
  27. package/src/components/CreatePage.svelte +0 -39
  28. package/src/components/DevTools.svelte +0 -149
  29. package/src/components/DraggableGrid.svelte +0 -86
  30. package/src/components/DraggableHeader.svelte +0 -112
  31. package/src/components/DrawerForm.svelte +0 -35
  32. package/src/components/EditPage.svelte +0 -52
  33. package/src/components/EmptyState.svelte +0 -31
  34. package/src/components/ErrorBoundary.svelte +0 -49
  35. package/src/components/ErrorComponent.svelte +0 -36
  36. package/src/components/FieldRenderer.svelte +0 -357
  37. package/src/components/ForgotPasswordPage.svelte +0 -112
  38. package/src/components/Header.svelte +0 -94
  39. package/src/components/InferencerPanel.svelte +0 -192
  40. package/src/components/InfiniteList.svelte +0 -93
  41. package/src/components/InlineEdit.svelte +0 -88
  42. package/src/components/InsightCard.svelte +0 -98
  43. package/src/components/KeyboardShortcuts.svelte +0 -61
  44. package/src/components/Layout.svelte +0 -167
  45. package/src/components/ListPage.svelte +0 -71
  46. package/src/components/LiveIndicator.svelte +0 -58
  47. package/src/components/LoginPage.svelte +0 -163
  48. package/src/components/MarkdownRenderer.svelte +0 -127
  49. package/src/components/ModalForm.svelte +0 -38
  50. package/src/components/NavigateToResource.svelte +0 -20
  51. package/src/components/PageHeader.svelte +0 -39
  52. package/src/components/PageSkeleton.svelte +0 -71
  53. package/src/components/PasswordInput.svelte +0 -96
  54. package/src/components/PermissionMatrix.svelte +0 -185
  55. package/src/components/ProfilePage.svelte +0 -284
  56. package/src/components/RegisterPage.svelte +0 -112
  57. package/src/components/RolesSettings.svelte +0 -146
  58. package/src/components/SettingsPage.svelte +0 -113
  59. package/src/components/ShowPage.svelte +0 -73
  60. package/src/components/Sidebar.svelte +0 -417
  61. package/src/components/SidebarItem.svelte +0 -129
  62. package/src/components/SmartSuggest.svelte +0 -158
  63. package/src/components/StatsCard.svelte +0 -79
  64. package/src/components/StepsForm.svelte +0 -192
  65. package/src/components/TaskQueueDrawer.svelte +0 -158
  66. package/src/components/TenantSwitcher.svelte +0 -84
  67. package/src/components/ThemedTitle.svelte +0 -32
  68. package/src/components/Toast.svelte +0 -46
  69. package/src/components/TooltipButton.svelte +0 -42
  70. package/src/components/UndoableNotification.svelte +0 -69
  71. package/src/components/UpdatePasswordPage.svelte +0 -89
  72. package/src/components/VirtualTable.svelte +0 -76
  73. package/src/components/VoiceInput.svelte +0 -120
  74. package/src/components/buttons/CloneButton.svelte +0 -30
  75. package/src/components/buttons/CreateButton.svelte +0 -29
  76. package/src/components/buttons/DeleteButton.svelte +0 -59
  77. package/src/components/buttons/EditButton.svelte +0 -30
  78. package/src/components/buttons/ExportButton.svelte +0 -30
  79. package/src/components/buttons/ImportButton.svelte +0 -56
  80. package/src/components/buttons/ListButton.svelte +0 -23
  81. package/src/components/buttons/RefreshButton.svelte +0 -36
  82. package/src/components/buttons/SaveButton.svelte +0 -27
  83. package/src/components/buttons/ShowButton.svelte +0 -31
  84. package/src/components/buttons/index.ts +0 -10
  85. package/src/components/charts/BarChart.svelte +0 -69
  86. package/src/components/charts/LineChart.svelte +0 -115
  87. package/src/components/charts/PieChart.svelte +0 -95
  88. package/src/components/charts/index.ts +0 -3
  89. package/src/components/fieldComponentMap.ts +0 -79
  90. package/src/components/fields/ArrayField.svelte +0 -86
  91. package/src/components/fields/BooleanField.svelte +0 -13
  92. package/src/components/fields/DateField.svelte +0 -6
  93. package/src/components/fields/EmailField.svelte +0 -9
  94. package/src/components/fields/FileField.svelte +0 -14
  95. package/src/components/fields/ImageField.svelte +0 -9
  96. package/src/components/fields/JsonField.svelte +0 -72
  97. package/src/components/fields/MarkdownField.svelte +0 -9
  98. package/src/components/fields/MultiSelectField.svelte +0 -21
  99. package/src/components/fields/NumberField.svelte +0 -6
  100. package/src/components/fields/RelationField.svelte +0 -24
  101. package/src/components/fields/RichTextField.svelte +0 -35
  102. package/src/components/fields/SelectField.svelte +0 -14
  103. package/src/components/fields/TagField.svelte +0 -12
  104. package/src/components/fields/TextField.svelte +0 -11
  105. package/src/components/fields/UrlField.svelte +0 -14
  106. package/src/components/ui/alert/alert-description.svelte +0 -17
  107. package/src/components/ui/alert/alert-title.svelte +0 -17
  108. package/src/components/ui/alert/alert.svelte +0 -44
  109. package/src/components/ui/alert/index.ts +0 -15
  110. package/src/components/ui/alert-dialog/alert-dialog-content.svelte +0 -29
  111. package/src/components/ui/alert-dialog/alert-dialog-description.svelte +0 -16
  112. package/src/components/ui/alert-dialog/alert-dialog-footer.svelte +0 -11
  113. package/src/components/ui/alert-dialog/alert-dialog-header.svelte +0 -11
  114. package/src/components/ui/alert-dialog/alert-dialog-overlay.svelte +0 -16
  115. package/src/components/ui/alert-dialog/alert-dialog-portal.svelte +0 -6
  116. package/src/components/ui/alert-dialog/alert-dialog-title.svelte +0 -16
  117. package/src/components/ui/alert-dialog/index.ts +0 -20
  118. package/src/components/ui/avatar/avatar.svelte +0 -63
  119. package/src/components/ui/avatar/index.ts +0 -2
  120. package/src/components/ui/badge/badge.svelte +0 -49
  121. package/src/components/ui/badge/index.ts +0 -3
  122. package/src/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +0 -18
  123. package/src/components/ui/breadcrumb/breadcrumb-item.svelte +0 -15
  124. package/src/components/ui/breadcrumb/breadcrumb-link.svelte +0 -15
  125. package/src/components/ui/breadcrumb/breadcrumb-list.svelte +0 -15
  126. package/src/components/ui/breadcrumb/breadcrumb-page.svelte +0 -18
  127. package/src/components/ui/breadcrumb/breadcrumb-separator.svelte +0 -22
  128. package/src/components/ui/breadcrumb/breadcrumb.svelte +0 -11
  129. package/src/components/ui/breadcrumb/index.ts +0 -7
  130. package/src/components/ui/button/button.svelte +0 -90
  131. package/src/components/ui/button/index.ts +0 -18
  132. package/src/components/ui/card/card-action.svelte +0 -23
  133. package/src/components/ui/card/card-content.svelte +0 -20
  134. package/src/components/ui/card/card-description.svelte +0 -20
  135. package/src/components/ui/card/card-footer.svelte +0 -20
  136. package/src/components/ui/card/card-header.svelte +0 -23
  137. package/src/components/ui/card/card-title.svelte +0 -15
  138. package/src/components/ui/card/card.svelte +0 -22
  139. package/src/components/ui/card/index.ts +0 -25
  140. package/src/components/ui/checkbox/checkbox.svelte +0 -39
  141. package/src/components/ui/checkbox/index.ts +0 -6
  142. package/src/components/ui/collapsible/index.ts +0 -7
  143. package/src/components/ui/command/command-dialog.svelte +0 -31
  144. package/src/components/ui/command/index.ts +0 -2
  145. package/src/components/ui/context-menu/index.ts +0 -11
  146. package/src/components/ui/dialog/dialog-close.svelte +0 -11
  147. package/src/components/ui/dialog/dialog-content.svelte +0 -48
  148. package/src/components/ui/dialog/dialog-description.svelte +0 -17
  149. package/src/components/ui/dialog/dialog-footer.svelte +0 -32
  150. package/src/components/ui/dialog/dialog-header.svelte +0 -20
  151. package/src/components/ui/dialog/dialog-overlay.svelte +0 -17
  152. package/src/components/ui/dialog/dialog-portal.svelte +0 -7
  153. package/src/components/ui/dialog/dialog-title.svelte +0 -17
  154. package/src/components/ui/dialog/dialog-trigger.svelte +0 -11
  155. package/src/components/ui/dialog/dialog.svelte +0 -7
  156. package/src/components/ui/dialog/index.ts +0 -34
  157. package/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +0 -43
  158. package/src/components/ui/dropdown-menu/dropdown-menu-content.svelte +0 -35
  159. package/src/components/ui/dropdown-menu/dropdown-menu-item.svelte +0 -36
  160. package/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte +0 -10
  161. package/src/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +0 -27
  162. package/src/components/ui/dropdown-menu/dropdown-menu.svelte +0 -45
  163. package/src/components/ui/dropdown-menu/index.ts +0 -21
  164. package/src/components/ui/input/index.ts +0 -7
  165. package/src/components/ui/input/input.svelte +0 -48
  166. package/src/components/ui/label/index.ts +0 -1
  167. package/src/components/ui/label/label.svelte +0 -18
  168. package/src/components/ui/pagination/index.ts +0 -7
  169. package/src/components/ui/pagination/pagination-content.svelte +0 -11
  170. package/src/components/ui/pagination/pagination-ellipsis.svelte +0 -12
  171. package/src/components/ui/pagination/pagination-item.svelte +0 -11
  172. package/src/components/ui/pagination/pagination-link.svelte +0 -26
  173. package/src/components/ui/pagination/pagination-next.svelte +0 -22
  174. package/src/components/ui/pagination/pagination-previous.svelte +0 -22
  175. package/src/components/ui/pagination/pagination.svelte +0 -11
  176. package/src/components/ui/popover/index.ts +0 -12
  177. package/src/components/ui/popover/popover-content.svelte +0 -25
  178. package/src/components/ui/progress/index.ts +0 -1
  179. package/src/components/ui/progress/progress.svelte +0 -22
  180. package/src/components/ui/scroll-area/index.ts +0 -1
  181. package/src/components/ui/scroll-area/scroll-area.svelte +0 -20
  182. package/src/components/ui/select/index.ts +0 -2
  183. package/src/components/ui/select/select.svelte +0 -36
  184. package/src/components/ui/separator/index.ts +0 -7
  185. package/src/components/ui/separator/separator.svelte +0 -23
  186. package/src/components/ui/sheet/index.ts +0 -16
  187. package/src/components/ui/sheet/sheet-content.svelte +0 -28
  188. package/src/components/ui/sheet/sheet-description.svelte +0 -22
  189. package/src/components/ui/sheet/sheet-footer.svelte +0 -22
  190. package/src/components/ui/sheet/sheet-header.svelte +0 -22
  191. package/src/components/ui/sheet/sheet-title.svelte +0 -22
  192. package/src/components/ui/sheet/sheet.svelte +0 -78
  193. package/src/components/ui/skeleton/index.ts +0 -2
  194. package/src/components/ui/skeleton/skeleton.svelte +0 -21
  195. package/src/components/ui/switch/index.ts +0 -2
  196. package/src/components/ui/switch/switch.svelte +0 -49
  197. package/src/components/ui/table/index.ts +0 -28
  198. package/src/components/ui/table/table-body.svelte +0 -15
  199. package/src/components/ui/table/table-caption.svelte +0 -20
  200. package/src/components/ui/table/table-cell.svelte +0 -15
  201. package/src/components/ui/table/table-footer.svelte +0 -20
  202. package/src/components/ui/table/table-head.svelte +0 -15
  203. package/src/components/ui/table/table-header.svelte +0 -20
  204. package/src/components/ui/table/table-row.svelte +0 -15
  205. package/src/components/ui/table/table.svelte +0 -17
  206. package/src/components/ui/tabs/index.ts +0 -15
  207. package/src/components/ui/tabs/tabs-content.svelte +0 -30
  208. package/src/components/ui/tabs/tabs-list.svelte +0 -26
  209. package/src/components/ui/tabs/tabs-trigger.svelte +0 -37
  210. package/src/components/ui/tabs/tabs.svelte +0 -27
  211. package/src/components/ui/textarea/index.ts +0 -2
  212. package/src/components/ui/textarea/textarea.svelte +0 -24
  213. package/src/components/ui/tooltip/index.ts +0 -19
  214. package/src/components/ui/tooltip/tooltip-content.svelte +0 -52
  215. package/src/components/ui/tooltip/tooltip-portal.svelte +0 -7
  216. package/src/components/ui/tooltip/tooltip-provider.svelte +0 -7
  217. package/src/components/ui/tooltip/tooltip-trigger.svelte +0 -7
  218. package/src/components/ui/tooltip/tooltip.svelte +0 -10
  219. package/src/editor-config.svelte.ts +0 -23
  220. package/src/index.ts +0 -155
  221. package/src/markdown-deps.d.ts +0 -21
  222. package/src/router-state.svelte.ts +0 -65
  223. package/src/types.ts +0 -33
  224. package/src/utils.ts +0 -28
@@ -1,183 +0,0 @@
1
- <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
3
- import { getTheme, setTheme, getResolvedTheme, colorThemes, getColorTheme, setColorTheme, getColorPresets } from '@svadmin/core';
4
- import { getLocale, setLocale, getAvailableLocales } from '@svadmin/core/i18n';
5
- import * as Card from './ui/card/index.js';
6
- import { Button } from './ui/button/index.js';
7
- import { Sun, Moon, Monitor, Check } from '@lucide/svelte';
8
-
9
- // Theme mode
10
- const themeOptions = [
11
- { value: 'light' as const, icon: Sun, label: 'settings.light' },
12
- { value: 'dark' as const, icon: Moon, label: 'settings.dark' },
13
- { value: 'system' as const, icon: Monitor, label: 'settings.systemTheme' },
14
- ];
15
-
16
- let currentTheme = $derived(getTheme());
17
- let currentColor = $derived(getColorTheme());
18
- let currentLocale = $derived(getLocale());
19
- const availableLocales = getAvailableLocales();
20
- const presets = getColorPresets();
21
-
22
- // Sidebar density
23
- const DENSITY_KEY = 'svadmin-sidebar-density';
24
- let density = $state<'compact' | 'standard'>(
25
- (typeof localStorage !== 'undefined' ? localStorage.getItem(DENSITY_KEY) : null) as 'compact' | 'standard' ?? 'standard'
26
- );
27
-
28
- function setDensity(d: 'compact' | 'standard') {
29
- density = d;
30
- if (typeof localStorage !== 'undefined') localStorage.setItem(DENSITY_KEY, d);
31
- }
32
-
33
- // Page size
34
- const PAGE_SIZE_KEY = 'svadmin-default-page-size';
35
- const pageSizeOptions = [10, 20, 50];
36
- let pageSize = $state<number>(
37
- typeof localStorage !== 'undefined' ? parseInt(localStorage.getItem(PAGE_SIZE_KEY) ?? '10', 10) : 10
38
- );
39
-
40
- function setPageSize(size: number) {
41
- pageSize = size;
42
- if (typeof localStorage !== 'undefined') localStorage.setItem(PAGE_SIZE_KEY, String(size));
43
- }
44
-
45
- // Locale display names
46
- const localeNames: Record<string, string> = {
47
- 'zh-CN': '中文(简体)',
48
- 'en': 'English',
49
- 'ja': '日本語',
50
- 'ko': '한국어',
51
- };
52
- </script>
53
-
54
- <div class="space-y-6">
55
- <div>
56
- <h2 class="text-xl font-semibold text-foreground">{t('settings.appearance')}</h2>
57
- <p class="text-sm text-muted-foreground mt-1">{t('settings.settingsDescription')}</p>
58
- </div>
59
-
60
- <!-- Theme Mode -->
61
- <Card.Card>
62
- <Card.CardHeader class="pb-3">
63
- <Card.CardTitle class="text-base">{t('settings.themeMode')}</Card.CardTitle>
64
- </Card.CardHeader>
65
- <Card.CardContent>
66
- <div class="grid grid-cols-3 gap-3">
67
- {#each themeOptions as opt}
68
- {@const active = currentTheme === opt.value}
69
- <button
70
- class="relative flex flex-col items-center gap-2 rounded-xl border-2 p-4 transition-all duration-200
71
- {active
72
- ? 'border-primary bg-primary/5 shadow-sm'
73
- : 'border-border hover:border-muted-foreground/30 hover:bg-accent/50'}"
74
- onclick={() => setTheme(opt.value)}
75
- >
76
- {#if active}
77
- <div class="absolute top-2 right-2">
78
- <Check class="h-3.5 w-3.5 text-primary" />
79
- </div>
80
- {/if}
81
- <div class="h-10 w-10 rounded-lg flex items-center justify-center
82
- {active ? 'bg-primary/10 text-primary' : 'bg-muted text-muted-foreground'}">
83
- <opt.icon class="h-5 w-5" />
84
- </div>
85
- <span class="text-xs font-medium {active ? 'text-primary' : 'text-muted-foreground'}">
86
- {t(opt.label)}
87
- </span>
88
- </button>
89
- {/each}
90
- </div>
91
- </Card.CardContent>
92
- </Card.Card>
93
-
94
- <!-- Color Accent -->
95
- <Card.Card>
96
- <Card.CardHeader class="pb-3">
97
- <Card.CardTitle class="text-base">{t('settings.colorAccent')}</Card.CardTitle>
98
- </Card.CardHeader>
99
- <Card.CardContent>
100
- <div class="flex flex-wrap gap-3">
101
- {#each presets as preset}
102
- {@const active = currentColor === preset.name}
103
- <button
104
- class="group relative h-9 w-9 rounded-full transition-all duration-200 hover:scale-110
105
- {active ? 'ring-2 ring-offset-2 ring-offset-background scale-110' : 'opacity-70 hover:opacity-100'}"
106
- style="background-color: {preset.color}; {active ? `--tw-ring-color: ${preset.color}` : ''}"
107
- onclick={() => setColorTheme(preset.name as Parameters<typeof setColorTheme>[0])}
108
- title={preset.label}
109
- >
110
- {#if active}
111
- <Check class="h-4 w-4 text-white absolute inset-0 m-auto drop-shadow-sm" />
112
- {/if}
113
- </button>
114
- {/each}
115
- </div>
116
- </Card.CardContent>
117
- </Card.Card>
118
-
119
- <!-- Interface -->
120
- <Card.Card>
121
- <Card.CardHeader class="pb-3">
122
- <Card.CardTitle class="text-base">{t('settings.interface')}</Card.CardTitle>
123
- </Card.CardHeader>
124
- <Card.CardContent class="space-y-5">
125
- <!-- Language -->
126
- <div class="flex items-center justify-between">
127
- <div>
128
- <p class="text-sm font-medium text-foreground">{t('settings.language')}</p>
129
- <p class="text-xs text-muted-foreground">{localeNames[currentLocale] ?? currentLocale}</p>
130
- </div>
131
- <select
132
- class="h-9 rounded-md border border-input bg-background px-3 text-sm ring-offset-background
133
- focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
134
- value={currentLocale}
135
- onchange={(e) => setLocale((e.target as HTMLSelectElement).value)}
136
- >
137
- {#each availableLocales as loc}
138
- <option value={loc}>{localeNames[loc] ?? loc}</option>
139
- {/each}
140
- </select>
141
- </div>
142
-
143
- <div class="h-px bg-border"></div>
144
-
145
- <!-- Sidebar Density -->
146
- <div class="flex items-center justify-between">
147
- <div>
148
- <p class="text-sm font-medium text-foreground">{t('settings.sidebarDensity')}</p>
149
- </div>
150
- <div class="flex rounded-lg border border-input overflow-hidden">
151
- <button
152
- class="px-3 py-1.5 text-xs font-medium transition-colors
153
- {density === 'compact' ? 'bg-primary text-primary-foreground' : 'bg-background text-muted-foreground hover:bg-accent'}"
154
- onclick={() => setDensity('compact')}
155
- >{t('settings.compact')}</button>
156
- <button
157
- class="px-3 py-1.5 text-xs font-medium transition-colors
158
- {density === 'standard' ? 'bg-primary text-primary-foreground' : 'bg-background text-muted-foreground hover:bg-accent'}"
159
- onclick={() => setDensity('standard')}
160
- >{t('settings.standard')}</button>
161
- </div>
162
- </div>
163
-
164
- <div class="h-px bg-border"></div>
165
-
166
- <!-- Default Page Size -->
167
- <div class="flex items-center justify-between">
168
- <div>
169
- <p class="text-sm font-medium text-foreground">{t('settings.defaultPageSize')}</p>
170
- </div>
171
- <div class="flex rounded-lg border border-input overflow-hidden">
172
- {#each pageSizeOptions as size}
173
- <button
174
- class="px-3 py-1.5 text-xs font-mono font-medium transition-colors
175
- {pageSize === size ? 'bg-primary text-primary-foreground' : 'bg-background text-muted-foreground hover:bg-accent'}"
176
- onclick={() => setPageSize(size)}
177
- >{size}</button>
178
- {/each}
179
- </div>
180
- </div>
181
- </Card.CardContent>
182
- </Card.Card>
183
- </div>
@@ -1,93 +0,0 @@
1
- <script lang="ts">
2
- import { getAuditLogProvider, t } from '@svadmin/core';
3
- import type { AuditEntry } from '@svadmin/core';
4
- import * as Sheet from './ui/sheet/index.js';
5
- import { History, Loader2 } from '@lucide/svelte';
6
-
7
- let { open = $bindable(false), resource, recordId } = $props<{
8
- open: boolean;
9
- resource: string;
10
- recordId?: string | number;
11
- }>();
12
-
13
- let logs = $state<AuditEntry[]>([]);
14
- let isLoading = $state(false);
15
-
16
- $effect(() => {
17
- if (open) {
18
- const provider = getAuditLogProvider();
19
- if (provider) {
20
- isLoading = true;
21
- provider.get({ resource, meta: recordId ? { recordId } : undefined })
22
- .then((data) => {
23
- logs = data;
24
- isLoading = false;
25
- })
26
- .catch((err) => {
27
- console.error('[svadmin] Failed to fetch audit logs', err);
28
- isLoading = false;
29
- });
30
- }
31
- }
32
- });
33
-
34
- // Action semantic color mapping
35
- function getActionColor(action: string) {
36
- switch (action) {
37
- case 'create': return 'bg-success';
38
- case 'update': return 'bg-info';
39
- case 'delete': return 'bg-destructive';
40
- default: return 'bg-muted-foreground';
41
- }
42
- }
43
- </script>
44
-
45
- <Sheet.Root bind:open>
46
- <Sheet.Content class="w-full sm:max-w-md overflow-y-auto">
47
- <Sheet.Header>
48
- <Sheet.Title class="flex items-center gap-2">
49
- <History class="h-5 w-5" />
50
- {t('common.history') || 'History'}
51
- </Sheet.Title>
52
- <Sheet.Description>
53
- Audit log and revision history for this record.
54
- </Sheet.Description>
55
- </Sheet.Header>
56
-
57
- <div class="mt-8 space-y-4">
58
- {#if isLoading}
59
- <div class="flex justify-center py-8 text-muted-foreground">
60
- <Loader2 class="h-6 w-6 animate-spin" />
61
- </div>
62
- {:else if logs.length === 0}
63
- <div class="text-center py-10">
64
- <History class="h-8 w-8 mx-auto text-muted-foreground/30 mb-3" />
65
- <p class="text-sm text-muted-foreground">
66
- {t('common.noData') || 'No audit logs found'}
67
- </p>
68
- </div>
69
- {:else}
70
- <div class="relative border-l border-border ml-3 pl-6 space-y-8">
71
- {#each logs as log}
72
- <div class="relative">
73
- <span class="absolute -left-7 top-1 flex h-2 w-2 items-center justify-center rounded-full ring-4 ring-background {getActionColor(log.action)}"></span>
74
- <div class="flex flex-col gap-1.5">
75
- <div class="flex items-center gap-2 justify-between">
76
- <span class="text-sm font-semibold capitalize">{log.action}</span>
77
- <span class="text-xs text-muted-foreground">
78
- {new Date(log.timestamp).toLocaleString()}
79
- </span>
80
- </div>
81
- {#if log.userId}
82
- <span class="text-xs text-muted-foreground font-mono bg-muted inline-flex px-1.5 py-0.5 rounded w-fit">
83
- User: {log.userId}
84
- </span>
85
- {/if}
86
- </div>
87
- </div>
88
- {/each}
89
- </div>
90
- {/if}
91
- </div>
92
- </Sheet.Content>
93
- </Sheet.Root>
@@ -1,226 +0,0 @@
1
- <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
3
- import { getAuthProvider } from '@svadmin/core';
4
- import type { AuditLog } from '@svadmin/core';
5
- import { toast } from '@svadmin/core/toast';
6
- import * as Table from './ui/table/index.js';
7
- import { Badge } from './ui/badge/index.js';
8
- import { Button } from './ui/button/index.js';
9
- import { Input } from './ui/input/index.js';
10
- import { Loader2, ChevronLeft, ChevronRight, Search, FileSearch, Eye, X } from '@lucide/svelte';
11
-
12
- const authProvider = getAuthProvider();
13
-
14
- let logs = $state<AuditLog[]>([]);
15
- let total = $state(0);
16
- let page = $state(1);
17
- let pageSize = 20;
18
- let loading = $state(true);
19
- let error = $state<string | null>(null);
20
- let searchQuery = $state('');
21
-
22
- // Snapshot drawer
23
- let drawerOpen = $state(false);
24
- let drawerLog = $state<AuditLog | null>(null);
25
-
26
- async function loadLogs() {
27
- if (!authProvider?.getAuditLogs) {
28
- error = t('settings.auditNotSupported') ?? 'Audit logs are not supported by the current AuthProvider';
29
- loading = false;
30
- return;
31
- }
32
- loading = true;
33
- try {
34
- const result = await authProvider.getAuditLogs({ page, pageSize });
35
- logs = result.data;
36
- total = result.total;
37
- } catch (e) {
38
- toast.error((e as Error).message);
39
- } finally {
40
- loading = false;
41
- }
42
- }
43
-
44
- $effect(() => {
45
- loadLogs();
46
- });
47
-
48
- let totalPages = $derived(Math.ceil(total / pageSize) || 1);
49
-
50
- let filteredLogs = $derived(
51
- searchQuery.trim()
52
- ? logs.filter(l =>
53
- (l.userName ?? '').toLowerCase().includes(searchQuery.toLowerCase()) ||
54
- l.action.toLowerCase().includes(searchQuery.toLowerCase()) ||
55
- (l.resource ?? '').toLowerCase().includes(searchQuery.toLowerCase())
56
- )
57
- : logs
58
- );
59
-
60
- function openSnapshot(log: AuditLog) {
61
- drawerLog = log;
62
- drawerOpen = true;
63
- }
64
-
65
- function formatDate(d: string | Date) {
66
- return new Date(d).toLocaleString();
67
- }
68
-
69
- function getActionColor(action: string): 'default' | 'secondary' | 'destructive' | 'outline' {
70
- const a = action.toLowerCase();
71
- if (a.includes('delete') || a.includes('remove')) return 'destructive';
72
- if (a.includes('create') || a.includes('add')) return 'default';
73
- if (a.includes('update') || a.includes('edit')) return 'secondary';
74
- return 'outline';
75
- }
76
- </script>
77
-
78
- <div class="h-full flex flex-col">
79
- <!-- Header -->
80
- <div class="mb-6">
81
- <h2 class="text-xl font-semibold text-foreground flex items-center gap-2">
82
- <FileSearch class="h-5 w-5 text-muted-foreground" />
83
- {t('settings.auditLogs') ?? 'Audit Logs'}
84
- </h2>
85
- <p class="text-sm text-muted-foreground mt-1">
86
- {t('settings.auditDescription') ?? 'Track and review all system operations for compliance and debugging.'}
87
- </p>
88
- </div>
89
-
90
- {#if error}
91
- <div class="p-6 border border-destructive/20 bg-destructive/5 text-destructive rounded-lg text-center">
92
- <p>{error}</p>
93
- </div>
94
- {:else}
95
- <!-- Search Bar -->
96
- <div class="mb-4 relative">
97
- <Search class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
98
- <Input
99
- bind:value={searchQuery}
100
- placeholder={t('settings.searchLogs') ?? 'Search by user, action, or resource...'}
101
- class="pl-9"
102
- />
103
- </div>
104
-
105
- <!-- Table -->
106
- <div class="flex-1 shadow-sm ring-1 ring-border/10 rounded-lg bg-card overflow-hidden">
107
- {#if loading}
108
- <div class="flex items-center justify-center h-48 text-muted-foreground">
109
- <Loader2 class="h-5 w-5 animate-spin mr-2" />
110
- {t('common.loading')}
111
- </div>
112
- {:else}
113
- <div class="overflow-auto">
114
- <Table.Root>
115
- <Table.Header>
116
- <Table.Row>
117
- <Table.Head class="w-[160px]">{t('settings.auditTime') ?? 'Time'}</Table.Head>
118
- <Table.Head>{t('settings.auditUser') ?? 'User'}</Table.Head>
119
- <Table.Head>{t('settings.auditAction') ?? 'Action'}</Table.Head>
120
- <Table.Head>{t('settings.auditResource') ?? 'Resource'}</Table.Head>
121
- <Table.Head class="w-[120px]">{t('settings.auditIp') ?? 'IP Address'}</Table.Head>
122
- <Table.Head class="w-[60px] text-center">{t('common.detail') ?? 'Detail'}</Table.Head>
123
- </Table.Row>
124
- </Table.Header>
125
- <Table.Body>
126
- {#each filteredLogs as log}
127
- <Table.Row class="border-0 even:bg-muted/20 hover:bg-muted/40 transition-colors">
128
- <Table.Cell class="text-xs text-muted-foreground font-mono">
129
- {formatDate(log.createdAt)}
130
- </Table.Cell>
131
- <Table.Cell class="text-sm font-medium">{log.userName ?? log.userId ?? '—'}</Table.Cell>
132
- <Table.Cell>
133
- <Badge variant={getActionColor(log.action)}>{log.action}</Badge>
134
- </Table.Cell>
135
- <Table.Cell class="text-sm text-muted-foreground">{log.resource ?? '—'}</Table.Cell>
136
- <Table.Cell class="text-xs text-muted-foreground font-mono">{log.ipAddress ?? '—'}</Table.Cell>
137
- <Table.Cell class="text-center">
138
- {#if log.details}
139
- <Button variant="ghost" size="icon" class="h-7 w-7" onclick={() => openSnapshot(log)}>
140
- <Eye class="h-3.5 w-3.5" />
141
- </Button>
142
- {/if}
143
- </Table.Cell>
144
- </Table.Row>
145
- {:else}
146
- <Table.Row>
147
- <Table.Cell colspan={6} class="h-24 text-center text-muted-foreground">
148
- {t('common.noData')}
149
- </Table.Cell>
150
- </Table.Row>
151
- {/each}
152
- </Table.Body>
153
- </Table.Root>
154
- </div>
155
- {/if}
156
- </div>
157
-
158
- <!-- Pagination -->
159
- <div class="mt-4 flex items-center justify-between text-sm text-muted-foreground">
160
- <span>{total} {t('settings.auditTotal') ?? 'total records'}</span>
161
- <div class="flex items-center gap-2">
162
- <Button
163
- variant="outline" size="sm"
164
- disabled={page <= 1}
165
- onclick={() => { page--; loadLogs(); }}
166
- >
167
- <ChevronLeft class="h-4 w-4" />
168
- </Button>
169
- <span>{page} / {totalPages}</span>
170
- <Button
171
- variant="outline" size="sm"
172
- disabled={page >= totalPages}
173
- onclick={() => { page++; loadLogs(); }}
174
- >
175
- <ChevronRight class="h-4 w-4" />
176
- </Button>
177
- </div>
178
- </div>
179
- {/if}
180
- </div>
181
-
182
- <!-- Snapshot Drawer -->
183
- {#if drawerOpen && drawerLog}
184
- <!-- svelte-ignore a11y_no_static_element_interactions -->
185
- <!-- svelte-ignore a11y_click_events_have_key_events -->
186
- <div class="fixed inset-0 z-[100] flex justify-end bg-black/40 backdrop-blur-sm" onclick={() => drawerOpen = false}>
187
- <!-- svelte-ignore a11y_no_static_element_interactions -->
188
- <!-- svelte-ignore a11y_click_events_have_key_events -->
189
- <div class="bg-card shadow-2xl w-full max-w-2xl h-full flex flex-col animate-in slide-in-from-right duration-300" onclick={(e) => e.stopPropagation()}>
190
- <!-- Drawer Header -->
191
- <div class="p-6 shadow-[0_1px_0_rgba(0,0,0,0.05)] dark:shadow-[0_1px_0_rgba(255,255,255,0.05)] flex items-center justify-between bg-muted/30">
192
- <div>
193
- <h2 class="text-lg font-bold text-foreground flex items-center gap-2">
194
- <FileSearch class="h-5 w-5 text-primary" />
195
- {t('settings.auditSnapshot') ?? 'Change Snapshot'}
196
- </h2>
197
- <p class="text-xs text-muted-foreground mt-1">{formatDate(drawerLog.createdAt)} · {drawerLog.userName ?? drawerLog.userId}</p>
198
- </div>
199
- <button onclick={() => drawerOpen = false} class="text-muted-foreground hover:text-foreground transition p-2 rounded-lg hover:bg-muted">
200
- <X class="w-5 h-5" />
201
- </button>
202
- </div>
203
-
204
- <!-- Meta Tags -->
205
- <div class="px-6 py-3 shadow-[0_1px_0_rgba(0,0,0,0.05)] dark:shadow-[0_1px_0_rgba(255,255,255,0.05)] flex items-center gap-2 flex-wrap">
206
- <Badge variant="outline">{drawerLog.action}</Badge>
207
- {#if drawerLog.resource}
208
- <Badge variant="secondary">{drawerLog.resource}</Badge>
209
- {/if}
210
- {#if drawerLog.ipAddress}
211
- <Badge variant="outline" class="font-mono text-xs">{drawerLog.ipAddress}</Badge>
212
- {/if}
213
- </div>
214
-
215
- <!-- JSON Diff Viewer -->
216
- <div class="flex-1 p-6 overflow-y-auto bg-foreground/[0.03] dark:bg-background/50">
217
- <pre class="whitespace-pre-wrap text-xs font-mono leading-relaxed text-foreground/80 bg-muted/30 rounded-lg p-4 border border-border">{typeof drawerLog.details === 'object' ? JSON.stringify(drawerLog.details, null, 2) : drawerLog.details}</pre>
218
- </div>
219
-
220
- <!-- Footer -->
221
- <div class="p-4 shadow-[0_-1px_0_rgba(0,0,0,0.05)] dark:shadow-[0_-1px_0_rgba(255,255,255,0.05)] bg-muted/20 text-right text-[11px] text-muted-foreground font-medium">
222
- System audit snapshot · Read-only
223
- </div>
224
- </div>
225
- </div>
226
- {/if}
@@ -1,51 +0,0 @@
1
- <script lang="ts">
2
- import { useIsAuthenticated } from '@svadmin/core';
3
- import type { Snippet } from 'svelte';
4
- import { navigate } from '@svadmin/core';
5
-
6
- let {
7
- children,
8
- fallback,
9
- loading,
10
- redirectTo = '/login',
11
- appendCurrentPathToQuery = true,
12
- } = $props<{
13
- children?: Snippet;
14
- fallback?: Snippet;
15
- loading?: Snippet;
16
- redirectTo?: string;
17
- appendCurrentPathToQuery?: boolean;
18
- }>();
19
-
20
- const isAuth = useIsAuthenticated();
21
-
22
- function triggerRedirect() {
23
- if (fallback) return;
24
- let url = redirectTo;
25
- if (appendCurrentPathToQuery && typeof window !== 'undefined') {
26
- const current = encodeURIComponent(window.location.pathname + window.location.search);
27
- url += `?to=${current}`;
28
- }
29
- navigate(url);
30
- }
31
-
32
- $effect(() => {
33
- if (!isAuth.isLoading && !isAuth.isAuthenticated) {
34
- triggerRedirect();
35
- }
36
- });
37
- </script>
38
-
39
- {#if isAuth.isLoading}
40
- {#if loading}
41
- {@render loading()}
42
- {/if}
43
- {:else if isAuth.isAuthenticated}
44
- {#if children}
45
- {@render children()}
46
- {/if}
47
- {:else}
48
- {#if fallback}
49
- {@render fallback()}
50
- {/if}
51
- {/if}