@zveltio/components 1.0.0

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 (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/admin/ColumnList/ColumnList.svelte +68 -0
  4. package/admin/ColumnList/component.json +13 -0
  5. package/admin/MetadataSettings/MetadataSettings.svelte +62 -0
  6. package/admin/MetadataSettings/component.json +9 -0
  7. package/admin/RelationshipManager/RelationshipManager.svelte +86 -0
  8. package/admin/RelationshipManager/component.json +10 -0
  9. package/admin/RoleManager/RoleManager.svelte +167 -0
  10. package/admin/RoleManager/component.json +17 -0
  11. package/admin/TableDesigner/TableDesigner.svelte +37 -0
  12. package/admin/TableDesigner/component.json +14 -0
  13. package/ai/AIFeedback/AIFeedback.svelte +43 -0
  14. package/ai/AIInsightWidget/AIInsightWidget.svelte +56 -0
  15. package/ai/AIQueryBuilder/AIQueryBuilder.svelte +91 -0
  16. package/ai/Omnisearch/Omnisearch.svelte +112 -0
  17. package/ai/VoiceSearch/VoiceSearch.svelte +73 -0
  18. package/attachments/AttachmentManager/AttachmentManager.svelte +175 -0
  19. package/attachments/AttachmentManager/component.json +21 -0
  20. package/charts/SimpleBarChart/SimpleBarChart.svelte +28 -0
  21. package/charts/SimpleBarChart/component.json +14 -0
  22. package/common/AddressInput/AddressInput.svelte +88 -0
  23. package/common/AddressInput/component.json +12 -0
  24. package/common/Alert/Alert.svelte +20 -0
  25. package/common/Alert/component.json +15 -0
  26. package/common/Button/Button.svelte +28 -0
  27. package/common/Button/component.json +16 -0
  28. package/common/Card/Card.svelte +25 -0
  29. package/common/Card/component.json +13 -0
  30. package/common/DynamicDataTable/DynamicDataTable.svelte +84 -0
  31. package/common/DynamicDataTable/component.json +14 -0
  32. package/common/Input/Input.svelte +21 -0
  33. package/common/Input/component.json +16 -0
  34. package/common/Loading/Loading.svelte +12 -0
  35. package/common/Loading/component.json +12 -0
  36. package/common/Modal/Modal.svelte +31 -0
  37. package/common/Modal/component.json +14 -0
  38. package/common/Pagination/Pagination.svelte +40 -0
  39. package/common/Pagination/component.json +1 -0
  40. package/common/PermissionGuard/PermissionGuard.svelte +53 -0
  41. package/common/PermissionGuard/component.json +21 -0
  42. package/common/SearchableSelect/SearchableSelect.svelte +136 -0
  43. package/common/SearchableSelect/component.json +17 -0
  44. package/common/StatusBadge/StatusBadge.svelte +22 -0
  45. package/common/StatusBadge/component.json +1 -0
  46. package/dashboard/RecentActivity/RecentActivity.svelte +70 -0
  47. package/dashboard/RecentActivity/component.json +1 -0
  48. package/forms/FormField/FormField.svelte +39 -0
  49. package/forms/FormField/component.json +17 -0
  50. package/navigation/NavGroup/NavGroup.svelte +40 -0
  51. package/navigation/NavGroup/component.json +1 -0
  52. package/navigation/NavLink/NavLink.svelte +18 -0
  53. package/navigation/NavLink/component.json +15 -0
  54. package/navigation/SmartNavbar/SmartNavbar.svelte +184 -0
  55. package/navigation/SmartNavbar/component.json +20 -0
  56. package/package.json +53 -0
  57. package/pages/Settings/Settings.svelte +154 -0
  58. package/pages/Settings/component.json +11 -0
  59. package/src/lib/index.ts +53 -0
  60. package/views/ListView/ListView.svelte +19 -0
  61. package/views/ListView/component.json +13 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 liviumarianiordache
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Svelto Components - Final Clean
2
+
3
+ **84 componente** | Un singur navbar: SmartNavbar
4
+
5
+ ## Navigation
6
+
7
+ **SmartNavbar** - navbar universal cu toate funcțiile:
8
+ - Employee UI (ex-IntranetNavbar logic)
9
+ - Partners UI (ex-PartnersNavbar logic)
10
+ - Public/Guest UI (ex-PublicNavbar logic)
11
+ - User menu integrat (ex-UserDropdown)
12
+ - Poziție configurabilă: top/bottom/left/right
13
+ - Theme toggle integrat
14
+ - Mobile responsive
15
+ - Nested navigation
16
+ - Role filtering
17
+
18
+ **Navigation (3):**
19
+ - SmartNavbar ← folosește DOAR pe acesta
20
+ - NavLink
21
+ - NavGroup
22
+
23
+ ## Layout (5)
24
+ Header, Sidebar, Navbar, Breadcrumbs, PageLayout
25
+
26
+ ## Common (34)
27
+ Button, Input, Select, Modal, Card, Loading, Alert, Avatar, Badge, Checkbox, Divider, Drawer, DynamicDataTable, Pagination, PermissionGuard, SearchableSelect, SecureField, StatusBadge, Toaster, Tooltip, TrustBadge, ConfirmationDialog, EmptyState, LoadingSkeleton, PendingIndicator, TeamChat, Stat, Indicator, MapView, AddressInput, ExportActions, ThemeToggle, ViewSwitcher, WorkflowActions
28
+
29
+ ## Alte Categorii
30
+ Admin (9) | AI (5) | Views (16) | Charts (2) | Dashboard (2) | Forms (1) | Reports (3) | Attachments (1) | Audit (2) | Pages (1)
31
+
32
+ ## Features
33
+ - Svelte 5 Runes
34
+ - Props-based
35
+ - DaisyUI/Tailwind
36
+ - Zero duplicate navbars
37
+
38
+ ## License
39
+ MIT
@@ -0,0 +1,68 @@
1
+ <script lang="ts">
2
+ /**
3
+ * ColumnList - Table columns editor
4
+ */
5
+ interface Column {
6
+ name: string;
7
+ type: string;
8
+ nullable: boolean;
9
+ default_value?: string;
10
+ is_primary?: boolean;
11
+ is_unique?: boolean;
12
+ }
13
+
14
+ let {
15
+ columns = $bindable<Column[]>([]),
16
+ dataTypes = ['text', 'varchar', 'integer', 'bigint', 'decimal', 'boolean', 'date', 'timestamp', 'uuid', 'jsonb']
17
+ }: {
18
+ columns?: Column[];
19
+ dataTypes?: string[];
20
+ } = $props();
21
+
22
+ function addColumn() {
23
+ columns = [...columns, { name: '', type: 'text', nullable: true }];
24
+ }
25
+
26
+ function removeColumn(index: number) {
27
+ columns = columns.filter((_, i) => i !== index);
28
+ }
29
+ </script>
30
+
31
+ <div>
32
+ <div class="flex items-center justify-between mb-3">
33
+ <h3 class="font-semibold">Columns</h3>
34
+ <button class="btn btn-sm btn-primary" onclick={addColumn}>+ Add Column</button>
35
+ </div>
36
+
37
+ <div class="space-y-2">
38
+ {#each columns as col, i}
39
+ <div class="grid grid-cols-12 gap-2 items-center p-3 bg-base-200 rounded-lg">
40
+ <input
41
+ type="text"
42
+ bind:value={col.name}
43
+ placeholder="name"
44
+ class="col-span-4 input input-sm input-bordered font-mono"
45
+ />
46
+ <select bind:value={col.type} class="col-span-3 select select-sm select-bordered">
47
+ {#each dataTypes as t}
48
+ <option value={t}>{t}</option>
49
+ {/each}
50
+ </select>
51
+ <div class="col-span-4 flex gap-2 text-xs">
52
+ <label class="flex items-center gap-1 cursor-pointer">
53
+ <input type="checkbox" bind:checked={col.nullable} class="checkbox checkbox-xs" />
54
+ Null
55
+ </label>
56
+ <label class="flex items-center gap-1 cursor-pointer">
57
+ <input type="checkbox" bind:checked={col.is_unique} class="checkbox checkbox-xs" />
58
+ Unique
59
+ </label>
60
+ </div>
61
+ <button class="col-span-1 btn btn-xs btn-ghost text-error" onclick={() => removeColumn(i)}>✕</button>
62
+ </div>
63
+ {/each}
64
+ {#if columns.length === 0}
65
+ <div class="text-center py-4 text-sm opacity-50">No columns</div>
66
+ {/if}
67
+ </div>
68
+ </div>
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "ColumnList",
3
+ "category": "admin",
4
+ "description": "Editable list of table columns",
5
+ "dependencies": {
6
+ "packages": [],
7
+ "components": []
8
+ },
9
+ "props": {
10
+ "columns": "$bindable<Column[]>",
11
+ "dataTypes": "string[] - Available data types"
12
+ }
13
+ }
@@ -0,0 +1,62 @@
1
+ <script lang="ts">
2
+ /**
3
+ * MetadataSettings - Table metadata editor (display name, menu group, audit)
4
+ */
5
+ interface Metadata {
6
+ displayName?: string;
7
+ menuGroup?: string;
8
+ isLoggable?: boolean;
9
+ icon?: string;
10
+ }
11
+
12
+ let {
13
+ metadata = $bindable<Metadata>({})
14
+ }: {
15
+ metadata?: Metadata;
16
+ } = $props();
17
+ </script>
18
+
19
+ <div class="space-y-4 p-4 bg-base-200 rounded-lg">
20
+ <h4 class="font-semibold">Table Metadata</h4>
21
+
22
+ <div class="form-control">
23
+ <label class="label"><span class="label-text-alt">Display Name</span></label>
24
+ <input
25
+ type="text"
26
+ bind:value={metadata.displayName}
27
+ placeholder="User Profiles"
28
+ class="input input-sm input-bordered"
29
+ />
30
+ </div>
31
+
32
+ <div class="form-control">
33
+ <label class="label"><span class="label-text-alt">Menu Group</span></label>
34
+ <input
35
+ type="text"
36
+ bind:value={metadata.menuGroup}
37
+ placeholder="Administration"
38
+ class="input input-sm input-bordered"
39
+ />
40
+ </div>
41
+
42
+ <div class="form-control">
43
+ <label class="label"><span class="label-text-alt">Icon</span></label>
44
+ <input
45
+ type="text"
46
+ bind:value={metadata.icon}
47
+ placeholder="Users"
48
+ class="input input-sm input-bordered"
49
+ />
50
+ </div>
51
+
52
+ <div class="form-control">
53
+ <label class="label cursor-pointer justify-start gap-2">
54
+ <input
55
+ type="checkbox"
56
+ bind:checked={metadata.isLoggable}
57
+ class="toggle toggle-sm toggle-primary"
58
+ />
59
+ <span class="label-text">Enable Audit Log</span>
60
+ </label>
61
+ </div>
62
+ </div>
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "MetadataSettings",
3
+ "category": "admin",
4
+ "description": "Table metadata editor (display name, menu group, audit logging)",
5
+ "dependencies": {"packages": [], "components": []},
6
+ "props": {
7
+ "metadata": "$bindable<Metadata> - {displayName?, menuGroup?, isLoggable?, icon?}"
8
+ }
9
+ }
@@ -0,0 +1,86 @@
1
+ <script lang="ts">
2
+ /**
3
+ * RelationshipManager - Foreign key relationship viewer
4
+ */
5
+ interface Constraint {
6
+ constraint_type: string;
7
+ constraint_name: string;
8
+ column_name?: string;
9
+ foreign_table?: string;
10
+ foreign_column?: string;
11
+ definition?: string;
12
+ }
13
+
14
+ let {
15
+ tableName,
16
+ constraints = []
17
+ }: {
18
+ tableName: string;
19
+ constraints: Constraint[];
20
+ } = $props();
21
+
22
+ const foreignKeys = $derived(constraints.filter(c => c.constraint_type === 'FOREIGN KEY'));
23
+ const uniqueConstraints = $derived(constraints.filter(c => c.constraint_type === 'UNIQUE'));
24
+ const checkConstraints = $derived(constraints.filter(c => c.constraint_type === 'CHECK'));
25
+ </script>
26
+
27
+ <div class="space-y-4">
28
+ <div class="flex items-center gap-2">
29
+ <h3 class="font-semibold">Relationships & Constraints</h3>
30
+ <span class="badge badge-sm">{constraints.length}</span>
31
+ </div>
32
+
33
+ <div class="alert alert-info">
34
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
35
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
36
+ </svg>
37
+ <span class="text-sm">Viewing existing constraints. Full editor coming soon.</span>
38
+ </div>
39
+
40
+ {#if foreignKeys.length > 0}
41
+ <div>
42
+ <h4 class="text-xs uppercase font-bold opacity-50 mb-2">Foreign Keys ({foreignKeys.length})</h4>
43
+ <div class="space-y-2">
44
+ {#each foreignKeys as fk}
45
+ <div class="p-3 bg-base-200 rounded-lg">
46
+ <div class="font-mono text-sm font-bold">{fk.constraint_name}</div>
47
+ {#if fk.definition}
48
+ <div class="text-xs opacity-60 mt-1">{fk.definition}</div>
49
+ {/if}
50
+ </div>
51
+ {/each}
52
+ </div>
53
+ </div>
54
+ {/if}
55
+
56
+ {#if uniqueConstraints.length > 0}
57
+ <div>
58
+ <h4 class="text-xs uppercase font-bold opacity-50 mb-2">Unique Constraints ({uniqueConstraints.length})</h4>
59
+ <div class="space-y-2">
60
+ {#each uniqueConstraints as uc}
61
+ <div class="p-3 bg-base-200 rounded-lg">
62
+ <div class="font-mono text-sm">{uc.constraint_name}</div>
63
+ </div>
64
+ {/each}
65
+ </div>
66
+ </div>
67
+ {/if}
68
+
69
+ {#if checkConstraints.length > 0}
70
+ <div>
71
+ <h4 class="text-xs uppercase font-bold opacity-50 mb-2">Check Constraints ({checkConstraints.length})</h4>
72
+ <div class="space-y-2">
73
+ {#each checkConstraints as cc}
74
+ <div class="p-3 bg-base-200 rounded-lg">
75
+ <div class="font-mono text-sm">{cc.constraint_name}</div>
76
+ {#if cc.definition}<div class="text-xs opacity-60 mt-1">{cc.definition}</div>{/if}
77
+ </div>
78
+ {/each}
79
+ </div>
80
+ </div>
81
+ {/if}
82
+
83
+ {#if constraints.length === 0}
84
+ <div class="text-center py-8 text-sm opacity-50">No constraints defined</div>
85
+ {/if}
86
+ </div>
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "RelationshipManager",
3
+ "category": "admin",
4
+ "description": "Foreign key and constraint viewer",
5
+ "dependencies": {"packages": [], "components": []},
6
+ "props": {
7
+ "tableName": "string",
8
+ "constraints": "Constraint[]"
9
+ }
10
+ }
@@ -0,0 +1,167 @@
1
+ <script lang="ts">
2
+ /**
3
+ * RoleManager - RBAC role management UI
4
+ * Standalone, callback-based
5
+ */
6
+
7
+ interface Role {
8
+ id: string;
9
+ name: string;
10
+ description: string;
11
+ allowed_for: 'INTERNAL' | 'PARTNER';
12
+ parent_role_id?: string | null;
13
+ }
14
+
15
+ let {
16
+ roles = [],
17
+ onSave,
18
+ onDelete
19
+ }: {
20
+ roles: Role[];
21
+ onSave: (role: Omit<Role, 'id'> | Role) => Promise<void>;
22
+ onDelete: (roleId: string) => Promise<void>;
23
+ } = $props();
24
+
25
+ let isModalOpen = $state(false);
26
+ let editingRole = $state<Partial<Role> | null>(null);
27
+ let saving = $state(false);
28
+
29
+ function openNew() {
30
+ editingRole = {
31
+ name: '',
32
+ description: '',
33
+ allowed_for: 'INTERNAL',
34
+ parent_role_id: null
35
+ };
36
+ isModalOpen = true;
37
+ }
38
+
39
+ function openEdit(role: Role) {
40
+ editingRole = { ...role };
41
+ isModalOpen = true;
42
+ }
43
+
44
+ async function handleSave() {
45
+ if (!editingRole) return;
46
+ saving = true;
47
+ try {
48
+ await onSave(editingRole as Role);
49
+ isModalOpen = false;
50
+ editingRole = null;
51
+ } finally {
52
+ saving = false;
53
+ }
54
+ }
55
+
56
+ async function handleDelete(roleId: string) {
57
+ if (!confirm('Delete this role?')) return;
58
+ await onDelete(roleId);
59
+ }
60
+ </script>
61
+
62
+ <div class="p-4">
63
+ <div class="flex items-center justify-between mb-4">
64
+ <h2 class="text-2xl font-bold">Roles</h2>
65
+ <button class="btn btn-primary" onclick={openNew}>
66
+ + New Role
67
+ </button>
68
+ </div>
69
+
70
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
71
+ {#each roles as role}
72
+ <div class="card bg-base-100 border border-base-200 shadow-sm hover:shadow-md transition">
73
+ <div class="card-body p-4">
74
+ <div class="flex items-start justify-between">
75
+ <div class="flex items-center gap-2">
76
+ <div class="avatar placeholder">
77
+ <div class="w-10 rounded-full bg-primary text-primary-content">
78
+ <span class="text-sm">{role.name.substring(0, 2).toUpperCase()}</span>
79
+ </div>
80
+ </div>
81
+ <div>
82
+ <h3 class="font-bold">{role.name}</h3>
83
+ <span class="badge badge-xs {role.allowed_for === 'INTERNAL' ? 'badge-primary' : 'badge-secondary'}">
84
+ {role.allowed_for}
85
+ </span>
86
+ </div>
87
+ </div>
88
+ <div class="dropdown dropdown-end">
89
+ <button tabindex="0" class="btn btn-ghost btn-xs btn-square">
90
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
91
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z" />
92
+ </svg>
93
+ </button>
94
+ <ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-32">
95
+ <li><button onclick={() => openEdit(role)}>Edit</button></li>
96
+ <li><button class="text-error" onclick={() => handleDelete(role.id)}>Delete</button></li>
97
+ </ul>
98
+ </div>
99
+ </div>
100
+
101
+ <p class="text-sm opacity-70 mt-2">{role.description || '—'}</p>
102
+
103
+ {#if role.parent_role_id}
104
+ <div class="text-xs text-info mt-2">
105
+ Parent: {roles.find(r => r.id === role.parent_role_id)?.name || 'Unknown'}
106
+ </div>
107
+ {/if}
108
+ </div>
109
+ </div>
110
+ {/each}
111
+ </div>
112
+ </div>
113
+
114
+ <!-- Modal -->
115
+ {#if isModalOpen && editingRole}
116
+ <dialog class="modal modal-open">
117
+ <div class="modal-box">
118
+ <h3 class="font-bold text-lg mb-4">{editingRole.id ? 'Edit' : 'Create'} Role</h3>
119
+
120
+ <div class="space-y-3">
121
+ <div class="form-control">
122
+ <label class="label"><span class="label-text">Name</span></label>
123
+ <input
124
+ type="text"
125
+ bind:value={editingRole.name}
126
+ class="input input-bordered"
127
+ placeholder="Administrator"
128
+ />
129
+ </div>
130
+
131
+ <div class="form-control">
132
+ <label class="label"><span class="label-text">Type</span></label>
133
+ <select bind:value={editingRole.allowed_for} class="select select-bordered">
134
+ <option value="INTERNAL">Internal (Employee)</option>
135
+ <option value="PARTNER">Partner (External)</option>
136
+ </select>
137
+ </div>
138
+
139
+ <div class="form-control">
140
+ <label class="label"><span class="label-text">Parent Role</span></label>
141
+ <select bind:value={editingRole.parent_role_id} class="select select-bordered">
142
+ <option value={null}>— None (Top Level) —</option>
143
+ {#each roles.filter(r => r.id !== editingRole?.id) as r}
144
+ <option value={r.id}>{r.name}</option>
145
+ {/each}
146
+ </select>
147
+ </div>
148
+
149
+ <div class="form-control">
150
+ <label class="label"><span class="label-text">Description</span></label>
151
+ <textarea
152
+ bind:value={editingRole.description}
153
+ class="textarea textarea-bordered"
154
+ placeholder="Role description..."
155
+ ></textarea>
156
+ </div>
157
+ </div>
158
+
159
+ <div class="modal-action">
160
+ <button class="btn" onclick={() => isModalOpen = false} disabled={saving}>Cancel</button>
161
+ <button class="btn btn-primary" onclick={handleSave} disabled={saving}>
162
+ {#if saving}Saving...{:else}Save{/if}
163
+ </button>
164
+ </div>
165
+ </div>
166
+ </dialog>
167
+ {/if}
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "RoleManager",
3
+ "category": "admin",
4
+ "description": "RBAC role management with hierarchy support",
5
+ "dependencies": {
6
+ "packages": [],
7
+ "components": []
8
+ },
9
+ "props": {
10
+ "roles": "Role[] - List of roles",
11
+ "onSave": "(role: Role) => Promise<void> - Save callback",
12
+ "onDelete": "(roleId: string) => Promise<void> - Delete callback"
13
+ },
14
+ "types": {
15
+ "Role": "{id: string; name: string; description: string; allowed_for: 'INTERNAL' | 'PARTNER'; parent_role_id?: string | null}"
16
+ }
17
+ }
@@ -0,0 +1,37 @@
1
+ <script lang="ts">
2
+ let {
3
+ tableName = $bindable(''),
4
+ columns = $bindable([]),
5
+ onSave
6
+ }: {
7
+ tableName?: string;
8
+ columns?: any[];
9
+ onSave: (data: any) => Promise<void>;
10
+ } = $props();
11
+
12
+ function addColumn() {
13
+ columns = [...columns, { name: '', type: 'text', nullable: true }];
14
+ }
15
+ </script>
16
+
17
+ <div class="space-y-4">
18
+ <input bind:value={tableName} placeholder="Table Name" class="input input-bordered w-full" />
19
+
20
+ <div class="space-y-2">
21
+ {#each columns as col, i}
22
+ <div class="flex gap-2">
23
+ <input bind:value={col.name} placeholder="Column Name" class="input input-bordered flex-1" />
24
+ <select bind:value={col.type} class="select select-bordered">
25
+ <option>text</option>
26
+ <option>number</option>
27
+ <option>boolean</option>
28
+ <option>date</option>
29
+ </select>
30
+ <button class="btn btn-error btn-square" onclick={() => columns = columns.filter((_, idx) => idx !== i)}>✕</button>
31
+ </div>
32
+ {/each}
33
+ </div>
34
+
35
+ <button class="btn btn-primary" onclick={addColumn}>+ Add Column</button>
36
+ <button class="btn btn-success" onclick={() => onSave({ tableName, columns })}>Save</button>
37
+ </div>
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "TableDesigner",
3
+ "category": "admin",
4
+ "description": "TableDesigner - Svelte 5 component",
5
+ "dependencies": {
6
+ "packages": [],
7
+ "components": []
8
+ },
9
+ "props": {
10
+ "tableName": "$bindable<string>",
11
+ "columns": "$bindable<Array>",
12
+ "onSave": "function"
13
+ }
14
+ }
@@ -0,0 +1,43 @@
1
+ <script lang="ts">
2
+ let {
3
+ onFeedback
4
+ }: {
5
+ onFeedback: (type: 'positive' | 'negative', comment?: string) => Promise<void>;
6
+ } = $props();
7
+
8
+ let showComment = $state(false);
9
+ let comment = $state('');
10
+ let submitted = $state(false);
11
+
12
+ async function handleFeedback(type: 'positive' | 'negative') {
13
+ await onFeedback(type, comment || undefined);
14
+ submitted = true;
15
+ setTimeout(() => {
16
+ submitted = false;
17
+ showComment = false;
18
+ comment = '';
19
+ }, 2000);
20
+ }
21
+ </script>
22
+
23
+ <div class="flex items-center gap-2">
24
+ {#if !submitted}
25
+ <span class="text-xs opacity-60">Was this helpful?</span>
26
+ <button class="btn btn-xs btn-ghost" onclick={() => handleFeedback('positive')} title="Helpful">👍</button>
27
+ <button class="btn btn-xs btn-ghost" onclick={() => { showComment = true; }} title="Not helpful">👎</button>
28
+
29
+ {#if showComment}
30
+ <div class="flex gap-1">
31
+ <input
32
+ type="text"
33
+ bind:value={comment}
34
+ placeholder="Optional feedback..."
35
+ class="input input-xs input-bordered w-40"
36
+ />
37
+ <button class="btn btn-xs btn-primary" onclick={() => handleFeedback('negative')}>Send</button>
38
+ </div>
39
+ {/if}
40
+ {:else}
41
+ <span class="text-xs text-success">✓ Thank you!</span>
42
+ {/if}
43
+ </div>
@@ -0,0 +1,56 @@
1
+ <script lang="ts">
2
+ interface Insight {
3
+ title: string;
4
+ description: string;
5
+ confidence?: number;
6
+ category?: string;
7
+ }
8
+
9
+ let {
10
+ insights = [],
11
+ loading = false,
12
+ onRefresh = null
13
+ }: {
14
+ insights: Insight[];
15
+ loading?: boolean;
16
+ onRefresh?: (() => Promise<void>) | null;
17
+ } = $props();
18
+ </script>
19
+
20
+ <div class="card bg-base-100 border border-base-200">
21
+ <div class="card-body">
22
+ <div class="flex items-center justify-between mb-4">
23
+ <h3 class="card-title text-sm">AI Insights</h3>
24
+ {#if onRefresh}
25
+ <button class="btn btn-xs btn-ghost" onclick={onRefresh} disabled={loading}>
26
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 {loading ? 'animate-spin' : ''}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
27
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
28
+ </svg>
29
+ </button>
30
+ {/if}
31
+ </div>
32
+
33
+ {#if loading}
34
+ <div class="flex justify-center py-4"><span class="loading loading-spinner"></span></div>
35
+ {:else if insights.length === 0}
36
+ <p class="text-sm opacity-60 text-center py-4">No insights available</p>
37
+ {:else}
38
+ <div class="space-y-3">
39
+ {#each insights as insight}
40
+ <div class="p-3 bg-base-200 rounded-lg">
41
+ <div class="flex items-start justify-between mb-1">
42
+ <h4 class="font-semibold text-sm">{insight.title}</h4>
43
+ {#if insight.confidence}
44
+ <span class="badge badge-xs">{Math.round(insight.confidence * 100)}%</span>
45
+ {/if}
46
+ </div>
47
+ <p class="text-xs opacity-70">{insight.description}</p>
48
+ {#if insight.category}
49
+ <span class="badge badge-xs badge-primary mt-2">{insight.category}</span>
50
+ {/if}
51
+ </div>
52
+ {/each}
53
+ </div>
54
+ {/if}
55
+ </div>
56
+ </div>