@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
@@ -0,0 +1,225 @@
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 { Button } from '@aphexcms/ui/shadcn/button';
6
+ import { Calendar } from '@aphexcms/ui/shadcn/calendar';
7
+ import * as Popover from '@aphexcms/ui/shadcn/popover';
8
+ import type { DateTimeField } from '../../../types/schemas';
9
+ import dayjs from 'dayjs';
10
+ import customParseFormat from 'dayjs/plugin/customParseFormat';
11
+ import utc from 'dayjs/plugin/utc';
12
+
13
+ // Enable strict parsing and UTC
14
+ dayjs.extend(customParseFormat);
15
+ dayjs.extend(utc);
16
+
17
+ interface Props {
18
+ field: DateTimeField;
19
+ value: string | null; // Always stored as ISO datetime UTC YYYY-MM-DDTHH:mm:ssZ
20
+ onUpdate: (value: string | null) => void;
21
+ validationClasses?: string;
22
+ onBlur?: (event: any) => void;
23
+ onFocus?: (event: any) => void;
24
+ readonly?: boolean;
25
+ }
26
+
27
+ let {
28
+ field,
29
+ value,
30
+ onUpdate,
31
+ validationClasses,
32
+ onBlur,
33
+ onFocus,
34
+ readonly = false
35
+ }: Props = $props();
36
+
37
+ // Get date and time formats from options or use defaults
38
+ const dateFormat = $derived(field.options?.dateFormat || 'YYYY-MM-DD');
39
+ const timeFormat = $derived(field.options?.timeFormat || 'HH:mm');
40
+ const displayFormat = $derived(`${dateFormat} ${timeFormat}`);
41
+
42
+ // Track local input value for display
43
+ let inputValue = $state('');
44
+ let timeValue = $state('00:00');
45
+
46
+ // Convert stored ISO value to display format
47
+ // Only update if the formatted value is different to avoid overwriting user input
48
+ $effect(() => {
49
+ if (!value || value === '') {
50
+ if (inputValue !== '') {
51
+ inputValue = '';
52
+ timeValue = '00:00';
53
+ }
54
+ } else {
55
+ // Parse the datetime value
56
+ const datetime = dayjs(value);
57
+ if (datetime.isValid()) {
58
+ const formatted = datetime.format(displayFormat);
59
+ const time = datetime.format('HH:mm');
60
+ // Only update if different to avoid overwriting user's in-progress typing
61
+ if (inputValue !== formatted) {
62
+ inputValue = formatted;
63
+ }
64
+ if (timeValue !== time) {
65
+ timeValue = time;
66
+ }
67
+ } else {
68
+ // Value is not ISO (might be user's in-progress input or invalid)
69
+ if (inputValue !== value) {
70
+ inputValue = value;
71
+ }
72
+ }
73
+ }
74
+ });
75
+
76
+ // Convert string value to DateValue for calendar
77
+ const dateValue = $derived.by(() => {
78
+ if (!value || value === '') return undefined;
79
+
80
+ try {
81
+ // Parse datetime and extract just the date part
82
+ const datetime = dayjs(value);
83
+ if (datetime.isValid()) {
84
+ return parseDate(datetime.format('YYYY-MM-DD'));
85
+ }
86
+ } catch (err) {
87
+ console.error('Failed to parse datetime:', value, err);
88
+ }
89
+ return undefined;
90
+ });
91
+
92
+ // Handle date selection from calendar
93
+ function handleDateChange(newValue: DateValue | undefined) {
94
+ if (!newValue) {
95
+ onUpdate(null);
96
+ } else {
97
+ // Combine selected date with current time and convert to UTC
98
+ const dateStr = newValue.toString(); // YYYY-MM-DD
99
+ const localDatetime = dayjs(`${dateStr} ${timeValue}`, 'YYYY-MM-DD HH:mm');
100
+ const utcDatetime = localDatetime.utc().format('YYYY-MM-DDTHH:mm:ss[Z]');
101
+ onUpdate(utcDatetime);
102
+ }
103
+ }
104
+
105
+ // Handle manual input changes
106
+ function handleInputChange(event: Event) {
107
+ const target = event.target as HTMLInputElement;
108
+ const displayValue = target.value;
109
+ inputValue = displayValue;
110
+
111
+ if (displayValue === '') {
112
+ onUpdate(null);
113
+ return;
114
+ }
115
+
116
+ // Parse according to the chosen format (date + time)
117
+ const parsed = dayjs(displayValue, displayFormat, true);
118
+
119
+ if (parsed.isValid()) {
120
+ // Convert to ISO datetime UTC
121
+ const utcDatetime = parsed.utc().format('YYYY-MM-DDTHH:mm:ss[Z]');
122
+ onUpdate(utcDatetime);
123
+ // Update time value for picker
124
+ timeValue = parsed.format('HH:mm');
125
+ } else {
126
+ // Still update with the invalid value so autosave triggers
127
+ // Validation will catch the error
128
+ onUpdate(displayValue);
129
+ }
130
+ }
131
+
132
+ // Handle time input changes
133
+ function handleTimeChange(event: Event) {
134
+ const target = event.target as HTMLInputElement;
135
+ const newTime = target.value; // HH:mm format
136
+ timeValue = newTime;
137
+
138
+ // If we have a date, update the datetime
139
+ if (value && value !== '') {
140
+ const datetime = dayjs(value);
141
+ if (datetime.isValid()) {
142
+ // Get the local date and combine with new time, then convert to UTC
143
+ const dateStr = datetime.format('YYYY-MM-DD');
144
+ const localDatetime = dayjs(`${dateStr} ${newTime}`, 'YYYY-MM-DD HH:mm');
145
+ const utcDatetime = localDatetime.utc().format('YYYY-MM-DDTHH:mm:ss[Z]');
146
+ onUpdate(utcDatetime);
147
+ }
148
+ }
149
+ }
150
+
151
+ // Set to current time
152
+ function setToCurrentTime() {
153
+ const now = dayjs();
154
+ timeValue = now.format('HH:mm');
155
+
156
+ // If we have a date, update the datetime
157
+ if (value && value !== '') {
158
+ const datetime = dayjs(value);
159
+ if (datetime.isValid()) {
160
+ // Get the local date and combine with current time, then convert to UTC
161
+ const dateStr = datetime.format('YYYY-MM-DD');
162
+ const localDatetime = dayjs(`${dateStr} ${timeValue}`, 'YYYY-MM-DD HH:mm');
163
+ const utcDatetime = localDatetime.utc().format('YYYY-MM-DDTHH:mm:ss[Z]');
164
+ onUpdate(utcDatetime);
165
+ }
166
+ }
167
+ }
168
+ </script>
169
+
170
+ <div class="space-y-2">
171
+ <div class="flex w-full gap-2">
172
+ <Input
173
+ id={field.name}
174
+ type="text"
175
+ placeholder={displayFormat}
176
+ value={inputValue}
177
+ oninput={handleInputChange}
178
+ onblur={onBlur}
179
+ onfocus={onFocus}
180
+ class="flex-1 {validationClasses}"
181
+ disabled={readonly}
182
+ />
183
+ <Popover.Root>
184
+ <Popover.Trigger
185
+ 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"
186
+ disabled={readonly}
187
+ >
188
+ <CalendarIcon class="h-4 w-4" />
189
+ </Popover.Trigger>
190
+ <Popover.Content class="w-auto p-0">
191
+ <Calendar
192
+ type="single"
193
+ value={dateValue}
194
+ onValueChange={handleDateChange}
195
+ captionLayout="dropdown"
196
+ />
197
+
198
+ <!-- Time Picker Section -->
199
+ <div class="border-border border-t">
200
+ <div class="flex items-center gap-2 p-3">
201
+ <div class="flex-1">
202
+ <Input
203
+ type="time"
204
+ value={timeValue}
205
+ oninput={handleTimeChange}
206
+ aria-label="Select time"
207
+ disabled={readonly}
208
+ class="w-full"
209
+ />
210
+ </div>
211
+ <Button
212
+ variant="outline"
213
+ size="sm"
214
+ onclick={setToCurrentTime}
215
+ disabled={readonly}
216
+ type="button"
217
+ >
218
+ Set to now
219
+ </Button>
220
+ </div>
221
+ </div>
222
+ </Popover.Content>
223
+ </Popover.Root>
224
+ </div>
225
+ </div>
@@ -0,0 +1,14 @@
1
+ import type { DateTimeField } from '../../../types/schemas.js';
2
+ interface Props {
3
+ field: DateTimeField;
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 DateTimeField: import("svelte").Component<Props, {}, "">;
12
+ type DateTimeField = ReturnType<typeof DateTimeField>;
13
+ export default DateTimeField;
14
+ //# sourceMappingURL=DateTimeField.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DateTimeField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/DateTimeField.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAM3D,UAAU,KAAK;IACd,KAAK,EAAE,aAAa,CAAC;IACrB,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;AA6LF,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -22,6 +22,7 @@
22
22
  fieldPath?: string;
23
23
  readonly?: boolean;
24
24
  compact?: boolean; // Compact mode for arrays
25
+ organizationId?: string; // Document's organization ID for asset uploads
25
26
  }
26
27
 
27
28
  let {
@@ -32,7 +33,8 @@
32
33
  schemaType,
33
34
  fieldPath,
34
35
  readonly = false,
35
- compact = false
36
+ compact = false,
37
+ organizationId
36
38
  }: Props = $props();
37
39
 
38
40
  // Component state
@@ -50,6 +52,9 @@
50
52
  const formData = new FormData();
51
53
  formData.append('file', file);
52
54
 
55
+ // Add document's organization ID so asset belongs to document's org
56
+ if (organizationId) formData.append('organizationId', organizationId);
57
+
53
58
  // Add field metadata for privacy checking
54
59
  if (schemaType) formData.append('schemaType', schemaType);
55
60
  if (fieldPath) formData.append('fieldPath', fieldPath);
@@ -83,7 +88,7 @@
83
88
  async function handleFileSelect(files: FileList | null) {
84
89
  if (readonly || !files || files.length === 0) return;
85
90
 
86
- const file = files[0];
91
+ const file = files[0]!;
87
92
 
88
93
  const imageValue = await uploadFile(file);
89
94
  if (imageValue) {
@@ -193,7 +198,9 @@
193
198
  <!-- Compact image row with thumbnail -->
194
199
  <div class="border-border flex items-center gap-3 rounded-md border p-2 {validationClasses}">
195
200
  <!-- Thumbnail -->
196
- <div class="bg-muted flex h-12 w-12 flex-shrink-0 items-center justify-center overflow-hidden rounded">
201
+ <div
202
+ class="bg-muted flex h-12 w-12 flex-shrink-0 items-center justify-center overflow-hidden rounded"
203
+ >
197
204
  {#if loadingAsset}
198
205
  <div class="border-primary h-4 w-4 animate-spin rounded-full border-b-2"></div>
199
206
  {:else if previewUrl}
@@ -351,10 +358,10 @@
351
358
  {:else}
352
359
  <!-- Sanity-style upload bar -->
353
360
  <div class="border-border overflow-hidden rounded-md border {validationClasses}">
354
- <div class="flex items-center">
361
+ <div class="flex flex-col items-center divide-y lg:flex-row lg:divide-x lg:divide-y-0">
355
362
  <!-- Drag and drop area (left side) -->
356
363
  <div
357
- class="flex-1 px-4 py-3 transition-colors {readonly
364
+ class="flex h-14 w-full px-3 py-4 transition-colors lg:flex-1 {readonly
358
365
  ? ''
359
366
  : isDragging
360
367
  ? 'bg-primary/5'
@@ -374,14 +381,20 @@
374
381
  <div class="flex items-center gap-3">
375
382
  <FileImage size={20} class="text-muted-foreground" />
376
383
  <span class="text-muted-foreground text-sm">
377
- {readonly ? 'No image' : isDragging ? 'Drop image here' : 'Drag or paste image here'}
384
+ {readonly
385
+ ? 'No image'
386
+ : isDragging
387
+ ? 'Drop image here'
388
+ : 'Drag or paste image here'}
378
389
  </span>
379
390
  </div>
380
391
  {/if}
381
392
  </div>
382
393
 
383
394
  <!-- Buttons (right side) -->
384
- <div class="border-border bg-muted/20 flex items-center gap-2 border-l px-3 py-2">
395
+ <div
396
+ class="border-border bg-muted/20 flex h-14 w-full items-center justify-center gap-2 px-3 py-4 lg:w-1/2"
397
+ >
385
398
  <Button
386
399
  variant="outline"
387
400
  size="sm"
@@ -9,6 +9,7 @@ interface Props {
9
9
  fieldPath?: string;
10
10
  readonly?: boolean;
11
11
  compact?: boolean;
12
+ organizationId?: string;
12
13
  }
13
14
  declare const ImageField: import("svelte").Component<Props, {}, "">;
14
15
  type ImageField = ReturnType<typeof ImageField>;
@@ -1 +1 @@
1
- {"version":3,"file":"ImageField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ImageField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAY1E,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAiVF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"ImageField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/ImageField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAY1E,UAAU,KAAK;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAyVF,QAAA,MAAM,UAAU,2CAAwC,CAAC;AACzD,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC;AAChD,eAAe,UAAU,CAAC"}
@@ -112,7 +112,7 @@
112
112
  try {
113
113
  const result = await documents.create({
114
114
  type: targetType,
115
- draftData: {
115
+ data: {
116
116
  title: 'Untitled'
117
117
  }
118
118
  });
@@ -77,8 +77,6 @@
77
77
  Enter a {sourceField} first to generate a slug automatically
78
78
  </p>
79
79
  {:else}
80
- <p class="text-muted-foreground text-xs">
81
- Click "Generate" or enter a custom slug
82
- </p>
80
+ <p class="text-muted-foreground text-xs">Click "Generate" or enter a custom slug</p>
83
81
  {/if}
84
82
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"SlugField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/SlugField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAIvD,UAAU,KAAK;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,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,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;AA+DF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"SlugField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/SlugField.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAIvD,UAAU,KAAK;IACd,KAAK,EAAE,SAAS,CAAC;IACjB,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,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;AA6DF,QAAA,MAAM,SAAS,2CAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
@@ -1,10 +1,14 @@
1
1
  <script lang="ts">
2
2
  import { Input } from '@aphexcms/ui/shadcn/input';
3
- import type { Field } from '../../../types/schemas';
3
+ import * as Select from '@aphexcms/ui/shadcn/select';
4
+ import * as RadioGroup from '@aphexcms/ui/shadcn/radio-group';
5
+ import { Label } from '@aphexcms/ui/shadcn/label';
6
+ import type { StringField, DependentList } from '../../../types/schemas';
4
7
 
5
8
  interface Props {
6
- field: Field;
9
+ field: StringField;
7
10
  value: any;
11
+ documentData?: Record<string, any>;
8
12
  onUpdate: (value: any) => void;
9
13
  validationClasses?: string;
10
14
  onBlur?: (event: any) => void;
@@ -15,6 +19,7 @@
15
19
  let {
16
20
  field,
17
21
  value,
22
+ documentData,
18
23
  onUpdate,
19
24
  validationClasses,
20
25
  onBlur,
@@ -22,19 +27,158 @@
22
27
  readonly = false
23
28
  }: Props = $props();
24
29
 
30
+ // Check if list is a dependent list
31
+ function isDependentList(list: any): list is DependentList {
32
+ return list && typeof list === 'object' && 'dependsOn' in list && 'options' in list;
33
+ }
34
+
35
+ // Resolve the actual list items (either static or dependent)
36
+ const resolvedList = $derived(() => {
37
+ if (!field.list) return [];
38
+
39
+ if (Array.isArray(field.list)) {
40
+ // Static list
41
+ return field.list;
42
+ } else if (isDependentList(field.list)) {
43
+ // Dependent list - get options based on dependsOn field value
44
+ const dependentValue = documentData?.[field.list.dependsOn];
45
+ if (!dependentValue) return [];
46
+ return field.list.options[dependentValue] || [];
47
+ }
48
+
49
+ return [];
50
+ });
51
+
52
+ // Check if this is a dependent field that's missing its dependency
53
+ const isDependentFieldWithoutValue = $derived(() => {
54
+ if (!field.list) return false;
55
+ if (Array.isArray(field.list)) return false;
56
+ if (isDependentList(field.list)) {
57
+ const dependentValue = documentData?.[field.list.dependsOn];
58
+ return !dependentValue;
59
+ }
60
+ return false;
61
+ });
62
+
63
+ // Get the name of the field this depends on (for display)
64
+ const dependsOnFieldName = $derived(() => {
65
+ if (field.list && isDependentList(field.list)) {
66
+ return field.list.dependsOn;
67
+ }
68
+ return '';
69
+ });
70
+
71
+ // Normalize list items to { title, value } format
72
+ const listItems = $derived(
73
+ resolvedList().map((item) =>
74
+ typeof item === 'string' ? { title: item.toUpperCase(), value: item } : item
75
+ )
76
+ );
77
+
78
+ const layout = $derived(field.options?.layout || 'dropdown');
79
+ const direction = $derived(field.options?.direction || 'vertical');
80
+
81
+ // For Select component - derive trigger content
82
+ const selectedItem = $derived(listItems.find((item) => item.value === value));
83
+ const triggerContent = $derived(selectedItem?.title ?? field.placeholder ?? field.title);
84
+
25
85
  function handleInputChange(event: Event) {
26
86
  const target = event.target as HTMLInputElement;
27
87
  onUpdate(target.value);
28
88
  }
89
+
90
+ function handleSelectChange(newValue: string | undefined) {
91
+ if (newValue !== undefined) {
92
+ onUpdate(newValue);
93
+ }
94
+ }
95
+
96
+ // Check if we should show list UI (dropdown or radio)
97
+ const hasListOptions = $derived(listItems.length > 0);
98
+
99
+ // Auto-reset dependent field value when parent changes and current value is invalid
100
+ $effect(() => {
101
+ // Only for dependent fields with options
102
+ if (!field.list || !isDependentList(field.list)) return;
103
+
104
+ const items = listItems; // Track listItems changes
105
+
106
+ // If we have a value and options, check if value is valid
107
+ if (value && items.length > 0) {
108
+ const isValid = items.some((item) => item.value === value);
109
+
110
+ if (!isValid) {
111
+ // Current value not in new options - reset to first option
112
+ const newValue = items[0]?.value || '';
113
+
114
+ console.log(`🔄 Dependent field "${field.name}" reset: "${value}" → "${newValue}"`);
115
+ onUpdate(newValue);
116
+ }
117
+ }
118
+ });
29
119
  </script>
30
120
 
31
- <Input
32
- id={field.name}
33
- value={value || ''}
34
- placeholder={field.title}
35
- oninput={handleInputChange}
36
- onblur={onBlur}
37
- onfocus={onFocus}
38
- class={validationClasses}
39
- disabled={readonly}
40
- />
121
+ {#if isDependentFieldWithoutValue()}
122
+ <!-- Show message when dependent field hasn't been selected -->
123
+ <div class="border-muted-foreground/30 bg-muted/30 rounded-md border border-dashed p-4">
124
+ <p class="text-muted-foreground text-sm">
125
+ Please select <span class="font-medium">{dependsOnFieldName()}</span> first
126
+ </p>
127
+ </div>
128
+ {:else if hasListOptions}
129
+ {#if layout === 'radio'}
130
+ <!-- Radio Button Layout -->
131
+ <RadioGroup.Root
132
+ value={value || ''}
133
+ onValueChange={handleSelectChange}
134
+ disabled={readonly}
135
+ class={validationClasses}
136
+ >
137
+ <div class={direction === 'horizontal' ? 'flex flex-wrap gap-4' : 'space-y-2'}>
138
+ {#each listItems as item, index (item.value)}
139
+ <div class="flex items-center space-x-2">
140
+ <RadioGroup.Item value={item.value} id={`${field.name}-${index}`} />
141
+ <Label for={`${field.name}-${index}`}>{item.title}</Label>
142
+ </div>
143
+ {/each}
144
+ </div>
145
+ </RadioGroup.Root>
146
+ {:else}
147
+ <!-- Dropdown/Select Layout -->
148
+ <Select.Root
149
+ type="single"
150
+ name={field.name}
151
+ value={value || ''}
152
+ onValueChange={handleSelectChange}
153
+ disabled={readonly}
154
+ >
155
+ <Select.Trigger class="w-full {validationClasses}">
156
+ {triggerContent}
157
+ </Select.Trigger>
158
+ <Select.Content>
159
+ <Select.Group>
160
+ {#if field.title}
161
+ <Select.Label>{field.title}</Select.Label>
162
+ {/if}
163
+ {#each listItems as item (item.value)}
164
+ <Select.Item value={item.value} label={item.title}>
165
+ {item.title}
166
+ </Select.Item>
167
+ {/each}
168
+ </Select.Group>
169
+ </Select.Content>
170
+ </Select.Root>
171
+ {/if}
172
+ {:else}
173
+ <!-- Regular Input -->
174
+ <Input
175
+ id={field.name}
176
+ value={value || ''}
177
+ placeholder={field.placeholder || field.title}
178
+ oninput={handleInputChange}
179
+ onblur={onBlur}
180
+ onfocus={onFocus}
181
+ class={validationClasses}
182
+ disabled={readonly}
183
+ />
184
+ {/if}
@@ -1,7 +1,8 @@
1
- import type { Field } from '../../../types/schemas.js';
1
+ import type { StringField } from '../../../types/schemas.js';
2
2
  interface Props {
3
- field: Field;
3
+ field: StringField;
4
4
  value: any;
5
+ documentData?: Record<string, any>;
5
6
  onUpdate: (value: any) => void;
6
7
  validationClasses?: string;
7
8
  onBlur?: (event: any) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"StringField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/StringField.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAGnD,UAAU,KAAK;IACd,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,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;AA0BF,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"StringField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/StringField.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,wBAAwB,CAAC;AAGxE,UAAU,KAAK;IACd,KAAK,EAAE,WAAW,CAAC;IACnB,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,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;AA8JF,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -0,0 +1,41 @@
1
+ <script lang="ts">
2
+ import { Input } from '@aphexcms/ui/shadcn/input';
3
+ import type { URLField } from '../../../types/schemas';
4
+
5
+ interface Props {
6
+ field: URLField;
7
+ value: any;
8
+ onUpdate: (value: any) => void;
9
+ validationClasses?: string;
10
+ onBlur?: (event: any) => void;
11
+ onFocus?: (event: any) => void;
12
+ readonly?: boolean;
13
+ }
14
+
15
+ let {
16
+ field,
17
+ value,
18
+ onUpdate,
19
+ validationClasses,
20
+ onBlur,
21
+ onFocus,
22
+ readonly = false
23
+ }: Props = $props();
24
+
25
+ function handleInputChange(event: Event) {
26
+ const target = event.target as HTMLInputElement;
27
+ onUpdate(target.value);
28
+ }
29
+ </script>
30
+
31
+ <Input
32
+ id={field.name}
33
+ type="url"
34
+ value={value || ''}
35
+ placeholder={field.placeholder || 'https://example.com'}
36
+ oninput={handleInputChange}
37
+ onblur={onBlur}
38
+ onfocus={onFocus}
39
+ class={validationClasses}
40
+ disabled={readonly}
41
+ />
@@ -0,0 +1,14 @@
1
+ import type { URLField } from '../../../types/schemas.js';
2
+ interface Props {
3
+ field: URLField;
4
+ value: any;
5
+ onUpdate: (value: any) => void;
6
+ validationClasses?: string;
7
+ onBlur?: (event: any) => void;
8
+ onFocus?: (event: any) => void;
9
+ readonly?: boolean;
10
+ }
11
+ declare const URLField: import("svelte").Component<Props, {}, "">;
12
+ type URLField = ReturnType<typeof URLField>;
13
+ export default URLField;
14
+ //# sourceMappingURL=URLField.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"URLField.svelte.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/admin/fields/URLField.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGtD,UAAU,KAAK;IACd,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,EAAE,GAAG,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAC/B,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;AA0BF,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -1,7 +1,6 @@
1
1
  export { default as AdminApp } from './AdminApp.svelte';
2
2
  export { default as Sidebar } from './layout/Sidebar.svelte';
3
3
  export { default as DocumentEditor } from './admin/DocumentEditor.svelte';
4
- export { default as DocumentTypesList } from './admin/DocumentTypesList.svelte';
5
4
  export { default as SchemaField } from './admin/SchemaField.svelte';
6
5
  export * from './fields/index.js';
7
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/components/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAG7D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAChF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAGpE,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/components/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGxD,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAG7D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAGpE,cAAc,gBAAgB,CAAC"}
@@ -6,7 +6,6 @@ export { default as AdminApp } from './AdminApp.svelte';
6
6
  export { default as Sidebar } from './layout/Sidebar.svelte';
7
7
  // Admin components (will be migrated from your current structure)
8
8
  export { default as DocumentEditor } from './admin/DocumentEditor.svelte';
9
- export { default as DocumentTypesList } from './admin/DocumentTypesList.svelte';
10
9
  export { default as SchemaField } from './admin/SchemaField.svelte';
11
10
  // Field components
12
11
  export * from './fields/index.js';