@aphexcms/cms-core 0.1.16 → 0.2.1

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 (120) hide show
  1. package/dist/api/client.d.ts.map +1 -1
  2. package/dist/api/client.js +7 -1
  3. package/dist/api/types.d.ts +2 -0
  4. package/dist/api/types.d.ts.map +1 -1
  5. package/dist/cli/generate-types.js +62 -17
  6. package/dist/cli/index.js +1 -1
  7. package/dist/client/index.d.ts +0 -1
  8. package/dist/client/index.d.ts.map +1 -1
  9. package/dist/client/index.js +0 -1
  10. package/dist/components/AdminApp.svelte +278 -45
  11. package/dist/components/AdminApp.svelte.d.ts +2 -0
  12. package/dist/components/AdminApp.svelte.d.ts.map +1 -1
  13. package/dist/components/admin/DocumentEditor.svelte +60 -13
  14. package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
  15. package/dist/components/admin/ObjectModal.svelte +15 -4
  16. package/dist/components/admin/ObjectModal.svelte.d.ts +1 -0
  17. package/dist/components/admin/ObjectModal.svelte.d.ts.map +1 -1
  18. package/dist/components/admin/SchemaField.svelte +64 -5
  19. package/dist/components/admin/SchemaField.svelte.d.ts +1 -0
  20. package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -1
  21. package/dist/components/admin/fields/ArrayField.svelte +402 -111
  22. package/dist/components/admin/fields/ArrayField.svelte.d.ts +1 -0
  23. package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
  24. package/dist/components/admin/fields/DateField.svelte +145 -0
  25. package/dist/components/admin/fields/DateField.svelte.d.ts +14 -0
  26. package/dist/components/admin/fields/DateField.svelte.d.ts.map +1 -0
  27. package/dist/components/admin/fields/DateTimeField.svelte +225 -0
  28. package/dist/components/admin/fields/DateTimeField.svelte.d.ts +14 -0
  29. package/dist/components/admin/fields/DateTimeField.svelte.d.ts.map +1 -0
  30. package/dist/components/admin/fields/ImageField.svelte +221 -110
  31. package/dist/components/admin/fields/ImageField.svelte.d.ts +2 -0
  32. package/dist/components/admin/fields/ImageField.svelte.d.ts.map +1 -1
  33. package/dist/components/admin/fields/ReferenceField.svelte +1 -1
  34. package/dist/components/admin/fields/SlugField.svelte +21 -13
  35. package/dist/components/admin/fields/SlugField.svelte.d.ts +2 -2
  36. package/dist/components/admin/fields/SlugField.svelte.d.ts.map +1 -1
  37. package/dist/components/admin/fields/StringField.svelte +156 -12
  38. package/dist/components/admin/fields/StringField.svelte.d.ts +3 -2
  39. package/dist/components/admin/fields/StringField.svelte.d.ts.map +1 -1
  40. package/dist/components/admin/fields/URLField.svelte +41 -0
  41. package/dist/components/admin/fields/URLField.svelte.d.ts +14 -0
  42. package/dist/components/admin/fields/URLField.svelte.d.ts.map +1 -0
  43. package/dist/components/index.d.ts +0 -1
  44. package/dist/components/index.d.ts.map +1 -1
  45. package/dist/components/index.js +0 -1
  46. package/dist/db/interfaces/asset.d.ts.map +1 -1
  47. package/dist/db/interfaces/document.d.ts +0 -2
  48. package/dist/db/interfaces/document.d.ts.map +1 -1
  49. package/dist/db/interfaces/index.d.ts +2 -1
  50. package/dist/db/interfaces/index.d.ts.map +1 -1
  51. package/dist/db/interfaces/user.d.ts +2 -0
  52. package/dist/db/interfaces/user.d.ts.map +1 -1
  53. package/dist/db/utils/reference-resolver.js +1 -1
  54. package/dist/engine.d.ts.map +1 -1
  55. package/dist/engine.js +3 -0
  56. package/dist/field-validation/date-utils.d.ts +30 -0
  57. package/dist/field-validation/date-utils.d.ts.map +1 -0
  58. package/dist/field-validation/date-utils.js +147 -0
  59. package/dist/field-validation/rule.d.ts +4 -0
  60. package/dist/field-validation/rule.d.ts.map +1 -1
  61. package/dist/field-validation/rule.js +170 -4
  62. package/dist/field-validation/utils.d.ts +7 -3
  63. package/dist/field-validation/utils.d.ts.map +1 -1
  64. package/dist/field-validation/utils.js +130 -38
  65. package/dist/hooks.d.ts.map +1 -1
  66. package/dist/hooks.js +38 -21
  67. package/dist/lib/field-validation/date-utils.js +147 -0
  68. package/dist/lib/field-validation/rule.js +170 -4
  69. package/dist/lib/field-validation/utils.js +130 -38
  70. package/dist/local-api/collection-api.d.ts +16 -4
  71. package/dist/local-api/collection-api.d.ts.map +1 -1
  72. package/dist/local-api/collection-api.js +51 -17
  73. package/dist/local-api/index.d.ts +1 -1
  74. package/dist/local-api/index.d.ts.map +1 -1
  75. package/dist/routes/assets-cdn.d.ts.map +1 -1
  76. package/dist/routes/assets-cdn.js +14 -7
  77. package/dist/routes/assets.d.ts.map +1 -1
  78. package/dist/routes/assets.js +6 -1
  79. package/dist/routes/documents-by-id.d.ts.map +1 -1
  80. package/dist/routes/documents-by-id.js +18 -7
  81. package/dist/routes/documents-publish.js +2 -2
  82. package/dist/routes/documents-query.d.ts +3 -1
  83. package/dist/routes/documents-query.d.ts.map +1 -1
  84. package/dist/routes/documents-query.js +6 -2
  85. package/dist/routes/documents.d.ts.map +1 -1
  86. package/dist/routes/documents.js +20 -4
  87. package/dist/routes/index.d.ts +1 -0
  88. package/dist/routes/index.d.ts.map +1 -1
  89. package/dist/routes/index.js +2 -0
  90. package/dist/routes/user-preferences.d.ts +4 -0
  91. package/dist/routes/user-preferences.d.ts.map +1 -0
  92. package/dist/routes/user-preferences.js +77 -0
  93. package/dist/schema-utils/utils.d.ts +4 -0
  94. package/dist/schema-utils/utils.d.ts.map +1 -1
  95. package/dist/schema-utils/utils.js +23 -2
  96. package/dist/schema-utils/validator.d.ts +4 -0
  97. package/dist/schema-utils/validator.d.ts.map +1 -1
  98. package/dist/schema-utils/validator.js +120 -0
  99. package/dist/types/filters.d.ts +13 -0
  100. package/dist/types/filters.d.ts.map +1 -1
  101. package/dist/types/organization.d.ts +3 -0
  102. package/dist/types/organization.d.ts.map +1 -1
  103. package/dist/types/schemas.d.ts +67 -7
  104. package/dist/types/schemas.d.ts.map +1 -1
  105. package/dist/utils/default-orderings.d.ts +10 -0
  106. package/dist/utils/default-orderings.d.ts.map +1 -0
  107. package/dist/utils/default-orderings.js +63 -0
  108. package/dist/utils/field-defaults.d.ts +8 -0
  109. package/dist/utils/field-defaults.d.ts.map +1 -0
  110. package/dist/utils/field-defaults.js +20 -0
  111. package/dist/utils/index.d.ts +1 -0
  112. package/dist/utils/index.d.ts.map +1 -1
  113. package/dist/utils/index.js +1 -0
  114. package/dist/utils/initial-value-helpers.d.ts +50 -0
  115. package/dist/utils/initial-value-helpers.d.ts.map +1 -0
  116. package/dist/utils/initial-value-helpers.js +70 -0
  117. package/package.json +6 -4
  118. package/dist/components/admin/DocumentTypesList.svelte +0 -97
  119. package/dist/components/admin/DocumentTypesList.svelte.d.ts +0 -14
  120. package/dist/components/admin/DocumentTypesList.svelte.d.ts.map +0 -1
@@ -1,10 +1,16 @@
1
1
  <script lang="ts">
2
2
  import { Button } from '@aphexcms/ui/shadcn/button';
3
+ import { Input } from '@aphexcms/ui/shadcn/input';
4
+ import { Textarea } from '@aphexcms/ui/shadcn/textarea';
5
+ import { Checkbox } from '@aphexcms/ui/shadcn/checkbox';
3
6
  import * as DropdownMenu from '@aphexcms/ui/shadcn/dropdown-menu';
7
+ import * as Card from '@aphexcms/ui/shadcn/card';
4
8
  import type { ArrayField as ArrayFieldType, SchemaType } from '../../../types/schemas';
5
9
  import { getArrayTypes, getSchemaByName } from '../../../schema-utils/utils';
6
10
  import { getSchemaContext } from '../../../schema-context.svelte';
7
11
  import ObjectModal from '../ObjectModal.svelte';
12
+ import ImageField from './ImageField.svelte';
13
+ import { getDefaultValueForFieldType } from '../../../utils/field-defaults';
8
14
 
9
15
  interface Props {
10
16
  field: ArrayFieldType;
@@ -12,31 +18,118 @@
12
18
  onUpdate: (value: any) => void;
13
19
  onOpenReference?: (documentId: string, documentType: string) => void;
14
20
  readonly?: boolean;
21
+ organizationId?: string; // For asset uploads to org-specific storage
15
22
  }
16
23
 
17
- let { field, value, onUpdate, onOpenReference, readonly = false }: Props = $props();
24
+ let {
25
+ field,
26
+ value,
27
+ onUpdate,
28
+ onOpenReference,
29
+ readonly = false,
30
+ organizationId
31
+ }: Props = $props();
18
32
 
19
33
  // Get schemas from context
20
34
  const schemas = getSchemaContext();
21
35
 
22
- // Get available types for this array field
36
+ // Get schema for a type - either from inline definition or registry
37
+ function getSchemaForType(typeName: string): SchemaType | null {
38
+ // First check if this type has an inline definition in field.of
39
+ // Match by name OR type (since inline objects might use either)
40
+ const inlineDef = field.of?.find(
41
+ (ref) => (ref.name && ref.name === typeName) || ref.type === typeName
42
+ );
43
+
44
+ if (inlineDef && inlineDef.fields) {
45
+ // Create a temporary SchemaType from inline definition
46
+ return {
47
+ type: 'object',
48
+ name: inlineDef.name || typeName,
49
+ title: inlineDef.title || typeName,
50
+ fields: inlineDef.fields
51
+ };
52
+ }
53
+
54
+ // Otherwise look it up in the schema registry
55
+ return getSchemaByName(schemas, typeName);
56
+ }
57
+
58
+ // Determine if this is a primitive array or object array
59
+ // If the type is not found in schemas AND has no inline fields, it's a primitive type
60
+ const isPrimitiveArray = $derived(
61
+ field.of &&
62
+ field.of.length > 0 &&
63
+ field.of[0]?.type &&
64
+ !field.of[0].fields && // Not an inline object
65
+ !getSchemaByName(schemas, field.of[0].type) // Not in registry
66
+ );
67
+ const primitiveType = $derived(isPrimitiveArray ? field.of?.[0]?.type : null);
68
+
69
+ // Get available types for this array field (for object arrays)
23
70
  const availableTypes = $derived(getArrayTypes(schemas, field));
24
71
 
25
- // Modal state
72
+ // Modal state (for object arrays)
26
73
  let modalOpen = $state(false);
27
74
  let editingIndex = $state<number | null>(null);
28
75
  let editingType = $state<string | null>(null);
29
76
  let editingSchema = $state<SchemaType | null>(null);
30
77
  let editingValue = $state<Record<string, any>>({});
31
78
 
79
+ // Image modal state
80
+ let imageModalOpen = $state(false);
81
+ let imageModalValue = $state<any>(null);
82
+
32
83
  // Ensure value is always an array
33
84
  const arrayValue = $derived(Array.isArray(value) ? value : []);
34
85
 
86
+ // Primitive array functions
87
+ function handleAddPrimitive() {
88
+ if (readonly) return;
89
+
90
+ // For images, open the modal
91
+ if (primitiveType === 'image') {
92
+ imageModalValue = null;
93
+ imageModalOpen = true;
94
+ return;
95
+ }
96
+
97
+ // For other primitives, add a new empty item
98
+ const newArray = [...arrayValue];
99
+ const defaultValue = primitiveType === 'boolean' ? false : primitiveType === 'number' ? 0 : '';
100
+ newArray.push(defaultValue);
101
+ onUpdate(newArray);
102
+ }
103
+
104
+ function handleUpdatePrimitive(index: number, newValue: any) {
105
+ if (readonly) return;
106
+ const newArray = [...arrayValue];
107
+ newArray[index] = newValue;
108
+ onUpdate(newArray);
109
+ }
110
+
111
+ function handleImageModalClose() {
112
+ imageModalOpen = false;
113
+ imageModalValue = null;
114
+ }
115
+
116
+ function handleImageUpload(newValue: any) {
117
+ if (newValue) {
118
+ // Auto-add the image to the array
119
+ const newArray = [...arrayValue, newValue];
120
+ onUpdate(newArray);
121
+ // Close the modal
122
+ imageModalOpen = false;
123
+ imageModalValue = null;
124
+ }
125
+ }
126
+
127
+ // Object array functions
35
128
  function handleTypeSelected(selectedType: string) {
36
129
  if (readonly || !selectedType) return;
37
130
 
38
- // Get the schema for the selected type
39
- const schema = getSchemaByName(schemas, selectedType);
131
+ // Get the schema for the selected type (inline or from registry)
132
+ const schema = getSchemaForType(selectedType);
40
133
  if (!schema) return;
41
134
 
42
135
  // Initialize empty object with default values
@@ -44,10 +137,17 @@
44
137
 
45
138
  if (schema.fields) {
46
139
  schema.fields.forEach((field) => {
47
- if (field.type === 'boolean' && 'initialValue' in field) {
48
- newItem[field.name] = field.initialValue;
140
+ if ('initialValue' in field && field.initialValue !== undefined) {
141
+ // Only use literal initialValue (skip functions to keep this synchronous)
142
+ if (typeof field.initialValue !== 'function') {
143
+ newItem[field.name] = field.initialValue;
144
+ } else {
145
+ // Function-based initialValues are skipped for nested items
146
+ // They will use field type defaults instead
147
+ newItem[field.name] = getDefaultValueForFieldType(field.type);
148
+ }
49
149
  } else {
50
- newItem[field.name] = '';
150
+ newItem[field.name] = getDefaultValueForFieldType(field.type);
51
151
  }
52
152
  });
53
153
  }
@@ -64,7 +164,7 @@
64
164
  const item = arrayValue[index];
65
165
  if (!item._type) return;
66
166
 
67
- const schema = getSchemaByName(schemas, item._type);
167
+ const schema = getSchemaForType(item._type);
68
168
  if (!schema) return;
69
169
 
70
170
  editingIndex = index;
@@ -135,120 +235,260 @@
135
235
  <div class="border-border space-y-4 rounded-md border p-4">
136
236
  <h4 class="text-sm font-medium">{field.title}</h4>
137
237
 
138
- <!-- Array items -->
139
- {#if arrayValue.length > 0}
140
- <div class="space-y-2">
141
- {#each arrayValue as item, index (index)}
142
- <div class="border-border/50 space-y-2 rounded border p-3">
143
- <div class="flex items-center justify-between">
144
- <div class="flex items-center gap-2">
238
+ {#if isPrimitiveArray}
239
+ <!-- Primitive array UI -->
240
+ {#if arrayValue.length === 0}
241
+ <!-- Empty state -->
242
+ <div
243
+ class="border-border/50 bg-muted/30 flex items-center justify-center rounded border border-dashed p-6"
244
+ >
245
+ <p class="text-muted-foreground text-sm">No items</p>
246
+ </div>
247
+ {:else}
248
+ <div class="space-y-2">
249
+ {#each arrayValue as item, index (index)}
250
+ {#if primitiveType === 'image'}
251
+ <!-- Image item in compact mode -->
252
+ <ImageField
253
+ field={{
254
+ ...field.of?.[0],
255
+ name: `image-${index}`,
256
+ type: 'image',
257
+ title: `Image ${index + 1}`
258
+ }}
259
+ value={item}
260
+ onUpdate={(newValue) => {
261
+ const newArray = [...arrayValue];
262
+ if (newValue === null) {
263
+ // Remove the image if null
264
+ newArray.splice(index, 1);
265
+ } else {
266
+ newArray[index] = newValue;
267
+ }
268
+ onUpdate(newArray);
269
+ }}
270
+ {readonly}
271
+ compact={true}
272
+ {organizationId}
273
+ />
274
+ {:else}
275
+ <!-- Always-editable primitive with options menu -->
276
+ <div class="border-border/50 flex items-center gap-2 rounded border p-2">
145
277
  <span class="text-muted-foreground text-xs">#{index + 1}</span>
146
- <h5 class="text-sm font-medium">{getItemTitle(item)}</h5>
147
- {#if item._type}
148
- <span class="bg-muted rounded px-2 py-1 text-xs">{item._type}</span>
278
+ {#if primitiveType === 'boolean'}
279
+ <div class="flex flex-1 items-center gap-2">
280
+ <Checkbox
281
+ checked={item}
282
+ onCheckedChange={(checked) => handleUpdatePrimitive(index, checked)}
283
+ disabled={readonly}
284
+ />
285
+ <span class="text-sm">{item ? 'True' : 'False'}</span>
286
+ </div>
287
+ {:else if primitiveType === 'text'}
288
+ <Textarea
289
+ value={item}
290
+ oninput={(e) => handleUpdatePrimitive(index, e.currentTarget.value)}
291
+ {readonly}
292
+ class="flex-1"
293
+ rows={3}
294
+ placeholder="Enter text..."
295
+ />
296
+ {:else if primitiveType === 'number'}
297
+ <Input
298
+ type="number"
299
+ value={item}
300
+ oninput={(e) =>
301
+ handleUpdatePrimitive(index, parseFloat(e.currentTarget.value) || 0)}
302
+ {readonly}
303
+ class="flex-1"
304
+ placeholder="Enter number..."
305
+ />
306
+ {:else}
307
+ <Input
308
+ value={item}
309
+ oninput={(e) => handleUpdatePrimitive(index, e.currentTarget.value)}
310
+ {readonly}
311
+ class="flex-1"
312
+ placeholder="Enter value..."
313
+ />
314
+ {/if}
315
+ {#if !readonly}
316
+ <DropdownMenu.Root>
317
+ <DropdownMenu.Trigger>
318
+ <Button variant="ghost" size="sm" class="h-8 w-8 p-0">
319
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
320
+ <path
321
+ stroke-linecap="round"
322
+ stroke-linejoin="round"
323
+ stroke-width="2"
324
+ 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"
325
+ />
326
+ </svg>
327
+ </Button>
328
+ </DropdownMenu.Trigger>
329
+ <DropdownMenu.Content align="end">
330
+ <DropdownMenu.Item onclick={() => handleRemoveItem(index)}>
331
+ <svg
332
+ class="mr-2 h-4 w-4"
333
+ fill="none"
334
+ viewBox="0 0 24 24"
335
+ stroke="currentColor"
336
+ >
337
+ <path
338
+ stroke-linecap="round"
339
+ stroke-linejoin="round"
340
+ stroke-width="2"
341
+ d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
342
+ />
343
+ </svg>
344
+ Remove
345
+ </DropdownMenu.Item>
346
+ </DropdownMenu.Content>
347
+ </DropdownMenu.Root>
149
348
  {/if}
150
349
  </div>
151
- <div class="flex items-center gap-2">
152
- <Button
153
- variant="ghost"
154
- size="sm"
155
- onclick={() => {
156
- handleEditItem(index);
157
- }}
158
- class="h-8 w-8 p-0"
159
- title={readonly ? 'View item' : 'Edit item'}
160
- >
161
- {#if readonly}
162
- <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
163
- <path
164
- stroke-linecap="round"
165
- stroke-linejoin="round"
166
- stroke-width="2"
167
- d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
168
- />
169
- <path
170
- stroke-linecap="round"
171
- stroke-linejoin="round"
172
- stroke-width="2"
173
- d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
174
- />
175
- </svg>
176
- {:else}
177
- <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
178
- <path
179
- stroke-linecap="round"
180
- stroke-linejoin="round"
181
- stroke-width="2"
182
- d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
183
- />
184
- </svg>
350
+ {/if}
351
+ {/each}
352
+ </div>
353
+ {/if}
354
+
355
+ <!-- Add primitive item section -->
356
+ {#if !readonly}
357
+ <Button variant="outline" class="w-full" onclick={handleAddPrimitive}>
358
+ <svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
359
+ <path
360
+ stroke-linecap="round"
361
+ stroke-linejoin="round"
362
+ stroke-width="2"
363
+ d="M12 4v16m8-8H4"
364
+ />
365
+ </svg>
366
+ Add Item
367
+ </Button>
368
+ {/if}
369
+ {:else}
370
+ <!-- Object array UI (existing code) -->
371
+ {#if arrayValue.length === 0}
372
+ <!-- Empty state -->
373
+ <div
374
+ class="border-border/50 bg-muted/30 flex items-center justify-center rounded border border-dashed p-6"
375
+ >
376
+ <p class="text-muted-foreground text-sm">No items</p>
377
+ </div>
378
+ {:else}
379
+ <div class="space-y-2">
380
+ {#each arrayValue as item, index (index)}
381
+ <div class="border-border/50 space-y-2 rounded border p-3">
382
+ <div class="flex items-center justify-between">
383
+ <div class="flex items-center gap-2">
384
+ <span class="text-muted-foreground text-xs">#{index + 1}</span>
385
+ <h5 class="text-sm font-medium">{getItemTitle(item)}</h5>
386
+ {#if item._type}
387
+ <span class="bg-muted rounded px-2 py-1 text-xs">{item._type}</span>
185
388
  {/if}
186
- </Button>
187
-
188
- {#if !readonly}
389
+ </div>
390
+ <div class="flex items-center gap-2">
189
391
  <Button
190
392
  variant="ghost"
191
393
  size="sm"
192
- onclick={() => handleRemoveItem(index)}
193
- class="text-destructive hover:text-destructive h-8 w-8 p-0"
194
- title="Remove item"
394
+ onclick={() => {
395
+ handleEditItem(index);
396
+ }}
397
+ class="h-8 w-8 p-0"
398
+ title={readonly ? 'View item' : 'Edit item'}
195
399
  >
196
- <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
197
- <path
198
- stroke-linecap="round"
199
- stroke-linejoin="round"
200
- stroke-width="2"
201
- d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
202
- />
203
- </svg>
400
+ {#if readonly}
401
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
402
+ <path
403
+ stroke-linecap="round"
404
+ stroke-linejoin="round"
405
+ stroke-width="2"
406
+ d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
407
+ />
408
+ <path
409
+ stroke-linecap="round"
410
+ stroke-linejoin="round"
411
+ stroke-width="2"
412
+ d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
413
+ />
414
+ </svg>
415
+ {:else}
416
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
417
+ <path
418
+ stroke-linecap="round"
419
+ stroke-linejoin="round"
420
+ stroke-width="2"
421
+ d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
422
+ />
423
+ </svg>
424
+ {/if}
204
425
  </Button>
205
- {/if}
426
+
427
+ {#if !readonly}
428
+ <Button
429
+ variant="ghost"
430
+ size="sm"
431
+ onclick={() => handleRemoveItem(index)}
432
+ class="text-destructive hover:text-destructive h-8 w-8 p-0"
433
+ title="Remove item"
434
+ >
435
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
436
+ <path
437
+ stroke-linecap="round"
438
+ stroke-linejoin="round"
439
+ stroke-width="2"
440
+ d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
441
+ />
442
+ </svg>
443
+ </Button>
444
+ {/if}
445
+ </div>
206
446
  </div>
207
- </div>
208
447
 
209
- <!-- Show a preview of the item content -->
210
- <div class="text-muted-foreground pl-6 text-xs">
211
- {#if item.title || item.heading}
212
- {item.title || item.heading}
213
- {:else if item.description}
214
- {item.description.substring(0, 100)}{item.description.length > 100 ? '...' : ''}
215
- {:else}
216
- {readonly ? 'Click view to see details' : 'Click edit to configure this item'}
217
- {/if}
448
+ <!-- Show a preview of the item content -->
449
+ <div class="text-muted-foreground pl-6 text-xs">
450
+ {#if item.title || item.heading}
451
+ {item.title || item.heading}
452
+ {:else if item.description}
453
+ {item.description.substring(0, 100)}{item.description.length > 100 ? '...' : ''}
454
+ {:else}
455
+ {readonly ? 'Click view to see details' : 'Click edit to configure this item'}
456
+ {/if}
457
+ </div>
218
458
  </div>
219
- </div>
220
- {/each}
221
- </div>
222
- {/if}
223
-
224
- <!-- Add Item section (hidden for read-only) -->
225
- {#if !readonly}
226
- <div class="border-border border-t pt-2">
227
- <DropdownMenu.Root>
228
- <DropdownMenu.Trigger>
229
- {#snippet child({ props })}
230
- <Button {...props} variant="outline" class="w-full cursor-pointer">
231
- <svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
232
- <path
233
- stroke-linecap="round"
234
- stroke-linejoin="round"
235
- stroke-width="2"
236
- d="M12 4v16m8-8H4"
237
- />
238
- </svg>
239
- Add Item
240
- </Button>
241
- {/snippet}
242
- </DropdownMenu.Trigger>
243
- <DropdownMenu.Content class="w-56">
244
- {#each availableTypes as type, index (index)}
245
- <DropdownMenu.Item onclick={() => handleTypeSelected(type.name)}>
246
- {type.title}
247
- </DropdownMenu.Item>
248
- {/each}
249
- </DropdownMenu.Content>
250
- </DropdownMenu.Root>
251
- </div>
459
+ {/each}
460
+ </div>
461
+ {/if}
462
+
463
+ <!-- Add Item section (hidden for read-only) -->
464
+ {#if !readonly}
465
+ <div class="border-border border-t pt-2">
466
+ <DropdownMenu.Root>
467
+ <DropdownMenu.Trigger>
468
+ {#snippet child({ props })}
469
+ <Button {...props} variant="outline" class="w-full cursor-pointer">
470
+ <svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
471
+ <path
472
+ stroke-linecap="round"
473
+ stroke-linejoin="round"
474
+ stroke-width="2"
475
+ d="M12 4v16m8-8H4"
476
+ />
477
+ </svg>
478
+ Add Item
479
+ </Button>
480
+ {/snippet}
481
+ </DropdownMenu.Trigger>
482
+ <DropdownMenu.Content class="w-56">
483
+ {#each availableTypes as type, index (index)}
484
+ <DropdownMenu.Item onclick={() => handleTypeSelected(type.name)}>
485
+ {type.title}
486
+ </DropdownMenu.Item>
487
+ {/each}
488
+ </DropdownMenu.Content>
489
+ </DropdownMenu.Root>
490
+ </div>
491
+ {/if}
252
492
  {/if}
253
493
  </div>
254
494
 
@@ -262,5 +502,56 @@
262
502
  onSave={handleModalSave}
263
503
  {onOpenReference}
264
504
  {readonly}
505
+ {organizationId}
265
506
  />
266
507
  {/if}
508
+
509
+ <!-- Image upload modal -->
510
+ {#if imageModalOpen}
511
+ <div
512
+ class="bg-background/80 backdrop-blur-xs fixed bottom-0 left-0 right-0 top-12 z-[100] flex items-center justify-center p-6 sm:absolute sm:top-0 sm:p-4"
513
+ onclick={(e) => {
514
+ if (e.target === e.currentTarget) handleImageModalClose();
515
+ }}
516
+ onkeydown={(e) => {
517
+ if (e.key === 'Escape') handleImageModalClose();
518
+ }}
519
+ role="button"
520
+ tabindex="-1"
521
+ >
522
+ <Card.Root class="flex max-h-[85vh] w-full max-w-2xl flex-col overflow-hidden shadow-lg">
523
+ <Card.Header class="border-b">
524
+ <div class="flex items-center justify-between">
525
+ <div>
526
+ <Card.Title>{field.title} - Add Image</Card.Title>
527
+ <Card.Description>Upload a new image to add to the array</Card.Description>
528
+ </div>
529
+ <Button variant="ghost" size="icon" onclick={handleImageModalClose}>
530
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
531
+ <path
532
+ stroke-linecap="round"
533
+ stroke-linejoin="round"
534
+ stroke-width="2"
535
+ d="M6 18L18 6M6 6l12 12"
536
+ />
537
+ </svg>
538
+ </Button>
539
+ </div>
540
+ </Card.Header>
541
+
542
+ <Card.Content class="flex-1 overflow-auto">
543
+ <ImageField
544
+ field={{
545
+ ...field.of?.[0],
546
+ name: 'image',
547
+ type: 'image',
548
+ title: 'Image'
549
+ }}
550
+ value={imageModalValue}
551
+ onUpdate={handleImageUpload}
552
+ {organizationId}
553
+ />
554
+ </Card.Content>
555
+ </Card.Root>
556
+ </div>
557
+ {/if}
@@ -5,6 +5,7 @@ interface Props {
5
5
  onUpdate: (value: any) => void;
6
6
  onOpenReference?: (documentId: string, documentType: string) => void;
7
7
  readonly?: boolean;
8
+ organizationId?: string;
8
9
  }
9
10
  declare const ArrayField: import("svelte").Component<Props, {}, "">;
10
11
  type ArrayField = ReturnType<typeof ArrayField>;
@@ -1 +1 @@
1
- {"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;AAMtF,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AA0NF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ArrayField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ArrayField.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;AAQtF,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAobF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}