@svgrid/enterprise 2.3.0 → 2.3.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 (73) hide show
  1. package/dist/cdn/{pdfmake-C6pyKjX0.js → pdfmake-DVbZqKeD.js} +2297 -2327
  2. package/dist/cdn/rolldown-runtime-DtPi1Y-2.js +13 -0
  3. package/dist/cdn/{smart.export-CfW2zpyq.js → smart.export-D9FF4wAB.js} +54 -51
  4. package/dist/cdn/svgrid-enterprise.svelte-external.js +11308 -7898
  5. package/dist/cdn/{vfs_fonts-BktEULEs.js → vfs_fonts-iHMTEkpG.js} +1 -1
  6. package/dist/designer/assets/GridMenus-CW-rnHS9.js +7 -0
  7. package/dist/designer/assets/SvDateTimePicker-Cygij-dr.css +1 -0
  8. package/dist/designer/assets/SvDateTimePicker-DOBYyLeN.js +1 -0
  9. package/dist/designer/assets/SvGridChart-BEDNTdc5.js +2 -0
  10. package/dist/designer/assets/SvGridChart-Vu4Nvjiv.css +1 -0
  11. package/dist/designer/assets/SvGridChartPanel-x3lzSxuf.js +10 -0
  12. package/dist/designer/assets/SvGridChartView-CAKhPELi.js +1 -0
  13. package/dist/designer/assets/SvGridChartView-DX9HfkBR.css +1 -0
  14. package/dist/designer/assets/SvGridDropdown-CAYG7Y-O.js +1 -0
  15. package/dist/designer/assets/SvGridDropdown-Crwb2P3Q.css +1 -0
  16. package/dist/designer/assets/SvListBox-BMSfFJJT.css +1 -0
  17. package/dist/designer/assets/SvListBox-BVzVfCG3.js +1 -0
  18. package/dist/designer/assets/chart-DHEgt4lN.js +1 -0
  19. package/dist/designer/assets/disclose-version-CIm4S9xS.js +2 -0
  20. package/dist/designer/assets/dismissable-2EZKoens.js +1 -0
  21. package/dist/designer/assets/dismissable-BF-9TJSd.css +1 -0
  22. package/dist/designer/assets/easing-zaypKrr0.js +3 -0
  23. package/dist/designer/assets/export-format-DwjuTfrQ.js +6 -0
  24. package/dist/designer/assets/index-DVrv9dH6.js +75797 -0
  25. package/dist/designer/assets/index-DjPp1mn7.css +1 -0
  26. package/dist/designer/assets/js-scroller.svelte-tHanKJx0.js +110 -0
  27. package/dist/designer/assets/jszip.min-CX2fPs3O.js +2 -0
  28. package/dist/designer/assets/pdfmake-BpIYFHoG.js +242 -0
  29. package/dist/designer/assets/popover-CUkLsvRX.js +1 -0
  30. package/dist/designer/assets/rolldown-runtime-Dd_uD5pT.js +1 -0
  31. package/dist/designer/assets/smart.export-CDCwbFH4.js +37 -0
  32. package/dist/designer/assets/src-BRKu53tV.js +2842 -0
  33. package/dist/designer/assets/src-DseB1VRN.css +1 -0
  34. package/dist/designer/assets/vfs_fonts-BV-Ul9v2.js +1 -0
  35. package/dist/designer/index.html +32 -0
  36. package/dist/node/studio.js +16778 -12672
  37. package/package.json +10 -7
  38. package/src/SvGridBoard.svelte +4 -1
  39. package/src/SvGridScheduler.svelte +114 -90
  40. package/src/ai-export-pdf.dom.test.ts +77 -77
  41. package/src/ai-export-xlsx.dom.test.ts +90 -90
  42. package/src/ai-export.dom.test.ts +114 -114
  43. package/src/export-ooxml.ts +4 -0
  44. package/src/export-xls.ts +3 -0
  45. package/src/expressions/evaluate.ts +9 -0
  46. package/src/import.test.ts +1 -1
  47. package/src/import.ts +1 -1
  48. package/src/index.ts +12 -0
  49. package/src/pivot.test.ts +0 -1
  50. package/src/schema-designer.ts +1 -1
  51. package/src/sources/introspect-supabase.ts +2 -2
  52. package/src/studio/emit-project.test.ts +298 -7
  53. package/src/studio/emit-project.ts +484 -30
  54. package/src/studio/emit-schema.ts +20 -10
  55. package/src/studio/index.ts +34 -0
  56. package/src/studio/init-flow.test.ts +364 -0
  57. package/src/studio/init-flow.ts +409 -0
  58. package/src/studio/project.test.ts +0 -1
  59. package/src/studio/project.ts +109 -2
  60. package/src/studio/samples/datasets.test.ts +84 -0
  61. package/src/studio/samples/datasets.ts +340 -0
  62. package/src/studio/samples/live-data.test.ts +1 -1
  63. package/src/studio/samples/samples.test.ts +268 -268
  64. package/src/studio/samples/shared.ts +7 -150
  65. package/src/studio/screen-suites.test.ts +226 -0
  66. package/src/studio/screen-suites.ts +445 -0
  67. package/src/studio/ui-components-surface.test.ts +2 -2
  68. package/src/studio/ui-components.generated.ts +1643 -106
  69. package/src/studio/ui-components.ts +742 -641
  70. package/src/sveltekit/index.ts +1 -0
  71. package/src/sveltekit/sql-source.ts +1 -1
  72. package/src/sveltekit/transport-scope.test.ts +125 -0
  73. package/src/sveltekit/transport.ts +76 -2
@@ -0,0 +1,340 @@
1
+ /**
2
+ * Starter datasets for the guided "new app" flow.
3
+ *
4
+ * Where `sampleApps` are finished, hand-laid-out showcases, these are just DATA:
5
+ * a small pair of related entities plus realistic seed rows. The guided wizard
6
+ * (and `svgrid-studio init`) feeds them through `crudAppFromSchemas`, so the
7
+ * user sees the same generated app shape they would get from their own database
8
+ * - only without having to connect one first.
9
+ *
10
+ * Each dataset carries a parent and a child linked by a relation, plus at least
11
+ * one colored enum, so the generated app has status pills, a chart dimension and
12
+ * a related-records tab out of the box.
13
+ *
14
+ * Pure + node-safe: `build()` returns fresh data on every call.
15
+ */
16
+ import type { EntitySchema } from '../../schema.js'
17
+ import { pad, ids } from './shared.js'
18
+
19
+ type Row = Record<string, unknown>
20
+
21
+ /** A ready-made pair of entities + seed rows, offered as a starting point. */
22
+ export type StarterDataset = {
23
+ /** Stable id (also the wizard / CLI selection value). */
24
+ id: string
25
+ /** Display name, e.g. "Customers & orders". */
26
+ name: string
27
+ /** One-line description for the picker card. */
28
+ description: string
29
+ /** A single emoji used as the card icon. */
30
+ emoji: string
31
+ /** Build the schemas + seed (fresh each call). */
32
+ build: () => { entities: EntitySchema[]; seed: Record<string, Row[]> }
33
+ }
34
+
35
+ // --- 1. Customers & orders ---------------------------------------------------
36
+
37
+ function customersOrders(): { entities: EntitySchema[]; seed: Record<string, Row[]> } {
38
+ const customers: EntitySchema = {
39
+ name: 'customers',
40
+ label: 'Customer',
41
+ idField: 'id',
42
+ fields: [
43
+ { field: 'id', type: 'text', primaryKey: true, readonly: true },
44
+ { field: 'name', type: 'text', label: 'Customer', required: true, minLength: 2 },
45
+ { field: 'email', type: 'text', label: 'Email', format: 'email' },
46
+ { field: 'city', type: 'text', label: 'City' },
47
+ { field: 'tier', type: 'enum', label: 'Tier', options: [
48
+ { value: 'free', label: 'Free', color: '#64748b' },
49
+ { value: 'pro', label: 'Pro', color: '#2563eb' },
50
+ { value: 'enterprise', label: 'Enterprise', color: '#7c3aed' },
51
+ ] },
52
+ { field: 'since', type: 'dateString', label: 'Customer since' },
53
+ ],
54
+ }
55
+ const orders: EntitySchema = {
56
+ name: 'orders',
57
+ label: 'Order',
58
+ idField: 'id',
59
+ fields: [
60
+ { field: 'id', type: 'text', primaryKey: true, readonly: true },
61
+ { field: 'reference', type: 'text', label: 'Reference', required: true },
62
+ { field: 'customer_id', type: 'relation', label: 'Customer', relation: { entity: 'customers', labelField: 'name' } },
63
+ { field: 'total', type: 'number', label: 'Total ($)', min: 0 },
64
+ { field: 'status', type: 'enum', label: 'Status', options: [
65
+ { value: 'pending', label: 'Pending', color: '#f59e0b' },
66
+ { value: 'paid', label: 'Paid', color: '#16a34a' },
67
+ { value: 'shipped', label: 'Shipped', color: '#2563eb' },
68
+ { value: 'refunded', label: 'Refunded', color: '#dc2626' },
69
+ ] },
70
+ { field: 'placed', type: 'dateString', label: 'Placed' },
71
+ ],
72
+ }
73
+ const customerRows: Row[] = [
74
+ { id: 'cu1', name: 'Northwind Traders', email: 'ops@northwind.test', city: 'Seattle', tier: 'enterprise', since: '2023-02-14' },
75
+ { id: 'cu2', name: 'Blue Harbour Foods', email: 'hello@blueharbour.test', city: 'Bristol', tier: 'pro', since: '2024-06-02' },
76
+ { id: 'cu3', name: 'Cedar & Co', email: 'accounts@cedar.test', city: 'Toronto', tier: 'pro', since: '2024-11-19' },
77
+ { id: 'cu4', name: 'Meridian Labs', email: 'buy@meridian.test', city: 'Berlin', tier: 'free', since: '2025-03-08' },
78
+ { id: 'cu5', name: 'Fairlight Studio', email: 'studio@fairlight.test', city: 'Melbourne', tier: 'pro', since: '2025-05-21' },
79
+ { id: 'cu6', name: 'Alpine Outfitters', email: 'orders@alpine.test', city: 'Zurich', tier: 'enterprise', since: '2022-09-30' },
80
+ ]
81
+ const orderRows: Row[] = [
82
+ { id: 'or1', reference: 'INV-1041', customer_id: 'cu1', total: 4820, status: 'paid', placed: '2026-01-12' },
83
+ { id: 'or2', reference: 'INV-1042', customer_id: 'cu2', total: 615, status: 'shipped', placed: '2026-01-15' },
84
+ { id: 'or3', reference: 'INV-1043', customer_id: 'cu1', total: 1290, status: 'pending', placed: '2026-01-19' },
85
+ { id: 'or4', reference: 'INV-1044', customer_id: 'cu3', total: 340, status: 'refunded', placed: '2026-01-23' },
86
+ { id: 'or5', reference: 'INV-1045', customer_id: 'cu6', total: 7150, status: 'paid', placed: '2026-02-02' },
87
+ ]
88
+ return {
89
+ entities: [customers, orders],
90
+ seed: {
91
+ customers: pad(customers, customerRows, 24),
92
+ orders: pad(orders, orderRows, 40, { customer_id: ids(customerRows) }),
93
+ },
94
+ }
95
+ }
96
+
97
+ // --- 2. Products & categories ------------------------------------------------
98
+
99
+ function productsCategories(): { entities: EntitySchema[]; seed: Record<string, Row[]> } {
100
+ const categories: EntitySchema = {
101
+ name: 'categories',
102
+ label: 'Category',
103
+ idField: 'id',
104
+ fields: [
105
+ { field: 'id', type: 'text', primaryKey: true, readonly: true },
106
+ { field: 'name', type: 'text', label: 'Category', required: true },
107
+ { field: 'description', type: 'text', label: 'Description' },
108
+ ],
109
+ }
110
+ const products: EntitySchema = {
111
+ name: 'products',
112
+ label: 'Product',
113
+ idField: 'id',
114
+ fields: [
115
+ { field: 'id', type: 'text', primaryKey: true, readonly: true },
116
+ { field: 'name', type: 'text', label: 'Product', required: true, minLength: 2 },
117
+ { field: 'category_id', type: 'relation', label: 'Category', relation: { entity: 'categories', labelField: 'name' } },
118
+ { field: 'price', type: 'number', label: 'Price ($)', min: 0 },
119
+ { field: 'stock', type: 'number', label: 'In stock', min: 0 },
120
+ { field: 'status', type: 'enum', label: 'Status', options: [
121
+ { value: 'active', label: 'Active', color: '#16a34a' },
122
+ { value: 'low', label: 'Low stock', color: '#f59e0b' },
123
+ { value: 'discontinued', label: 'Discontinued', color: '#64748b' },
124
+ ] },
125
+ ],
126
+ }
127
+ const categoryRows: Row[] = [
128
+ { id: 'ca1', name: 'Beverages', description: 'Coffee, tea, soft drinks' },
129
+ { id: 'ca2', name: 'Condiments', description: 'Sauces, spreads, seasonings' },
130
+ { id: 'ca3', name: 'Produce', description: 'Fresh fruit and vegetables' },
131
+ { id: 'ca4', name: 'Bakery', description: 'Bread, pastries, cakes' },
132
+ { id: 'ca5', name: 'Household', description: 'Cleaning and kitchen supplies' },
133
+ ]
134
+ const productRows: Row[] = [
135
+ { id: 'pr1', name: 'Single-origin coffee', category_id: 'ca1', price: 14.5, stock: 92, status: 'active' },
136
+ { id: 'pr2', name: 'Green tea sampler', category_id: 'ca1', price: 9.75, stock: 12, status: 'low' },
137
+ { id: 'pr3', name: 'Smoked chilli sauce', category_id: 'ca2', price: 6.2, stock: 240, status: 'active' },
138
+ { id: 'pr4', name: 'Sourdough loaf', category_id: 'ca4', price: 5, stock: 30, status: 'active' },
139
+ { id: 'pr5', name: 'Bamboo dish brush', category_id: 'ca5', price: 4.4, stock: 0, status: 'discontinued' },
140
+ ]
141
+ return {
142
+ entities: [categories, products],
143
+ seed: {
144
+ categories: pad(categories, categoryRows, 12),
145
+ products: pad(products, productRows, 36, { category_id: ids(categoryRows) }),
146
+ },
147
+ }
148
+ }
149
+
150
+ // --- 3. Projects & tasks -----------------------------------------------------
151
+
152
+ function projectsTasks(): { entities: EntitySchema[]; seed: Record<string, Row[]> } {
153
+ const projects: EntitySchema = {
154
+ name: 'projects',
155
+ label: 'Project',
156
+ idField: 'id',
157
+ fields: [
158
+ { field: 'id', type: 'text', primaryKey: true, readonly: true },
159
+ { field: 'name', type: 'text', label: 'Project', required: true, minLength: 2 },
160
+ { field: 'owner', type: 'text', label: 'Owner' },
161
+ { field: 'status', type: 'enum', label: 'Status', options: [
162
+ { value: 'planning', label: 'Planning', color: '#64748b' },
163
+ { value: 'active', label: 'Active', color: '#2563eb' },
164
+ { value: 'blocked', label: 'Blocked', color: '#dc2626' },
165
+ { value: 'done', label: 'Done', color: '#16a34a' },
166
+ ] },
167
+ { field: 'budget', type: 'number', label: 'Budget ($)', min: 0 },
168
+ { field: 'due', type: 'dateString', label: 'Due' },
169
+ ],
170
+ }
171
+ const tasks: EntitySchema = {
172
+ name: 'tasks',
173
+ label: 'Task',
174
+ idField: 'id',
175
+ fields: [
176
+ { field: 'id', type: 'text', primaryKey: true, readonly: true },
177
+ { field: 'title', type: 'text', label: 'Task', required: true, minLength: 2 },
178
+ { field: 'project_id', type: 'relation', label: 'Project', relation: { entity: 'projects', labelField: 'name' } },
179
+ { field: 'assignee', type: 'text', label: 'Assignee' },
180
+ { field: 'priority', type: 'enum', label: 'Priority', options: [
181
+ { value: 'low', label: 'Low', color: '#64748b' },
182
+ { value: 'medium', label: 'Medium', color: '#f59e0b' },
183
+ { value: 'high', label: 'High', color: '#dc2626' },
184
+ ] },
185
+ { field: 'estimate', type: 'number', label: 'Estimate (h)', min: 0 },
186
+ { field: 'done', type: 'boolean', label: 'Done' },
187
+ { field: 'due', type: 'dateString', label: 'Due' },
188
+ ],
189
+ }
190
+ const projectRows: Row[] = [
191
+ { id: 'pj1', name: 'Website redesign', owner: 'Dana Iqbal', status: 'active', budget: 48000, due: '2026-04-30' },
192
+ { id: 'pj2', name: 'Mobile checkout', owner: 'Sam Oyelaran', status: 'blocked', budget: 96000, due: '2026-06-15' },
193
+ { id: 'pj3', name: 'Warehouse migration', owner: 'Riya Kapoor', status: 'planning', budget: 130000, due: '2026-09-01' },
194
+ { id: 'pj4', name: 'Support portal', owner: 'Theo Marchand', status: 'done', budget: 25000, due: '2025-12-12' },
195
+ ]
196
+ const taskRows: Row[] = [
197
+ { id: 'ts1', title: 'Audit current pages', project_id: 'pj1', assignee: 'Dana Iqbal', priority: 'medium', estimate: 12, done: true, due: '2026-02-10' },
198
+ { id: 'ts2', title: 'New component library', project_id: 'pj1', assignee: 'Nils Berg', priority: 'high', estimate: 40, done: false, due: '2026-03-20' },
199
+ { id: 'ts3', title: 'Payment provider spike', project_id: 'pj2', assignee: 'Sam Oyelaran', priority: 'high', estimate: 16, done: false, due: '2026-02-28' },
200
+ { id: 'ts4', title: 'Inventory data mapping', project_id: 'pj3', assignee: 'Riya Kapoor', priority: 'low', estimate: 24, done: false, due: '2026-05-05' },
201
+ ]
202
+ return {
203
+ entities: [projects, tasks],
204
+ seed: {
205
+ projects: pad(projects, projectRows, 16),
206
+ tasks: pad(tasks, taskRows, 48, { project_id: ids(projectRows) }),
207
+ },
208
+ }
209
+ }
210
+
211
+ // --- 4. Employees & departments ---------------------------------------------
212
+
213
+ function employeesDepartments(): { entities: EntitySchema[]; seed: Record<string, Row[]> } {
214
+ const departments: EntitySchema = {
215
+ name: 'departments',
216
+ label: 'Department',
217
+ idField: 'id',
218
+ fields: [
219
+ { field: 'id', type: 'text', primaryKey: true, readonly: true },
220
+ { field: 'name', type: 'text', label: 'Department', required: true },
221
+ { field: 'location', type: 'text', label: 'Location' },
222
+ { field: 'headcount', type: 'number', label: 'Headcount', min: 0 },
223
+ ],
224
+ }
225
+ const employees: EntitySchema = {
226
+ name: 'employees',
227
+ label: 'Employee',
228
+ idField: 'id',
229
+ fields: [
230
+ { field: 'id', type: 'text', primaryKey: true, readonly: true },
231
+ { field: 'name', type: 'text', label: 'Name', required: true, minLength: 2 },
232
+ { field: 'email', type: 'text', label: 'Email', format: 'email' },
233
+ { field: 'department_id', type: 'relation', label: 'Department', relation: { entity: 'departments', labelField: 'name' } },
234
+ { field: 'role', type: 'text', label: 'Role' },
235
+ { field: 'salary', type: 'number', label: 'Salary ($)', min: 0 },
236
+ { field: 'status', type: 'enum', label: 'Status', options: [
237
+ { value: 'active', label: 'Active', color: '#16a34a' },
238
+ { value: 'onboarding', label: 'Onboarding', color: '#2563eb' },
239
+ { value: 'leave', label: 'On leave', color: '#f59e0b' },
240
+ { value: 'alumni', label: 'Alumni', color: '#64748b' },
241
+ ] },
242
+ { field: 'started', type: 'dateString', label: 'Start date' },
243
+ ],
244
+ }
245
+ const departmentRows: Row[] = [
246
+ { id: 'de1', name: 'Engineering', location: 'Lisbon', headcount: 42 },
247
+ { id: 'de2', name: 'Sales', location: 'Chicago', headcount: 18 },
248
+ { id: 'de3', name: 'Support', location: 'Manila', headcount: 25 },
249
+ { id: 'de4', name: 'Finance', location: 'London', headcount: 9 },
250
+ ]
251
+ const employeeRows: Row[] = [
252
+ { id: 'em1', name: 'Aisha Rahman', email: 'aisha@example.test', department_id: 'de1', role: 'Staff engineer', salary: 142000, status: 'active', started: '2021-08-16' },
253
+ { id: 'em2', name: 'Marcus Webb', email: 'marcus@example.test', department_id: 'de2', role: 'Account executive', salary: 88000, status: 'active', started: '2023-01-09' },
254
+ { id: 'em3', name: 'Lena Fischer', email: 'lena@example.test', department_id: 'de3', role: 'Support lead', salary: 76000, status: 'leave', started: '2022-04-25' },
255
+ { id: 'em4', name: 'Diego Santos', email: 'diego@example.test', department_id: 'de4', role: 'Financial analyst', salary: 81000, status: 'onboarding', started: '2026-01-05' },
256
+ ]
257
+ return {
258
+ entities: [departments, employees],
259
+ seed: {
260
+ departments: pad(departments, departmentRows, 10),
261
+ employees: pad(employees, employeeRows, 40, { department_id: ids(departmentRows) }),
262
+ },
263
+ }
264
+ }
265
+
266
+ // --- 5. Support tickets ------------------------------------------------------
267
+
268
+ function ticketsAccounts(): { entities: EntitySchema[]; seed: Record<string, Row[]> } {
269
+ const accounts: EntitySchema = {
270
+ name: 'accounts',
271
+ label: 'Account',
272
+ idField: 'id',
273
+ fields: [
274
+ { field: 'id', type: 'text', primaryKey: true, readonly: true },
275
+ { field: 'company', type: 'text', label: 'Company', required: true, minLength: 2 },
276
+ { field: 'contact', type: 'text', label: 'Primary contact' },
277
+ { field: 'plan', type: 'enum', label: 'Plan', options: [
278
+ { value: 'starter', label: 'Starter', color: '#64748b' },
279
+ { value: 'growth', label: 'Growth', color: '#2563eb' },
280
+ { value: 'scale', label: 'Scale', color: '#7c3aed' },
281
+ ] },
282
+ { field: 'seats', type: 'number', label: 'Seats', min: 1 },
283
+ ],
284
+ }
285
+ const tickets: EntitySchema = {
286
+ name: 'tickets',
287
+ label: 'Ticket',
288
+ idField: 'id',
289
+ fields: [
290
+ { field: 'id', type: 'text', primaryKey: true, readonly: true },
291
+ { field: 'subject', type: 'text', label: 'Subject', required: true, minLength: 3 },
292
+ { field: 'account_id', type: 'relation', label: 'Account', relation: { entity: 'accounts', labelField: 'company' } },
293
+ { field: 'priority', type: 'enum', label: 'Priority', options: [
294
+ { value: 'low', label: 'Low', color: '#64748b' },
295
+ { value: 'normal', label: 'Normal', color: '#2563eb' },
296
+ { value: 'urgent', label: 'Urgent', color: '#dc2626' },
297
+ ] },
298
+ { field: 'status', type: 'enum', label: 'Status', options: [
299
+ { value: 'open', label: 'Open', color: '#f59e0b' },
300
+ { value: 'pending', label: 'Pending', color: '#2563eb' },
301
+ { value: 'solved', label: 'Solved', color: '#16a34a' },
302
+ ] },
303
+ { field: 'hours', type: 'number', label: 'Hours logged', min: 0 },
304
+ { field: 'opened', type: 'dateString', label: 'Opened' },
305
+ ],
306
+ }
307
+ const accountRows: Row[] = [
308
+ { id: 'ac1', company: 'Halcyon Retail', contact: 'Priya Nair', plan: 'scale', seats: 220 },
309
+ { id: 'ac2', company: 'Redwood Clinic', contact: 'Tom Alvarez', plan: 'growth', seats: 45 },
310
+ { id: 'ac3', company: 'Vector Freight', contact: 'Ingrid Holm', plan: 'starter', seats: 8 },
311
+ { id: 'ac4', company: 'Pinehill Academy', contact: 'Joy Adeyemi', plan: 'growth', seats: 60 },
312
+ ]
313
+ const ticketRows: Row[] = [
314
+ { id: 'tk1', subject: 'Export fails on large report', account_id: 'ac1', priority: 'urgent', status: 'open', hours: 3.5, opened: '2026-02-03' },
315
+ { id: 'tk2', subject: 'Add a second admin seat', account_id: 'ac2', priority: 'low', status: 'solved', hours: 0.5, opened: '2026-01-28' },
316
+ { id: 'tk3', subject: 'Webhook retries stop after an hour', account_id: 'ac3', priority: 'normal', status: 'pending', hours: 2, opened: '2026-02-06' },
317
+ { id: 'tk4', subject: 'SSO metadata refresh', account_id: 'ac4', priority: 'normal', status: 'open', hours: 1.25, opened: '2026-02-11' },
318
+ ]
319
+ return {
320
+ entities: [accounts, tickets],
321
+ seed: {
322
+ accounts: pad(accounts, accountRows, 12),
323
+ tickets: pad(tickets, ticketRows, 44, { account_id: ids(accountRows) }),
324
+ },
325
+ }
326
+ }
327
+
328
+ /** Every starter dataset, in picker order. */
329
+ export const starterDatasets: StarterDataset[] = [
330
+ { id: 'customers-orders', name: 'Customers & orders', description: 'Accounts with their order history - the classic back-office pair.', emoji: '🧾', build: customersOrders },
331
+ { id: 'products-categories', name: 'Products & categories', description: 'A product catalog with stock levels and categories.', emoji: '📦', build: productsCategories },
332
+ { id: 'projects-tasks', name: 'Projects & tasks', description: 'Project tracker with assignees, priorities and due dates.', emoji: '🗂️', build: projectsTasks },
333
+ { id: 'employees-departments', name: 'Employees & departments', description: 'A staff directory with departments, roles and salaries.', emoji: '👥', build: employeesDepartments },
334
+ { id: 'tickets-accounts', name: 'Support tickets', description: 'Customer accounts with a support queue by priority and status.', emoji: '🎧', build: ticketsAccounts },
335
+ ]
336
+
337
+ /** Look up a starter dataset by id. */
338
+ export function getStarterDataset(id: string): StarterDataset | undefined {
339
+ return starterDatasets.find((d) => d.id === id)
340
+ }
@@ -62,7 +62,7 @@ describe('live-data starters', () => {
62
62
  const all = emitStudioProject(s.build()).map((f) => f.contents).join('\n')
63
63
  expect(all.includes('SvRecordDetail'), `${s.id}: no detail page`).toBe(true)
64
64
  expect(all.includes('import { goto }'), `${s.id}: no drill-through`).toBe(true)
65
- expect(all.includes("selectedId={$page.url.searchParams.get('id')"), `${s.id}: detail not URL-addressable`).toBe(true)
65
+ expect(all.includes("selectedId={page.url.searchParams.get('id')"), `${s.id}: detail not URL-addressable`).toBe(true)
66
66
  }
67
67
  })
68
68