@aphexcms/cms-core 0.1.17 → 0.2.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 (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 +59 -16
  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 +72 -17
  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 +20 -7
  31. package/dist/components/admin/fields/ImageField.svelte.d.ts +1 -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 +1 -3
  35. package/dist/components/admin/fields/SlugField.svelte.d.ts.map +1 -1
  36. package/dist/components/admin/fields/StringField.svelte +156 -12
  37. package/dist/components/admin/fields/StringField.svelte.d.ts +3 -2
  38. package/dist/components/admin/fields/StringField.svelte.d.ts.map +1 -1
  39. package/dist/components/admin/fields/URLField.svelte +41 -0
  40. package/dist/components/admin/fields/URLField.svelte.d.ts +14 -0
  41. package/dist/components/admin/fields/URLField.svelte.d.ts.map +1 -0
  42. package/dist/components/index.d.ts +0 -1
  43. package/dist/components/index.d.ts.map +1 -1
  44. package/dist/components/index.js +0 -1
  45. package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts +1 -8
  46. package/dist/components/layout/sidebar/AppSidebar.svelte.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 +129 -35
  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 +129 -35
  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
@@ -3,6 +3,7 @@
3
3
  import * as Card from '@aphexcms/ui/shadcn/card';
4
4
  import type { SchemaType } from 'src/types/schemas.js';
5
5
  import SchemaField from './SchemaField.svelte';
6
+ import { getDefaultValueForFieldType } from '../../utils/field-defaults';
6
7
 
7
8
  interface Props {
8
9
  open: boolean;
@@ -13,6 +14,7 @@
13
14
  onUpdate?: (value: Record<string, any>) => void; // For real-time updates
14
15
  onOpenReference?: (documentId: string, documentType: string) => void;
15
16
  readonly?: boolean;
17
+ organizationId?: string; // For asset uploads to org-specific storage
16
18
  }
17
19
 
18
20
  // TODO: add onUpdate to auto save
@@ -24,7 +26,8 @@
24
26
  onSave,
25
27
  onUpdate,
26
28
  onOpenReference,
27
- readonly = false
29
+ readonly = false,
30
+ organizationId
28
31
  }: Props = $props();
29
32
 
30
33
  // Initialize editing data with defaults and existing values
@@ -33,10 +36,17 @@
33
36
 
34
37
  if (schema?.fields) {
35
38
  schema.fields.forEach((field) => {
36
- if (field.type === 'boolean' && 'initialValue' in field) {
37
- initialData[field.name] = field.initialValue;
39
+ if ('initialValue' in field && field.initialValue !== undefined) {
40
+ // Only use literal initialValue (skip functions to keep this synchronous)
41
+ if (typeof field.initialValue !== 'function') {
42
+ initialData[field.name] = field.initialValue;
43
+ } else {
44
+ // Function-based initialValues are skipped for nested items
45
+ // They will use field type defaults instead
46
+ initialData[field.name] = getDefaultValueForFieldType(field.type);
47
+ }
38
48
  } else {
39
- initialData[field.name] = '';
49
+ initialData[field.name] = getDefaultValueForFieldType(field.type);
40
50
  }
41
51
  });
42
52
  }
@@ -116,6 +126,7 @@
116
126
  }}
117
127
  {onOpenReference}
118
128
  {readonly}
129
+ {organizationId}
119
130
  />
120
131
  {/each}
121
132
  {/if}
@@ -8,6 +8,7 @@ interface Props {
8
8
  onUpdate?: (value: Record<string, any>) => void;
9
9
  onOpenReference?: (documentId: string, documentType: string) => void;
10
10
  readonly?: boolean;
11
+ organizationId?: string;
11
12
  }
12
13
  declare const ObjectModal: import("svelte").Component<Props, {}, "">;
13
14
  type ObjectModal = ReturnType<typeof ObjectModal>;
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectModal.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/ObjectModal.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAItD,UAAU,KAAK;IACd,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAChD,eAAe,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACrE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AA+GF,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ObjectModal.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/ObjectModal.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAKtD,UAAU,KAAK;IACd,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,IAAI,CAAC;IAChD,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;AAwHF,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -2,22 +2,33 @@
2
2
  import { Label } from '@aphexcms/ui/shadcn/label';
3
3
  import { Badge } from '@aphexcms/ui/shadcn/badge';
4
4
  import * as Alert from '@aphexcms/ui/shadcn/alert';
5
- import type { Field } from 'src/types/schemas.js';
5
+ import type {
6
+ Field,
7
+ DateField as DateFieldType,
8
+ DateTimeField as DateTimeFieldType
9
+ } from 'src/types/schemas.js';
6
10
  import {
7
11
  isFieldRequired,
8
12
  validateField,
9
13
  getValidationClasses,
10
14
  type ValidationError
11
15
  } from '../../field-validation/utils';
16
+ import {
17
+ convertDateToUserFormat,
18
+ convertDateTimeToUserFormat
19
+ } from '../../field-validation/date-utils';
12
20
 
13
21
  // Import individual field components
14
22
  import StringField from './fields/StringField.svelte';
15
23
  import SlugField from './fields/SlugField.svelte';
24
+ import URLField from './fields/URLField.svelte';
16
25
  import TextareaField from './fields/TextareaField.svelte';
17
26
  import NumberField from './fields/NumberField.svelte';
18
27
  import BooleanField from './fields/BooleanField.svelte';
19
28
  import ImageField from './fields/ImageField.svelte';
20
29
  import ArrayField from './fields/ArrayField.svelte';
30
+ import DateField from './fields/DateField.svelte';
31
+ import DateTimeField from './fields/DateTimeField.svelte';
21
32
  import ReferenceField from './fields/ReferenceField.svelte';
22
33
  import SchemaField from './SchemaField.svelte';
23
34
 
@@ -31,6 +42,7 @@
31
42
  schemaType?: string; // Document type
32
43
  parentPath?: string; // Parent field path for nested fields
33
44
  readonly?: boolean; // Read-only mode for viewers
45
+ organizationId?: string; // Document's organization ID for asset uploads
34
46
  }
35
47
 
36
48
  let {
@@ -42,7 +54,8 @@
42
54
  doValidation,
43
55
  schemaType,
44
56
  parentPath,
45
- readonly = false
57
+ readonly = false,
58
+ organizationId
46
59
  }: Props = $props();
47
60
 
48
61
  // Build full field path
@@ -54,9 +67,47 @@
54
67
  // Real-time validation for wrapper display
55
68
  export async function performValidation(currentValue: any, context: any = {}) {
56
69
  validationErrors = []; // Clear previous errors
57
- const result = await validateField(field, currentValue, context);
70
+
71
+ console.log(`[SchemaField.performValidation] Field "${field.name}" type="${field.type}"`, {
72
+ currentValue,
73
+ context
74
+ });
75
+
76
+ // Convert date/datetime values from ISO to user format for validation
77
+ let valueForValidation = currentValue;
78
+ if (field.type === 'date' && currentValue && typeof currentValue === 'string') {
79
+ const dateField = field as DateFieldType;
80
+ const userFormat = dateField.options?.dateFormat || 'YYYY-MM-DD';
81
+ console.log(`[SchemaField.performValidation] Converting DATE field "${field.name}"`, {
82
+ currentValue,
83
+ userFormat
84
+ });
85
+ valueForValidation = convertDateToUserFormat(currentValue, userFormat);
86
+ console.log(`[SchemaField.performValidation] DATE converted`, {
87
+ valueForValidation
88
+ });
89
+ } else if (field.type === 'datetime' && currentValue && typeof currentValue === 'string') {
90
+ const dateTimeField = field as DateTimeFieldType;
91
+ const dateFormat = dateTimeField.options?.dateFormat || 'YYYY-MM-DD';
92
+ const timeFormat = dateTimeField.options?.timeFormat || 'HH:mm';
93
+ console.log(`[SchemaField.performValidation] Converting DATETIME field "${field.name}"`, {
94
+ currentValue,
95
+ dateFormat,
96
+ timeFormat
97
+ });
98
+ valueForValidation = convertDateTimeToUserFormat(currentValue, dateFormat, timeFormat);
99
+ console.log(`[SchemaField.performValidation] DATETIME converted`, {
100
+ valueForValidation
101
+ });
102
+ }
103
+
104
+ const result = await validateField(field, valueForValidation, context);
105
+ console.log(`[SchemaField.performValidation] Validation result for "${field.name}"`, {
106
+ errors: result.errors
107
+ });
58
108
  validationErrors = result.errors;
59
109
  }
110
+
60
111
  // Computed values
61
112
  const hasErrors = $derived(validationErrors.filter((e) => e.level === 'error').length > 0);
62
113
  const validationClasses = $derived(getValidationClasses(hasErrors));
@@ -109,15 +160,21 @@
109
160
 
110
161
  <!-- Field type routing to individual components -->
111
162
  {#if field.type === 'string'}
112
- <StringField {field} {value} {onUpdate} {validationClasses} {readonly} />
163
+ <StringField {field} {value} {documentData} {onUpdate} {validationClasses} {readonly} />
113
164
  {:else if field.type === 'text'}
114
165
  <TextareaField {field} {value} {onUpdate} {validationClasses} {readonly} />
115
166
  {:else if field.type === 'slug'}
116
167
  <SlugField {field} {value} {documentData} {onUpdate} {validationClasses} {readonly} />
168
+ {:else if field.type === 'url'}
169
+ <URLField {field} {value} {onUpdate} {validationClasses} {readonly} />
117
170
  {:else if field.type === 'number'}
118
171
  <NumberField {field} {value} {onUpdate} {validationClasses} {readonly} />
119
172
  {:else if field.type === 'boolean'}
120
173
  <BooleanField {field} {value} {onUpdate} {validationClasses} {readonly} />
174
+ {:else if field.type === 'date'}
175
+ <DateField {field} {value} {onUpdate} {validationClasses} {readonly} />
176
+ {:else if field.type === 'datetime'}
177
+ <DateTimeField {field} {value} {onUpdate} {validationClasses} {readonly} />
121
178
 
122
179
  <!-- Image Field -->
123
180
  {:else if field.type === 'image'}
@@ -129,6 +186,7 @@
129
186
  {schemaType}
130
187
  {fieldPath}
131
188
  {readonly}
189
+ {organizationId}
132
190
  />
133
191
 
134
192
  <!-- Object Field -->
@@ -145,13 +203,14 @@
145
203
  {schemaType}
146
204
  parentPath={fieldPath}
147
205
  {readonly}
206
+ {organizationId}
148
207
  />
149
208
  {/each}
150
209
  </div>
151
210
 
152
211
  <!-- Array Field -->
153
212
  {:else if field.type === 'array' && field.of}
154
- <ArrayField {field} {value} {onUpdate} {onOpenReference} {readonly} />
213
+ <ArrayField {field} {value} {onUpdate} {onOpenReference} {readonly} {organizationId} />
155
214
 
156
215
  <!-- Reference Field -->
157
216
  {:else if field.type === 'reference' && field.to}
@@ -10,6 +10,7 @@ interface Props {
10
10
  schemaType?: string;
11
11
  parentPath?: string;
12
12
  readonly?: boolean;
13
+ organizationId?: string;
13
14
  }
14
15
  declare const SchemaField: import("svelte").Component<Props, {
15
16
  performValidation: (currentValue: any, context?: any) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"SchemaField.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/SchemaField.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAgBlD,OAAO,WAAW,MAAM,sBAAsB,CAAC;AAG9C,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,GAAG,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC,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,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AA4IF,QAAA,MAAM,WAAW;sCArGgC,GAAG,YAAW,GAAG;MAqGT,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"SchemaField.svelte.d.ts","sourceRoot":"","sources":["../../../src/lib/components/admin/SchemaField.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,KAAK,EAGL,MAAM,sBAAsB,CAAC;AAuB/B,OAAO,WAAW,MAAM,sBAAsB,CAAC;AAG9C,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,GAAG,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC,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,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AA6LF,QAAA,MAAM,WAAW;sCAjJgC,GAAG,YAAW,GAAG;MAiJT,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -10,6 +10,7 @@
10
10
  import { getSchemaContext } from '../../../schema-context.svelte';
11
11
  import ObjectModal from '../ObjectModal.svelte';
12
12
  import ImageField from './ImageField.svelte';
13
+ import { getDefaultValueForFieldType } from '../../../utils/field-defaults';
13
14
 
14
15
  interface Props {
15
16
  field: ArrayFieldType;
@@ -17,17 +18,51 @@
17
18
  onUpdate: (value: any) => void;
18
19
  onOpenReference?: (documentId: string, documentType: string) => void;
19
20
  readonly?: boolean;
21
+ organizationId?: string; // For asset uploads to org-specific storage
20
22
  }
21
23
 
22
- 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();
23
32
 
24
33
  // Get schemas from context
25
34
  const schemas = getSchemaContext();
26
35
 
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
+
27
58
  // Determine if this is a primitive array or object array
28
- // If the type is not found in schemas, it's a primitive type
59
+ // If the type is not found in schemas AND has no inline fields, it's a primitive type
29
60
  const isPrimitiveArray = $derived(
30
- field.of && field.of.length > 0 && field.of[0]?.type && !getSchemaByName(schemas, field.of[0].type)
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
31
66
  );
32
67
  const primitiveType = $derived(isPrimitiveArray ? field.of?.[0]?.type : null);
33
68
 
@@ -93,8 +128,8 @@
93
128
  function handleTypeSelected(selectedType: string) {
94
129
  if (readonly || !selectedType) return;
95
130
 
96
- // Get the schema for the selected type
97
- const schema = getSchemaByName(schemas, selectedType);
131
+ // Get the schema for the selected type (inline or from registry)
132
+ const schema = getSchemaForType(selectedType);
98
133
  if (!schema) return;
99
134
 
100
135
  // Initialize empty object with default values
@@ -102,10 +137,17 @@
102
137
 
103
138
  if (schema.fields) {
104
139
  schema.fields.forEach((field) => {
105
- if (field.type === 'boolean' && 'initialValue' in field) {
106
- 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
+ }
107
149
  } else {
108
- newItem[field.name] = '';
150
+ newItem[field.name] = getDefaultValueForFieldType(field.type);
109
151
  }
110
152
  });
111
153
  }
@@ -122,7 +164,7 @@
122
164
  const item = arrayValue[index];
123
165
  if (!item._type) return;
124
166
 
125
- const schema = getSchemaByName(schemas, item._type);
167
+ const schema = getSchemaForType(item._type);
126
168
  if (!schema) return;
127
169
 
128
170
  editingIndex = index;
@@ -197,7 +239,9 @@
197
239
  <!-- Primitive array UI -->
198
240
  {#if arrayValue.length === 0}
199
241
  <!-- Empty state -->
200
- <div class="border-border/50 bg-muted/30 flex items-center justify-center rounded border border-dashed p-6">
242
+ <div
243
+ class="border-border/50 bg-muted/30 flex items-center justify-center rounded border border-dashed p-6"
244
+ >
201
245
  <p class="text-muted-foreground text-sm">No items</p>
202
246
  </div>
203
247
  {:else}
@@ -225,6 +269,7 @@
225
269
  }}
226
270
  {readonly}
227
271
  compact={true}
272
+ {organizationId}
228
273
  />
229
274
  {:else}
230
275
  <!-- Always-editable primitive with options menu -->
@@ -243,7 +288,7 @@
243
288
  <Textarea
244
289
  value={item}
245
290
  oninput={(e) => handleUpdatePrimitive(index, e.currentTarget.value)}
246
- readonly={readonly}
291
+ {readonly}
247
292
  class="flex-1"
248
293
  rows={3}
249
294
  placeholder="Enter text..."
@@ -252,8 +297,9 @@
252
297
  <Input
253
298
  type="number"
254
299
  value={item}
255
- oninput={(e) => handleUpdatePrimitive(index, parseFloat(e.currentTarget.value) || 0)}
256
- readonly={readonly}
300
+ oninput={(e) =>
301
+ handleUpdatePrimitive(index, parseFloat(e.currentTarget.value) || 0)}
302
+ {readonly}
257
303
  class="flex-1"
258
304
  placeholder="Enter number..."
259
305
  />
@@ -261,7 +307,7 @@
261
307
  <Input
262
308
  value={item}
263
309
  oninput={(e) => handleUpdatePrimitive(index, e.currentTarget.value)}
264
- readonly={readonly}
310
+ {readonly}
265
311
  class="flex-1"
266
312
  placeholder="Enter value..."
267
313
  />
@@ -282,7 +328,12 @@
282
328
  </DropdownMenu.Trigger>
283
329
  <DropdownMenu.Content align="end">
284
330
  <DropdownMenu.Item onclick={() => handleRemoveItem(index)}>
285
- <svg class="mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
331
+ <svg
332
+ class="mr-2 h-4 w-4"
333
+ fill="none"
334
+ viewBox="0 0 24 24"
335
+ stroke="currentColor"
336
+ >
286
337
  <path
287
338
  stroke-linecap="round"
288
339
  stroke-linejoin="round"
@@ -319,7 +370,9 @@
319
370
  <!-- Object array UI (existing code) -->
320
371
  {#if arrayValue.length === 0}
321
372
  <!-- Empty state -->
322
- <div class="border-border/50 bg-muted/30 flex items-center justify-center rounded border border-dashed p-6">
373
+ <div
374
+ class="border-border/50 bg-muted/30 flex items-center justify-center rounded border border-dashed p-6"
375
+ >
323
376
  <p class="text-muted-foreground text-sm">No items</p>
324
377
  </div>
325
378
  {:else}
@@ -449,13 +502,14 @@
449
502
  onSave={handleModalSave}
450
503
  {onOpenReference}
451
504
  {readonly}
505
+ {organizationId}
452
506
  />
453
507
  {/if}
454
508
 
455
509
  <!-- Image upload modal -->
456
510
  {#if imageModalOpen}
457
511
  <div
458
- class="bg-background/80 fixed bottom-0 left-0 right-0 top-12 z-[100] flex items-center justify-center p-6 backdrop-blur-xs sm:absolute sm:top-0 sm:p-4"
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"
459
513
  onclick={(e) => {
460
514
  if (e.target === e.currentTarget) handleImageModalClose();
461
515
  }}
@@ -495,6 +549,7 @@
495
549
  }}
496
550
  value={imageModalValue}
497
551
  onUpdate={handleImageUpload}
552
+ {organizationId}
498
553
  />
499
554
  </Card.Content>
500
555
  </Card.Root>
@@ -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":"AASA,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAc,MAAM,wBAAwB,CAAC;AAOtF,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;AA0YF,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"}
@@ -0,0 +1,145 @@
1
+ <script lang="ts">
2
+ import CalendarIcon from '@lucide/svelte/icons/calendar';
3
+ import { type DateValue, parseDate } from '@internationalized/date';
4
+ import { Input } from '@aphexcms/ui/shadcn/input';
5
+ import { Calendar } from '@aphexcms/ui/shadcn/calendar';
6
+ import * as Popover from '@aphexcms/ui/shadcn/popover';
7
+ import type { DateField } from '../../../types/schemas';
8
+ import dayjs from 'dayjs';
9
+ import customParseFormat from 'dayjs/plugin/customParseFormat';
10
+
11
+ // Enable strict parsing
12
+ dayjs.extend(customParseFormat);
13
+
14
+ interface Props {
15
+ field: DateField;
16
+ value: string | null; // Always stored as ISO YYYY-MM-DD
17
+ onUpdate: (value: string | null) => void;
18
+ validationClasses?: string;
19
+ onBlur?: (event: any) => void;
20
+ onFocus?: (event: any) => void;
21
+ readonly?: boolean;
22
+ }
23
+
24
+ let {
25
+ field,
26
+ value,
27
+ onUpdate,
28
+ validationClasses,
29
+ onBlur,
30
+ onFocus,
31
+ readonly = false
32
+ }: Props = $props();
33
+
34
+ // Get date format from options or use default
35
+ const dateFormat = $derived(field.options?.dateFormat || 'YYYY-MM-DD');
36
+
37
+ // Track local input value for display
38
+ let inputValue = $state('');
39
+
40
+ // Convert stored ISO value to display format
41
+ // Only update if the formatted value is different to avoid overwriting user input
42
+ $effect(() => {
43
+ if (!value || value === '') {
44
+ if (inputValue !== '') {
45
+ inputValue = '';
46
+ }
47
+ } else {
48
+ // Convert ISO (YYYY-MM-DD) to display format
49
+ const date = dayjs(value, 'YYYY-MM-DD', true);
50
+ if (date.isValid()) {
51
+ const formatted = date.format(dateFormat);
52
+ // Only update if different to avoid overwriting user's in-progress typing
53
+ if (inputValue !== formatted) {
54
+ inputValue = formatted;
55
+ }
56
+ } else {
57
+ // Value is not ISO (might be user's in-progress input or invalid)
58
+ if (inputValue !== value) {
59
+ inputValue = value;
60
+ }
61
+ }
62
+ }
63
+ });
64
+
65
+ // Convert string value to DateValue for calendar
66
+ const dateValue = $derived.by(() => {
67
+ if (!value || value === '') return undefined;
68
+
69
+ try {
70
+ // Value is stored as ISO date string (YYYY-MM-DD)
71
+ return parseDate(value);
72
+ } catch (err) {
73
+ console.error('Failed to parse date:', value, err);
74
+ return undefined;
75
+ }
76
+ });
77
+
78
+ // Handle date selection from calendar
79
+ function handleDateChange(newValue: DateValue | undefined) {
80
+ if (!newValue) {
81
+ onUpdate(null);
82
+ } else {
83
+ // Convert DateValue to ISO string (YYYY-MM-DD) for storage
84
+ const isoString = newValue.toString();
85
+ onUpdate(isoString);
86
+ }
87
+ }
88
+
89
+ // Handle manual input changes
90
+ function handleInputChange(event: Event) {
91
+ const target = event.target as HTMLInputElement;
92
+ const displayValue = target.value;
93
+ inputValue = displayValue;
94
+
95
+ if (displayValue === '') {
96
+ onUpdate(null);
97
+ return;
98
+ }
99
+
100
+ // Parse according to the chosen format
101
+ const parsed = dayjs(displayValue, dateFormat, true);
102
+
103
+ if (parsed.isValid()) {
104
+ // Convert to ISO for storage
105
+ const isoString = parsed.format('YYYY-MM-DD');
106
+ onUpdate(isoString);
107
+ } else {
108
+ // Still update with the invalid value so autosave triggers
109
+ // Validation will catch the error
110
+ onUpdate(displayValue);
111
+ }
112
+ }
113
+ </script>
114
+
115
+ <div class="space-y-2">
116
+ <div class="flex w-full gap-2">
117
+ <Input
118
+ id={field.name}
119
+ type="text"
120
+ placeholder={dateFormat}
121
+ value={inputValue}
122
+ oninput={handleInputChange}
123
+ onblur={onBlur}
124
+ onfocus={onFocus}
125
+ class="flex-1 {validationClasses}"
126
+ disabled={readonly}
127
+ />
128
+ <Popover.Root>
129
+ <Popover.Trigger
130
+ class="border-input bg-background ring-offset-background hover:bg-accent hover:text-accent-foreground focus-visible:ring-ring inline-flex h-10 w-10 items-center justify-center whitespace-nowrap rounded-md border text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50"
131
+ disabled={readonly}
132
+ >
133
+ <CalendarIcon class="h-4 w-4" />
134
+ </Popover.Trigger>
135
+ <Popover.Content class="w-auto p-0">
136
+ <Calendar
137
+ type="single"
138
+ value={dateValue}
139
+ onValueChange={handleDateChange}
140
+ captionLayout="dropdown"
141
+ />
142
+ </Popover.Content>
143
+ </Popover.Root>
144
+ </div>
145
+ </div>
@@ -0,0 +1,14 @@
1
+ import type { DateField } from '../../../types/schemas.js';
2
+ interface Props {
3
+ field: DateField;
4
+ value: string | null;
5
+ onUpdate: (value: string | null) => void;
6
+ validationClasses?: string;
7
+ onBlur?: (event: any) => void;
8
+ onFocus?: (event: any) => void;
9
+ readonly?: boolean;
10
+ }
11
+ declare const DateField: import("svelte").Component<Props, {}, "">;
12
+ type DateField = ReturnType<typeof DateField>;
13
+ export default DateField;
14
+ //# sourceMappingURL=DateField.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DateField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/DateField.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAKvD,UAAU,KAAK;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACzC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACnB;AA0HF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}