@svadmin/ui 0.29.0 → 0.29.2

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 -519
  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,357 +0,0 @@
1
- <script lang="ts" module>
2
- export type { FieldDefinition } from '@svadmin/core';
3
- </script>
4
-
5
- <script lang="ts">
6
- import type { FieldDefinition } from '@svadmin/core';
7
- import { t } from '@svadmin/core/i18n';
8
- import { Input } from './ui/input/index.js';
9
- import { Textarea } from './ui/textarea/index.js';
10
- import { Switch } from './ui/switch/index.js';
11
- import { Badge } from './ui/badge/index.js';
12
- import { Label } from './ui/label/index.js';
13
- import { Select } from './ui/select/index.js';
14
- import { Checkbox } from './ui/checkbox/index.js';
15
- import { Button } from './ui/button/index.js';
16
- import TooltipButton from './TooltipButton.svelte';
17
- import ComboboxField from './ComboboxField.svelte';
18
- import ArrayField from './fields/ArrayField.svelte';
19
- import { Plus, X } from '@lucide/svelte';
20
- import type { Snippet } from 'svelte';
21
- import { getRichTextEditor } from '../editor-config.svelte.js';
22
-
23
- let { field, value, onchange, children } = $props<{
24
- field: FieldDefinition;
25
- value: unknown;
26
- onchange: (val: unknown) => void;
27
- /** Optional custom renderer snippet */
28
- children?: Snippet;
29
- }>();
30
-
31
- // Typed accessors
32
- const strVal = $derived((value as string) ?? '');
33
- const numVal = $derived((value as number) ?? 0);
34
- const boolVal = $derived((value as boolean) ?? false);
35
- const tagsVal = $derived((value as string[]) ?? []);
36
- const multiVal = $derived((value as (string | number)[]) ?? []);
37
- const imagesVal = $derived((value as string[]) ?? []);
38
-
39
- function handleTagKeydown(e: KeyboardEvent) {
40
- if (e.key === 'Enter') {
41
- e.preventDefault();
42
- const input = e.target as HTMLInputElement;
43
- const tag = input.value.trim();
44
- if (tag) {
45
- onchange([...tagsVal, tag]);
46
- input.value = '';
47
- }
48
- }
49
- }
50
-
51
- function removeTag(index: number) {
52
- onchange(tagsVal.filter((_: string, i: number) => i !== index));
53
- }
54
-
55
- // Multiselect toggle
56
- function toggleMulti(optValue: string | number) {
57
- if (multiVal.includes(optValue)) {
58
- onchange(multiVal.filter((v: string | number) => v !== optValue));
59
- } else {
60
- onchange([...multiVal, optValue]);
61
- }
62
- }
63
-
64
- // Images management
65
- function addImage() {
66
- onchange([...imagesVal, '']);
67
- }
68
-
69
- function updateImage(index: number, url: string) {
70
- const next = [...imagesVal];
71
- next[index] = url;
72
- onchange(next);
73
- }
74
-
75
- function removeImage(index: number) {
76
- onchange(imagesVal.filter((_: string, i: number) => i !== index));
77
- }
78
- </script>
79
-
80
- <div class="space-y-1.5">
81
- <Label for={field.key} id="label-{field.key}">
82
- {field.label}
83
- {#if field.required}
84
- <span class="text-destructive">*</span>
85
- {/if}
86
- </Label>
87
-
88
- {#if children}
89
- {@render children()}
90
-
91
- {:else if field.type === 'array'}
92
- <ArrayField {field} {value} {onchange} />
93
-
94
- {:else if field.type === 'text' || field.type === 'image'}
95
- <Input
96
- id={field.key}
97
- type="text"
98
- value={strVal}
99
- oninput={(e) => onchange((e.target as HTMLInputElement).value)}
100
- required={field.required}
101
- placeholder={t('field.enterValue', { label: field.label })}
102
- />
103
-
104
- {:else if field.type === 'email'}
105
- <Input
106
- id={field.key}
107
- type="email"
108
- value={strVal}
109
- oninput={(e) => onchange((e.target as HTMLInputElement).value)}
110
- required={field.required}
111
- placeholder="name@example.com"
112
- />
113
-
114
- {:else if field.type === 'url'}
115
- <Input
116
- id={field.key}
117
- type="url"
118
- value={strVal}
119
- oninput={(e) => onchange((e.target as HTMLInputElement).value)}
120
- required={field.required}
121
- placeholder="https://"
122
- />
123
-
124
- {:else if field.type === 'phone'}
125
- <Input
126
- id={field.key}
127
- type="tel"
128
- value={strVal}
129
- oninput={(e) => onchange((e.target as HTMLInputElement).value)}
130
- required={field.required}
131
- placeholder="+1 (555) 000-0000"
132
- />
133
-
134
- {:else if field.type === 'color'}
135
- <div class="flex items-center gap-3">
136
- <input
137
- id={field.key}
138
- type="color"
139
- value={strVal || '#000000'}
140
- oninput={(e) => onchange((e.target as HTMLInputElement).value)}
141
- class="h-10 w-14 cursor-pointer rounded-md border border-input bg-background p-1"
142
- />
143
- <Input
144
- type="text"
145
- value={strVal}
146
- oninput={(e) => onchange((e.target as HTMLInputElement).value)}
147
- placeholder="#000000"
148
- class="max-w-32 font-mono text-sm"
149
- />
150
- {#if strVal}
151
- <span
152
- class="h-8 w-8 rounded-full border border-border shadow-sm"
153
- style="background-color: {strVal}"
154
- ></span>
155
- {/if}
156
- </div>
157
-
158
- {:else if field.type === 'number'}
159
- <Input
160
- id={field.key}
161
- type="number"
162
- value={String(numVal)}
163
- oninput={(e) => onchange(parseFloat((e.target as HTMLInputElement).value) || 0)}
164
- required={field.required}
165
- />
166
-
167
- {:else if field.type === 'richtext'}
168
- {@const EditorComp = getRichTextEditor()}
169
- {#if EditorComp}
170
- <EditorComp
171
- id={field.key}
172
- value={strVal}
173
- placeholder={t('field.enterValue', { label: field.label })}
174
- preset="full"
175
- onchange={(html: string) => onchange(html)}
176
- />
177
- {:else}
178
- <Textarea
179
- id={field.key}
180
- value={strVal}
181
- oninput={(e) => onchange((e.target as HTMLTextAreaElement).value)}
182
- required={field.required}
183
- rows={10}
184
- placeholder={t('field.enterValue', { label: field.label })}
185
- class="resize-y"
186
- />
187
- {/if}
188
-
189
- {:else if field.type === 'textarea'}
190
- <Textarea
191
- id={field.key}
192
- value={strVal}
193
- oninput={(e) => onchange((e.target as HTMLTextAreaElement).value)}
194
- required={field.required}
195
- rows={4}
196
- placeholder={t('field.enterValue', { label: field.label })}
197
- class="resize-y"
198
- />
199
-
200
- {:else if field.type === 'relation' && field.resource}
201
- <ComboboxField
202
- id={field.key}
203
- resource={field.resource}
204
- value={value as string | number | null}
205
- onchange={(v) => onchange(v)}
206
- optionLabel={field.optionLabel ?? 'title'}
207
- optionValue={field.optionValue ?? 'id'}
208
- placeholder={t('field.selectPlaceholder')}
209
- />
210
-
211
- {:else if field.type === 'select'}
212
- {#if (field.options?.length ?? 0) > 8}
213
- <!-- Many options → use ComboboxField-style search -->
214
- <ComboboxField
215
- id={field.key}
216
- resource=""
217
- value={value as string | number | null}
218
- onchange={(v) => onchange(v)}
219
- searchable={false}
220
- />
221
- {:else}
222
- <Select
223
- id={field.key}
224
- value={strVal}
225
- onchange={(e) => onchange((e.target as HTMLSelectElement).value)}
226
- required={field.required}
227
- placeholder={t('field.selectPlaceholder')}
228
- >
229
- {#each field.options ?? [] as opt}
230
- <option value={opt.value}>{opt.label}</option>
231
- {/each}
232
- </Select>
233
- {/if}
234
-
235
- {:else if field.type === 'multiselect'}
236
- <div
237
- class="space-y-2 rounded-lg border border-input p-3 max-h-48 overflow-y-auto"
238
- role="group"
239
- aria-labelledby="label-{field.key}"
240
- >
241
- {#each field.options ?? [] as opt}
242
- <label class="flex items-center gap-2 text-sm cursor-pointer hover:bg-muted/50 rounded px-1 py-0.5 transition-colors">
243
- <Checkbox
244
- id={`${field.key}-${opt.value}`}
245
- checked={multiVal.includes(opt.value)}
246
- onCheckedChange={() => toggleMulti(opt.value)}
247
- />
248
- {opt.label}
249
- </label>
250
- {/each}
251
- {#if !(field.options?.length)}
252
- <p class="text-xs text-muted-foreground">{t('field.noOptions')}</p>
253
- {/if}
254
- </div>
255
- {#if multiVal.length > 0}
256
- <div class="flex flex-wrap gap-1 mt-1">
257
- {#each multiVal as v}
258
- {@const label = field.options?.find((o: { label: string; value: string | number }) => o.value === v)?.label ?? String(v)}
259
- <Badge variant="secondary" class="gap-1">
260
- {label}
261
- <button type="button" onclick={() => toggleMulti(v)} class="ml-0.5 rounded-sm hover:text-destructive hover:bg-destructive/10 transition-colors" aria-label={t('common.clear')}>×</button>
262
- </Badge>
263
- {/each}
264
- </div>
265
- {/if}
266
-
267
- {:else if field.type === 'boolean'}
268
- <div class="flex items-center gap-2 pt-1">
269
- <Switch
270
- id={field.key}
271
- checked={boolVal}
272
- onCheckedChange={(v) => onchange(v)}
273
- />
274
- <span class="text-sm text-muted-foreground">{boolVal ? t('common.yes') : t('common.no')}</span>
275
- </div>
276
-
277
- {:else if field.type === 'tags'}
278
- <div class="space-y-2">
279
- <div class="flex flex-wrap gap-1.5">
280
- {#each tagsVal as tag, i}
281
- <Badge variant="secondary" class="gap-1">
282
- {tag}
283
- <button
284
- type="button"
285
- onclick={() => removeTag(i)}
286
- class="ml-0.5 rounded-sm hover:text-destructive hover:bg-destructive/10 transition-colors"
287
- aria-label={t('common.clear')}
288
- >×</button>
289
- </Badge>
290
- {/each}
291
- </div>
292
- <Input
293
- id={field.key}
294
- type="text"
295
- placeholder={t('field.tagsPlaceholder')}
296
- onkeydown={handleTagKeydown}
297
- />
298
- </div>
299
-
300
- {:else if field.type === 'date'}
301
- <Input
302
- id={field.key}
303
- type="date"
304
- value={strVal}
305
- oninput={(e) => onchange((e.target as HTMLInputElement).value)}
306
- required={field.required}
307
- />
308
-
309
- {:else if field.type === 'images'}
310
- <div class="space-y-2">
311
- {#each imagesVal as url, i}
312
- <div class="flex items-center gap-2">
313
- <Input
314
- id={i === 0 ? field.key : undefined}
315
- type="text"
316
- value={url}
317
- oninput={(e) => updateImage(i, (e.target as HTMLInputElement).value)}
318
- placeholder="https://example.com/image.jpg"
319
- class="flex-1"
320
- />
321
- {#if url}
322
- <img src={url} alt="preview" class="h-9 w-9 rounded object-cover border" />
323
- {/if}
324
- <TooltipButton tooltip={t('common.removeImage')} variant="ghost" size="icon" class="h-8 w-8 shrink-0" onclick={() => removeImage(i)}>
325
- <X class="h-3.5 w-3.5" />
326
- </TooltipButton>
327
- </div>
328
- {/each}
329
- <Button variant="outline" size="sm" type="button" onclick={addImage}>
330
- <Plus class="h-3.5 w-3.5 mr-1" /> {t('field.addImage')}
331
- </Button>
332
- </div>
333
-
334
- {:else if field.type === 'json'}
335
- <Textarea
336
- id={field.key}
337
- value={typeof value === 'string' ? strVal : JSON.stringify(value, null, 2)}
338
- oninput={(e) => {
339
- try {
340
- onchange(JSON.parse((e.target as HTMLTextAreaElement).value));
341
- } catch {
342
- // keep raw text until valid JSON
343
- }
344
- }}
345
- rows={6}
346
- class="resize-y font-mono text-xs"
347
- />
348
-
349
- {:else}
350
- <Input
351
- id={field.key}
352
- type="text"
353
- value={strVal}
354
- oninput={(e) => onchange((e.target as HTMLInputElement).value)}
355
- />
356
- {/if}
357
- </div>
@@ -1,112 +0,0 @@
1
- <script lang="ts">
2
- import { useForgotPassword } from '@svadmin/core';
3
- import { t } from '@svadmin/core/i18n';
4
- import { navigate } from '@svadmin/core/router';
5
- import { Button } from './ui/button/index.js';
6
- import { Input } from './ui/input/index.js';
7
- import { Label } from './ui/label/index.js';
8
- import * as Card from './ui/card/index.js';
9
- import * as Alert from './ui/alert/index.js';
10
- import { KeyRound, User, ArrowLeft, CheckCircle, Loader2, AlertCircle } from '@lucide/svelte';
11
-
12
- let { title = 'Admin' } = $props<{
13
- title?: string;
14
- }>();
15
-
16
- const forgot = useForgotPassword();
17
-
18
- let identifier = $state('');
19
- let error = $state('');
20
- let sent = $state(false);
21
-
22
- async function handleSubmit(e: Event) {
23
- e.preventDefault();
24
- error = '';
25
-
26
- if (!identifier) { error = t('auth.usernameOrEmailRequired'); return; }
27
-
28
- const result = await forgot.mutate({ email: identifier, username: identifier });
29
- if (result.success) {
30
- sent = true;
31
- } else {
32
- error = result.error?.message ?? t('common.operationFailed');
33
- }
34
- }
35
- </script>
36
-
37
- <div class="min-h-screen flex items-center justify-center bg-background p-4">
38
- <div class="w-full max-w-[380px]">
39
- <Card.Card>
40
- <Card.CardHeader class="text-center pb-2">
41
- <div class="inline-flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10 text-primary mx-auto mb-3">
42
- {#if sent}
43
- <CheckCircle class="h-5 w-5" />
44
- {:else}
45
- <KeyRound class="h-5 w-5" />
46
- {/if}
47
- </div>
48
- <Card.CardTitle class="text-xl">
49
- {sent ? t('auth.resetLinkSent') : t('auth.forgotPassword')}
50
- </Card.CardTitle>
51
- {#if !sent}
52
- <p class="text-sm text-muted-foreground">{t('auth.forgotPasswordDescription')}</p>
53
- {/if}
54
- </Card.CardHeader>
55
- <Card.CardContent>
56
- {#if sent}
57
- <div class="py-2">
58
- <p class="text-sm text-muted-foreground text-center mb-4">
59
- {t('auth.resetLinkSent')}
60
- </p>
61
- <Button variant="outline" class="w-full" onclick={() => navigate('/login')}>
62
- <ArrowLeft class="h-4 w-4 mr-2" />
63
- {t('auth.backToLogin')}
64
- </Button>
65
- </div>
66
- {:else}
67
- <form onsubmit={handleSubmit} class="space-y-4">
68
- {#if error}
69
- <Alert.Root variant="destructive">
70
- <AlertCircle class="h-4 w-4" />
71
- <Alert.Description>{error}</Alert.Description>
72
- </Alert.Root>
73
- {/if}
74
-
75
- <div class="space-y-2">
76
- <Label for="forgot-identifier">{t('auth.usernameOrEmail')}</Label>
77
- <div class="relative">
78
- <User class="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none z-[1]" />
79
- <Input
80
- id="forgot-identifier"
81
- type="text"
82
- placeholder={t('auth.identifierPlaceholder')}
83
- bind:value={identifier}
84
- class="pl-9"
85
- autocomplete="username"
86
- />
87
- </div>
88
- </div>
89
-
90
- <Button type="submit" class="w-full" disabled={forgot.isLoading}>
91
- {#if forgot.isLoading}
92
- <Loader2 class="h-4 w-4 animate-spin mr-2" />
93
- {/if}
94
- {t('auth.sendResetLink')}
95
- </Button>
96
- </form>
97
-
98
- <div class="flex items-center justify-center mt-5 pt-5 border-t">
99
- <Button variant="link" class="text-sm h-auto p-0 font-medium inline-flex items-center gap-1" onclick={() => navigate('/login')}>
100
- <ArrowLeft class="h-3 w-3" />
101
- {t('auth.backToLogin')}
102
- </Button>
103
- </div>
104
- {/if}
105
- </Card.CardContent>
106
- </Card.Card>
107
-
108
- <p class="text-xs text-muted-foreground mt-4 text-center opacity-60">
109
- Powered by {title}
110
- </p>
111
- </div>
112
- </div>
@@ -1,94 +0,0 @@
1
- <script lang="ts">
2
- import { Moon, Sun, Search, MonitorUp } from '@lucide/svelte';
3
- import { Button } from './ui/button';
4
- import TooltipButton from './TooltipButton.svelte';
5
- import { getResolvedTheme, toggleTheme } from '@svadmin/core';
6
- import Breadcrumbs from './Breadcrumbs.svelte';
7
- import { t } from '@svadmin/core/i18n';
8
- import { getComponentRegistry } from '../component-registry.svelte.js';
9
-
10
- let {
11
- showThemeToggle = false,
12
- showBreadcrumbs = true,
13
- showSearch = true,
14
- onSearchClick,
15
- children,
16
- /** Slot for additional actions on the right side of the header */
17
- rightActions,
18
- siteUrl,
19
- } = $props<{
20
- showThemeToggle?: boolean;
21
- showBreadcrumbs?: boolean;
22
- showSearch?: boolean;
23
- onSearchClick?: () => void;
24
- children?: import('svelte').Snippet;
25
- rightActions?: import('svelte').Snippet;
26
- siteUrl?: string;
27
- }>();
28
-
29
- // Retrieve optional component overrides from registry
30
- const registry = getComponentRegistry();
31
- const CustomBreadcrumbs = registry.Breadcrumbs;
32
- const CustomThemeToggle = registry.ThemeToggle;
33
- const CustomUserMenu = registry.UserMenu;
34
- const CustomNotificationPanel = registry.NotificationPanel;
35
- </script>
36
-
37
- <header class="sticky top-0 z-30 flex h-14 w-full shrink-0 items-center justify-between bg-card/60 backdrop-blur-md px-4 md:px-6">
38
- <div class="flex items-center gap-4">
39
- {#if children}
40
- {@render children()}
41
- {/if}
42
- {#if showBreadcrumbs}
43
- {#if CustomBreadcrumbs}
44
- <CustomBreadcrumbs />
45
- {:else}
46
- <Breadcrumbs />
47
- {/if}
48
- {/if}
49
- </div>
50
- <div class="ml-auto flex items-center gap-2">
51
- {#if rightActions}
52
- {@render rightActions()}
53
- {/if}
54
-
55
- {#if siteUrl}
56
- <TooltipButton tooltip={t('common.goToSite') || 'Site'} href={siteUrl} target="_blank" rel="noopener noreferrer" class="rounded-full flex items-center justify-center">
57
- <MonitorUp class="h-4 w-4 text-muted-foreground transition-colors hover:text-foreground" />
58
- </TooltipButton>
59
- {/if}
60
-
61
- {#if CustomNotificationPanel}
62
- <CustomNotificationPanel />
63
- {/if}
64
-
65
- {#if showSearch && onSearchClick}
66
- <Button variant="outline" size="sm" onclick={onSearchClick} class="gap-2 text-muted-foreground h-8 px-3">
67
- <Search class="h-3.5 w-3.5" />
68
- <span class="hidden sm:inline text-xs">Search...</span>
69
- <kbd class="hidden sm:inline-flex items-center gap-0.5 rounded border bg-muted px-1.5 py-0.5 text-[10px] font-medium font-mono text-muted-foreground">
70
- <span class="text-xs">⌘</span>K
71
- </kbd>
72
- </Button>
73
- {/if}
74
-
75
- {#if showThemeToggle}
76
- {#if CustomThemeToggle}
77
- <CustomThemeToggle />
78
- {:else}
79
- <TooltipButton tooltip={t('common.toggleTheme')} onclick={() => toggleTheme()} class="rounded-full">
80
- {#if getResolvedTheme() === 'dark'}
81
- <Moon class="h-4 w-4 transition-all" />
82
- {:else}
83
- <Sun class="h-4 w-4 transition-all" />
84
- {/if}
85
- </TooltipButton>
86
- {/if}
87
- {/if}
88
-
89
- {#if CustomUserMenu}
90
- <CustomUserMenu />
91
- {/if}
92
- </div>
93
- </header>
94
-