@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.
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +7 -1
- package/dist/api/types.d.ts +2 -0
- package/dist/api/types.d.ts.map +1 -1
- package/dist/cli/generate-types.js +62 -17
- package/dist/cli/index.js +1 -1
- package/dist/client/index.d.ts +0 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +0 -1
- package/dist/components/AdminApp.svelte +278 -45
- package/dist/components/AdminApp.svelte.d.ts +2 -0
- package/dist/components/AdminApp.svelte.d.ts.map +1 -1
- package/dist/components/admin/DocumentEditor.svelte +60 -13
- package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -1
- package/dist/components/admin/ObjectModal.svelte +15 -4
- package/dist/components/admin/ObjectModal.svelte.d.ts +1 -0
- package/dist/components/admin/ObjectModal.svelte.d.ts.map +1 -1
- package/dist/components/admin/SchemaField.svelte +64 -5
- package/dist/components/admin/SchemaField.svelte.d.ts +1 -0
- package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ArrayField.svelte +402 -111
- package/dist/components/admin/fields/ArrayField.svelte.d.ts +1 -0
- package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/DateField.svelte +145 -0
- package/dist/components/admin/fields/DateField.svelte.d.ts +14 -0
- package/dist/components/admin/fields/DateField.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/DateTimeField.svelte +225 -0
- package/dist/components/admin/fields/DateTimeField.svelte.d.ts +14 -0
- package/dist/components/admin/fields/DateTimeField.svelte.d.ts.map +1 -0
- package/dist/components/admin/fields/ImageField.svelte +221 -110
- package/dist/components/admin/fields/ImageField.svelte.d.ts +2 -0
- package/dist/components/admin/fields/ImageField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/ReferenceField.svelte +1 -1
- package/dist/components/admin/fields/SlugField.svelte +21 -13
- package/dist/components/admin/fields/SlugField.svelte.d.ts +2 -2
- package/dist/components/admin/fields/SlugField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/StringField.svelte +156 -12
- package/dist/components/admin/fields/StringField.svelte.d.ts +3 -2
- package/dist/components/admin/fields/StringField.svelte.d.ts.map +1 -1
- package/dist/components/admin/fields/URLField.svelte +41 -0
- package/dist/components/admin/fields/URLField.svelte.d.ts +14 -0
- package/dist/components/admin/fields/URLField.svelte.d.ts.map +1 -0
- package/dist/components/index.d.ts +0 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +0 -1
- package/dist/db/interfaces/asset.d.ts.map +1 -1
- package/dist/db/interfaces/document.d.ts +0 -2
- package/dist/db/interfaces/document.d.ts.map +1 -1
- package/dist/db/interfaces/index.d.ts +2 -1
- package/dist/db/interfaces/index.d.ts.map +1 -1
- package/dist/db/interfaces/user.d.ts +2 -0
- package/dist/db/interfaces/user.d.ts.map +1 -1
- package/dist/db/utils/reference-resolver.js +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +3 -0
- package/dist/field-validation/date-utils.d.ts +30 -0
- package/dist/field-validation/date-utils.d.ts.map +1 -0
- package/dist/field-validation/date-utils.js +147 -0
- package/dist/field-validation/rule.d.ts +4 -0
- package/dist/field-validation/rule.d.ts.map +1 -1
- package/dist/field-validation/rule.js +170 -4
- package/dist/field-validation/utils.d.ts +7 -3
- package/dist/field-validation/utils.d.ts.map +1 -1
- package/dist/field-validation/utils.js +130 -38
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +38 -21
- package/dist/lib/field-validation/date-utils.js +147 -0
- package/dist/lib/field-validation/rule.js +170 -4
- package/dist/lib/field-validation/utils.js +130 -38
- package/dist/local-api/collection-api.d.ts +16 -4
- package/dist/local-api/collection-api.d.ts.map +1 -1
- package/dist/local-api/collection-api.js +51 -17
- package/dist/local-api/index.d.ts +1 -1
- package/dist/local-api/index.d.ts.map +1 -1
- package/dist/routes/assets-cdn.d.ts.map +1 -1
- package/dist/routes/assets-cdn.js +14 -7
- package/dist/routes/assets.d.ts.map +1 -1
- package/dist/routes/assets.js +6 -1
- package/dist/routes/documents-by-id.d.ts.map +1 -1
- package/dist/routes/documents-by-id.js +18 -7
- package/dist/routes/documents-publish.js +2 -2
- package/dist/routes/documents-query.d.ts +3 -1
- package/dist/routes/documents-query.d.ts.map +1 -1
- package/dist/routes/documents-query.js +6 -2
- package/dist/routes/documents.d.ts.map +1 -1
- package/dist/routes/documents.js +20 -4
- package/dist/routes/index.d.ts +1 -0
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +2 -0
- package/dist/routes/user-preferences.d.ts +4 -0
- package/dist/routes/user-preferences.d.ts.map +1 -0
- package/dist/routes/user-preferences.js +77 -0
- package/dist/schema-utils/utils.d.ts +4 -0
- package/dist/schema-utils/utils.d.ts.map +1 -1
- package/dist/schema-utils/utils.js +23 -2
- package/dist/schema-utils/validator.d.ts +4 -0
- package/dist/schema-utils/validator.d.ts.map +1 -1
- package/dist/schema-utils/validator.js +120 -0
- package/dist/types/filters.d.ts +13 -0
- package/dist/types/filters.d.ts.map +1 -1
- package/dist/types/organization.d.ts +3 -0
- package/dist/types/organization.d.ts.map +1 -1
- package/dist/types/schemas.d.ts +67 -7
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/utils/default-orderings.d.ts +10 -0
- package/dist/utils/default-orderings.d.ts.map +1 -0
- package/dist/utils/default-orderings.js +63 -0
- package/dist/utils/field-defaults.d.ts +8 -0
- package/dist/utils/field-defaults.d.ts.map +1 -0
- package/dist/utils/field-defaults.js +20 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/initial-value-helpers.d.ts +50 -0
- package/dist/utils/initial-value-helpers.d.ts.map +1 -0
- package/dist/utils/initial-value-helpers.js +70 -0
- package/package.json +6 -4
- package/dist/components/admin/DocumentTypesList.svelte +0 -97
- package/dist/components/admin/DocumentTypesList.svelte.d.ts +0 -14
- 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 {
|
|
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
|
|
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 =
|
|
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 (
|
|
48
|
-
|
|
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 =
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
</
|
|
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={() =>
|
|
193
|
-
|
|
194
|
-
|
|
394
|
+
onclick={() => {
|
|
395
|
+
handleEditItem(index);
|
|
396
|
+
}}
|
|
397
|
+
class="h-8 w-8 p-0"
|
|
398
|
+
title={readonly ? 'View item' : 'Edit item'}
|
|
195
399
|
>
|
|
196
|
-
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
</DropdownMenu.
|
|
250
|
-
</
|
|
251
|
-
|
|
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":"
|
|
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"}
|