@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,129 +0,0 @@
1
- <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
3
- import { useSelect } from '@svadmin/core';
4
- import type { Filter } from '@svadmin/core';
5
- import { Button } from './ui/button/index.js';
6
- import TooltipButton from './TooltipButton.svelte';
7
- import { Badge } from './ui/badge/index.js';
8
- import { Command } from 'cmdk-sv';
9
- import { Skeleton } from './ui/skeleton/index.js';
10
- import { ChevronsUpDown, Check, X, Search } from '@lucide/svelte';
11
-
12
- interface Props {
13
- resource: string;
14
- value?: string | number | null;
15
- onchange?: (value: string | number | null) => void;
16
- optionLabel?: string;
17
- optionValue?: string;
18
- placeholder?: string;
19
- searchable?: boolean;
20
- onSearch?: (value: string) => Filter[];
21
- id?: string;
22
- class?: string;
23
- [key: string]: any;
24
- }
25
-
26
- let {
27
- resource,
28
- value = null,
29
- onchange,
30
- optionLabel = 'title',
31
- optionValue = 'id',
32
- placeholder = 'Select...',
33
- searchable = true,
34
- onSearch,
35
- ...restProps
36
- }: Props = $props();
37
-
38
- const { options, query, onSearchChange } = useSelect({
39
- get resource() { return resource; },
40
- get optionLabel() { return optionLabel; },
41
- get optionValue() { return optionValue; },
42
- get onSearch() { return onSearch ?? (searchable ? (v: string) => v ? [{ field: optionLabel, operator: 'contains' as const, value: v }] : [] : undefined); },
43
- });
44
-
45
- let open = $state(false);
46
- let searchInputValue = $state('');
47
-
48
- const selectedLabel = $derived(
49
- (options as { label: string; value: string | number }[])?.find(
50
- (o: { label: string; value: string | number }) => o.value === value
51
- )?.label ?? ''
52
- );
53
-
54
- function handleSelect(optValue: string | number) {
55
- onchange?.(optValue === value ? null : optValue);
56
- open = false;
57
- searchInputValue = '';
58
- }
59
-
60
- function handleClear(e: Event) {
61
- e.stopPropagation();
62
- onchange?.(null);
63
- }
64
-
65
- function handleSearchInput(e: Event) {
66
- const v = (e.currentTarget as HTMLInputElement).value;
67
- searchInputValue = v;
68
- onSearchChange(v);
69
- }
70
- </script>
71
-
72
- <div class="relative">
73
- <Button
74
- variant="outline"
75
- type="button"
76
- class="w-full justify-between font-normal"
77
- onclick={() => { open = !open; }}
78
- {...restProps}
79
- >
80
- {#if selectedLabel}
81
- <span class="truncate">{selectedLabel}</span>
82
- <TooltipButton tooltip={t('common.clear')} size="icon-sm" class="h-5 w-5 ml-1" onclick={handleClear}>
83
- <X class="h-3 w-3" />
84
- </TooltipButton>
85
- {:else}
86
- <span class="text-muted-foreground">{placeholder}</span>
87
- {/if}
88
- <ChevronsUpDown class="ml-auto h-4 w-4 shrink-0 opacity-50" />
89
- </Button>
90
-
91
- {#if open}
92
- <div class="absolute z-50 mt-1 w-full rounded-md border bg-popover shadow-lg">
93
- <Command.Root class="flex flex-col overflow-hidden rounded-md bg-popover text-popover-foreground">
94
- {#if searchable}
95
- <div class="flex items-center border-b px-2">
96
- <Search class="mr-1.5 h-3.5 w-3.5 shrink-0 opacity-50" />
97
- <Command.Input
98
- bind:value={searchInputValue}
99
- oninput={handleSearchInput}
100
- placeholder="Search..."
101
- class="flex h-9 w-full bg-transparent py-2 text-sm outline-none placeholder:text-muted-foreground"
102
- />
103
- </div>
104
- {/if}
105
- <Command.List class="max-h-48 overflow-y-auto p-1">
106
- <Command.Empty class="py-4 text-center text-xs text-muted-foreground">
107
- No results.
108
- </Command.Empty>
109
- {#if query.isLoading}
110
- {#each Array(3) as _}
111
- <div class="px-2 py-1.5"><Skeleton class="h-5 w-full" /></div>
112
- {/each}
113
- {:else}
114
- {#each (options as { label: string; value: string | number }[]) ?? [] as opt}
115
- <Command.Item
116
- value={opt.label}
117
- onSelect={() => handleSelect(opt.value)}
118
- class="relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground"
119
- >
120
- <Check class="mr-2 h-3.5 w-3.5 {value === opt.value ? 'opacity-100' : 'opacity-0'}" />
121
- {opt.label}
122
- </Command.Item>
123
- {/each}
124
- {/if}
125
- </Command.List>
126
- </Command.Root>
127
- </div>
128
- {/if}
129
- </div>
@@ -1,135 +0,0 @@
1
- <script lang="ts">
2
- import { getResources, getAgentProvider, getChatProvider } from '@svadmin/core';
3
- import { navigate } from '@svadmin/core/router';
4
- import { t } from '@svadmin/core/i18n';
5
- import { toggleTheme } from '@svadmin/core';
6
- import { Command } from 'cmdk-sv';
7
- import * as Dialog from './ui/dialog/index.js';
8
- import { Search, LayoutDashboard, Plus, Sun, FileText, Bot, Sparkles } from '@lucide/svelte';
9
-
10
- interface Props {
11
- open?: boolean;
12
- /** Callback fired when user selects "Ask AI" with a query string */
13
- onAskAI?: (query: string) => void;
14
- }
15
-
16
- let { open = $bindable(false), onAskAI }: Props = $props();
17
- let searchValue = $state('');
18
-
19
- const resources = getResources();
20
- const hasAI = $derived(!!(getAgentProvider() || getChatProvider()));
21
-
22
- function close() {
23
- open = false;
24
- searchValue = '';
25
- }
26
-
27
- function act(fn: () => void) {
28
- fn();
29
- close();
30
- }
31
-
32
- function askAI() {
33
- const query = searchValue.trim();
34
- if (onAskAI && query) {
35
- onAskAI(query);
36
- }
37
- close();
38
- }
39
-
40
- const itemClass = 'relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50';
41
- </script>
42
-
43
- <Dialog.Dialog bind:open>
44
- <Dialog.DialogContent class="overflow-hidden p-0 sm:max-w-[560px]">
45
- <Command.Root class="flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground [&_[data-cmdk-group-heading]]:text-muted-foreground [&_[data-cmdk-group-heading]]:px-2 [&_[data-cmdk-group-heading]]:py-1.5 [&_[data-cmdk-group-heading]]:text-xs [&_[data-cmdk-group-heading]]:font-semibold [&_[data-cmdk-group-heading]]:uppercase [&_[data-cmdk-group-heading]]:tracking-wider">
46
- <div class="flex items-center border-b px-3" data-cmdk-input-wrapper="">
47
- <Search class="mr-2 h-4 w-4 shrink-0 opacity-50" />
48
- <Command.Input
49
- bind:value={searchValue}
50
- placeholder={hasAI ? (t('commandPalette.searchOrAsk') || 'Search or ask AI...') : t('common.search')}
51
- class="flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
52
- />
53
- <kbd class="ml-2 text-[10px] font-mono font-medium text-muted-foreground bg-muted px-1.5 py-0.5 rounded border border-border">ESC</kbd>
54
- </div>
55
- <Command.List class="max-h-[320px] overflow-y-auto overflow-x-hidden p-1">
56
- <Command.Empty class="py-6 text-center text-sm text-muted-foreground">
57
- {#if hasAI && searchValue.trim()}
58
- <button
59
- class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md bg-primary/10 text-primary hover:bg-primary/20 transition-colors cursor-pointer text-sm font-medium"
60
- onclick={askAI}
61
- >
62
- <Sparkles class="h-3.5 w-3.5" />
63
- {t('commandPalette.askAI') || 'Ask AI'}: "{searchValue.trim()}"
64
- </button>
65
- {:else}
66
- {t('common.noData')}
67
- {/if}
68
- </Command.Empty>
69
-
70
- <!-- AI -->
71
- {#if hasAI && searchValue.trim()}
72
- <Command.Group heading="AI">
73
- <Command.Item
74
- value="ask-ai-{searchValue}"
75
- onSelect={askAI}
76
- class={itemClass}
77
- >
78
- <Sparkles class="h-4 w-4 text-primary" />
79
- <span class="text-primary font-medium">{t('commandPalette.askAI') || 'Ask AI'}</span>
80
- <span class="text-muted-foreground truncate ml-1">"{searchValue.trim()}"</span>
81
- </Command.Item>
82
- </Command.Group>
83
- <Command.Separator class="mx-1 my-1 h-px bg-border" />
84
- {/if}
85
-
86
- <!-- Navigation -->
87
- <Command.Group heading={t('common.home')}>
88
- <Command.Item
89
- value="home"
90
- onSelect={() => act(() => navigate('/'))}
91
- class={itemClass}
92
- >
93
- <LayoutDashboard class="h-4 w-4 text-muted-foreground" />
94
- {t('common.home')}
95
- </Command.Item>
96
- {#each resources as r}
97
- <Command.Item
98
- value={r.name}
99
- onSelect={() => act(() => navigate(`/${r.name}`))}
100
- class={itemClass}
101
- >
102
- <FileText class="h-4 w-4 text-muted-foreground" />
103
- {r.label}
104
- </Command.Item>
105
- {/each}
106
- </Command.Group>
107
-
108
- <Command.Separator class="mx-1 my-1 h-px bg-border" />
109
-
110
- <!-- Actions -->
111
- <Command.Group heading={t('common.actions')}>
112
- {#each resources as r}
113
- <Command.Item
114
- value="create-{r.name}"
115
- onSelect={() => act(() => navigate(`/${r.name}/create`))}
116
- class={itemClass}
117
- >
118
- <Plus class="h-4 w-4 text-muted-foreground" />
119
- {t('common.create')} {r.label}
120
- </Command.Item>
121
- {/each}
122
- <Command.Item
123
- value="toggle-theme"
124
- onSelect={() => act(() => toggleTheme())}
125
- class={itemClass}
126
- >
127
- <Sun class="h-4 w-4 text-muted-foreground" />
128
- {t('common.toggleTheme')}
129
- </Command.Item>
130
- </Command.Group>
131
- </Command.List>
132
- </Command.Root>
133
- </Dialog.DialogContent>
134
- </Dialog.Dialog>
135
-
@@ -1,126 +0,0 @@
1
- <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
3
- import { AlertTriangle, Copy, CheckCircle } from '@lucide/svelte';
4
- import { Button } from './ui/button/index.js';
5
- import TooltipButton from './TooltipButton.svelte';
6
- import * as Card from './ui/card/index.js';
7
-
8
- let { title, missingVars = [], envTemplate = '' } = $props<{
9
- title?: string;
10
- missingVars?: { key: string; description?: string }[];
11
- envTemplate?: string;
12
- }>();
13
-
14
- const displayTitle = $derived(title ?? t('config.missingEnvTitle'));
15
-
16
- let copied = $state<Record<string, boolean>>({});
17
- let copyTimers = $state<Record<string, ReturnType<typeof setTimeout>>>({});
18
-
19
- function setCopied(key: string) {
20
- copied = { ...copied, [key]: true };
21
- // Clear previous timer for this key if any
22
- if (copyTimers[key]) clearTimeout(copyTimers[key]);
23
- const timer = setTimeout(() => {
24
- copied = { ...copied, [key]: false };
25
- }, 2000);
26
- copyTimers = { ...copyTimers, [key]: timer };
27
- }
28
-
29
- // Cleanup all timers on destroy
30
- $effect(() => {
31
- return () => {
32
- Object.values(copyTimers).forEach(clearTimeout);
33
- };
34
- });
35
-
36
- async function copyToClipboard(text: string, key: string) {
37
- try {
38
- await navigator.clipboard.writeText(text);
39
- setCopied(key);
40
- } catch {
41
- console.warn('[svadmin] clipboard API unavailable');
42
- }
43
- }
44
-
45
- async function copyAll() {
46
- const text = envTemplate || missingVars.map((v: { key: string }) => `${v.key}=`).join('\n');
47
- await copyToClipboard(text, '__all__');
48
- }
49
- </script>
50
-
51
- <div class="min-h-screen flex items-center justify-center bg-background p-4">
52
- <div class="w-full max-w-[480px]">
53
- <Card.Card>
54
- <Card.CardHeader class="text-center pb-2">
55
- <div class="inline-flex items-center justify-center w-12 h-12 rounded-lg bg-destructive/10 text-destructive mx-auto mb-3">
56
- <AlertTriangle class="h-6 w-6" />
57
- </div>
58
- <Card.CardTitle class="text-xl">{displayTitle}</Card.CardTitle>
59
- <p class="text-sm text-muted-foreground">
60
- {t('config.missingEnvDescription')}
61
- </p>
62
- </Card.CardHeader>
63
- <Card.CardContent class="space-y-4">
64
- {#if missingVars.length > 0}
65
- <div class="rounded-lg border overflow-hidden">
66
- {#each missingVars as v, i (v.key)}
67
- <div class="flex items-center justify-between px-3 py-2.5 gap-2 {i < missingVars.length - 1 ? 'border-b border-border/50' : ''}">
68
- <div class="flex flex-col gap-0.5 min-w-0">
69
- <code class="text-[0.8125rem] font-semibold text-foreground font-mono">{v.key}</code>
70
- {#if v.description}
71
- <span class="text-[0.6875rem] text-muted-foreground">{v.description}</span>
72
- {/if}
73
- </div>
74
- <TooltipButton
75
- tooltip={t('common.copy')}
76
- variant="ghost"
77
- size="icon"
78
- class="h-7 w-7 shrink-0"
79
- onclick={() => copyToClipboard(`${v.key}=`, v.key)}
80
- >
81
- {#if copied[v.key]}
82
- <CheckCircle class="h-3.5 w-3.5 text-green-500" />
83
- {:else}
84
- <Copy class="h-3.5 w-3.5" />
85
- {/if}
86
- </TooltipButton>
87
- </div>
88
- {/each}
89
- </div>
90
- {/if}
91
-
92
- {#if envTemplate}
93
- <div class="rounded-lg border overflow-hidden">
94
- <div class="flex items-center justify-between px-3 py-2 bg-muted/50 border-b border-border/50">
95
- <span class="text-xs font-medium text-muted-foreground">{t('config.envFilePath')}</span>
96
- <TooltipButton
97
- tooltip={t('common.copyAll')}
98
- variant="ghost"
99
- size="sm"
100
- class="h-7 gap-1"
101
- onclick={copyAll}
102
- >
103
- {#if copied['__all__']}
104
- <CheckCircle class="h-3.5 w-3.5 text-green-500" />
105
- <span class="text-xs">{t('common.copied')}</span>
106
- {:else}
107
- <Copy class="h-3.5 w-3.5" />
108
- <span class="text-xs">{t('common.copyAll')}</span>
109
- {/if}
110
- </TooltipButton>
111
- </div>
112
- <pre class="px-3 py-3 text-xs font-mono leading-relaxed text-foreground bg-muted/20 m-0 whitespace-pre-wrap break-all">{envTemplate}</pre>
113
- </div>
114
- {/if}
115
-
116
- <p class="text-xs text-muted-foreground text-center mt-4">
117
- {t('config.reload')}
118
- </p>
119
-
120
- <Button variant="outline" class="w-full" onclick={() => window.location.reload()}>
121
- {t('config.reloadButton')}
122
- </Button>
123
- </Card.CardContent>
124
- </Card.Card>
125
- </div>
126
- </div>
@@ -1,53 +0,0 @@
1
- <script lang="ts">
2
- import * as AlertDialog from './ui/alert-dialog/index.js';
3
- import { Button } from './ui/button/index.js';
4
- import { t } from '@svadmin/core/i18n';
5
-
6
- let { open = $bindable(false), title, message = '',
7
- confirmText, cancelText, variant = 'danger',
8
- onconfirm, oncancel } = $props<{
9
- open?: boolean;
10
- title?: string;
11
- message?: string;
12
- confirmText?: string;
13
- cancelText?: string;
14
- variant?: 'danger' | 'warning' | 'info';
15
- onconfirm: () => void;
16
- oncancel: () => void;
17
- }>();
18
-
19
- const variantMap = {
20
- danger: 'destructive' as const,
21
- warning: 'destructive' as const,
22
- info: 'default' as const,
23
- };
24
-
25
- const resolvedTitle = $derived(title ?? t('common.confirmAction'));
26
- const resolvedConfirmText = $derived(confirmText ?? t('common.confirm'));
27
- const resolvedCancelText = $derived(cancelText ?? t('common.cancel'));
28
- </script>
29
-
30
- <AlertDialog.Root bind:open onOpenChange={(v: boolean) => { if (!v) oncancel(); }}>
31
- <AlertDialog.Content>
32
- <AlertDialog.Header>
33
- <AlertDialog.Title>{resolvedTitle}</AlertDialog.Title>
34
- <AlertDialog.Description>{message}</AlertDialog.Description>
35
- </AlertDialog.Header>
36
- <AlertDialog.Footer>
37
- <AlertDialog.Cancel>
38
- {#snippet child({ props })}
39
- <Button variant="outline" {...props} onclick={oncancel}>
40
- {resolvedCancelText}
41
- </Button>
42
- {/snippet}
43
- </AlertDialog.Cancel>
44
- <AlertDialog.Action>
45
- {#snippet child({ props })}
46
- <Button variant={variantMap[variant as keyof typeof variantMap]} {...props} onclick={onconfirm}>
47
- {resolvedConfirmText}
48
- </Button>
49
- {/snippet}
50
- </AlertDialog.Action>
51
- </AlertDialog.Footer>
52
- </AlertDialog.Content>
53
- </AlertDialog.Root>
@@ -1,163 +0,0 @@
1
- <script lang="ts">
2
- import { getChatProvider, getChatContext } from '@svadmin/core';
3
- import { Sparkles, X, ChevronRight, Check, RefreshCcw } from '@lucide/svelte';
4
- import { fade, slide } from 'svelte/transition';
5
- import { Button } from './ui/button/index.js';
6
- import MarkdownRenderer from './MarkdownRenderer.svelte';
7
- import TooltipButton from './TooltipButton.svelte';
8
-
9
- let { open = $bindable(false) } = $props<{ open?: boolean }>();
10
-
11
- const provider = $derived(getChatProvider());
12
- const ctx = $derived(getChatContext());
13
-
14
- let isPredicting = $state(false);
15
- let insightText = $state('');
16
- let abortController: AbortController | null = null;
17
- let hasFetchedForContext = $state('');
18
-
19
- // Automatically request insights when the context changes
20
- $effect(() => {
21
- if (!open || !provider) return;
22
-
23
- // Create a context hash to avoid refetching for the same page
24
- const currentHash = `${ctx.currentResource}-${ctx.currentView}-${ctx.selectedRecordId}`;
25
- if (hasFetchedForContext === currentHash) return;
26
-
27
- hasFetchedForContext = currentHash;
28
- getInsights();
29
- });
30
-
31
- async function getInsights() {
32
- if (!provider) return;
33
-
34
- isPredicting = true;
35
- insightText = '';
36
-
37
- if (abortController) abortController.abort();
38
- abortController = new AbortController();
39
-
40
- const prompt = `Analyze the current context and provide 3 quick insights or context-aware suggestions for the admin user.
41
- Resource: ${ctx.currentResource}
42
- View: ${ctx.currentView}
43
- Record ID: ${ctx.selectedRecordId || 'None'}
44
- Keep it concise, professional, and use bullet points.`;
45
-
46
- try {
47
- const result = provider.sendMessage(
48
- [{ id: 'insights', role: 'system', content: prompt, timestamp: Date.now() }],
49
- { signal: abortController.signal }
50
- );
51
-
52
- if (Symbol.asyncIterator in result) {
53
- for await (const chunk of result as AsyncIterable<string>) {
54
- insightText += chunk;
55
- }
56
- } else {
57
- insightText = await result;
58
- }
59
- } catch (err: unknown) {
60
- if (err instanceof Error && err.name !== 'AbortError') {
61
- insightText = 'Failed to generate insights for this context.';
62
- }
63
- } finally {
64
- isPredicting = false;
65
- }
66
- }
67
- </script>
68
-
69
- {#if open}
70
- <div
71
- class="fixed right-0 top-14 bottom-0 w-80 bg-card border-l shadow-2xl z-40 flex flex-col"
72
- transition:slide={{ axis: 'x', duration: 300 }}
73
- >
74
- <!-- Header -->
75
- <div class="h-14 flex items-center justify-between px-4 border-b bg-muted/20">
76
- <div class="flex items-center gap-2 font-medium">
77
- <Sparkles class="h-4 w-4 text-primary" />
78
- Copilot Sidebar
79
- </div>
80
- <TooltipButton tooltip="Close panel" variant="ghost" size="icon-sm" onclick={() => open = false}>
81
- <X class="h-4 w-4" />
82
- </TooltipButton>
83
- </div>
84
-
85
- <!-- Context Meta -->
86
- <div class="px-4 py-3 border-b bg-muted/10 text-xs text-muted-foreground flex flex-col gap-1">
87
- <div class="flex items-center gap-2">
88
- <span class="w-16">Resource:</span>
89
- <span class="font-mono text-foreground">{ctx.currentResource || 'Dashboard'}</span>
90
- </div>
91
- <div class="flex items-center gap-2">
92
- <span class="w-16">View:</span>
93
- <span class="font-mono text-foreground capitalize">{ctx.currentView || 'List'}</span>
94
- </div>
95
- {#if ctx.selectedRecordId}
96
- <div class="flex items-center gap-2">
97
- <span class="w-16">Record ID:</span>
98
- <span class="font-mono text-foreground">{ctx.selectedRecordId}</span>
99
- </div>
100
- {/if}
101
- </div>
102
-
103
- <!-- Content Area -->
104
- <div class="flex-1 overflow-y-auto p-4 space-y-6">
105
-
106
- <!-- Smart Insights Section -->
107
- <div>
108
- <h4 class="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-3 flex items-center justify-between">
109
- Smart Insights
110
- {#if isPredicting}
111
- <span class="flex items-center gap-1 text-[10px] text-primary normal-case font-medium">
112
- <div class="h-1 w-1 bg-primary rounded-full animate-bounce delay-75"></div>
113
- <div class="h-1 w-1 bg-primary rounded-full animate-bounce delay-150"></div>
114
- <div class="h-1 w-1 bg-primary rounded-full animate-bounce delay-300"></div>
115
- </span>
116
- {:else}
117
- <Button variant="ghost" size="icon-sm" class="h-5 w-5 hover:bg-muted" onclick={getInsights}>
118
- <RefreshCcw class="h-3 w-3" />
119
- </Button>
120
- {/if}
121
- </h4>
122
-
123
- <div class="text-sm">
124
- {#if !insightText && !isPredicting}
125
- <div class="text-muted-foreground italic text-center py-4 bg-muted/30 rounded-lg border border-dashed">
126
- No insights loaded.
127
- </div>
128
- {:else}
129
- <div class="bg-primary/5 p-3 rounded-xl border border-primary/10 prose prose-sm dark:prose-invert prose-p:my-1 prose-ul:my-1 prose-li:my-0.5">
130
- <MarkdownRenderer content={insightText} streaming={isPredicting} />
131
- </div>
132
- {/if}
133
- </div>
134
- </div>
135
-
136
- <!-- Quick Actions (Example static definitions for the copilot) -->
137
- <div>
138
- <h4 class="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-3">
139
- Suggested Actions
140
- </h4>
141
- <div class="space-y-2">
142
- {#if ctx.currentView === 'list'}
143
- <button class="w-full flex items-center gap-2 text-left p-2 rounded-lg hover:bg-muted border border-transparent hover:border-border transition-colors text-sm">
144
- <div class="bg-background rounded shadow-sm p-1"><Check class="h-3.5 w-3.5 text-muted-foreground" /></div>
145
- <span>Generate monthly report</span>
146
- </button>
147
- <button class="w-full flex items-center gap-2 text-left p-2 rounded-lg hover:bg-muted border border-transparent hover:border-border transition-colors text-sm">
148
- <div class="bg-background rounded shadow-sm p-1"><Check class="h-3.5 w-3.5 text-muted-foreground" /></div>
149
- <span>Find anomalies in {ctx.currentResource}</span>
150
- </button>
151
- {:else if ctx.currentView === 'edit' || ctx.currentView === 'create'}
152
- <button class="w-full flex items-center gap-2 text-left p-2 rounded-lg hover:bg-muted border border-transparent hover:border-border transition-colors text-sm">
153
- <div class="bg-background rounded shadow-sm p-1"><Check class="h-3.5 w-3.5 text-muted-foreground" /></div>
154
- <span>Auto-fill form from context</span>
155
- </button>
156
- {:else}
157
- <div class="text-muted-foreground text-sm py-2 text-center">No precise actions for this view</div>
158
- {/if}
159
- </div>
160
- </div>
161
- </div>
162
- </div>
163
- {/if}
@@ -1,39 +0,0 @@
1
- <script lang="ts">
2
- import { getResource } from '@svadmin/core';
3
- import { navigate } from '@svadmin/core/router';
4
- import { t } from '@svadmin/core/i18n';
5
- import type { Snippet } from 'svelte';
6
- import PageHeader from './PageHeader.svelte';
7
- import AutoForm from './AutoForm.svelte';
8
- import ListButton from './buttons/ListButton.svelte';
9
-
10
- interface Props {
11
- resourceName: string;
12
- title?: string;
13
- headerActions?: Snippet;
14
- class?: string;
15
- }
16
-
17
- let {
18
- resourceName,
19
- title,
20
- headerActions,
21
- class: className = '',
22
- }: Props = $props();
23
-
24
- const resource = $derived(getResource(resourceName));
25
- const pageTitle = $derived(title ?? `${t('common.create')}${resource.label}`);
26
- </script>
27
-
28
- <div class="space-y-6 {className}">
29
- <PageHeader title={pageTitle}>
30
- {#snippet actions()}
31
- <ListButton resource={resourceName} hideText />
32
- {#if headerActions}
33
- {@render headerActions()}
34
- {/if}
35
- {/snippet}
36
- </PageHeader>
37
-
38
- <AutoForm {resourceName} mode="create" />
39
- </div>