diff --git a/app/ycode/api/collections/[id]/fields/route.ts b/app/ycode/api/collections/[id]/fields/route.ts index 4d3010e5..e7798cdb 100644 --- a/app/ycode/api/collections/[id]/fields/route.ts +++ b/app/ycode/api/collections/[id]/fields/route.ts @@ -78,8 +78,9 @@ export async function POST( order: body.order ?? 0, reference_collection_id: body.reference_collection_id || null, hidden: body.hidden ?? false, + is_computed: body.is_computed ?? false, data: body.data || {}, - is_published: false, // Always create as draft + is_published: false, }); return noCache( diff --git a/app/ycode/components/CMS.tsx b/app/ycode/components/CMS.tsx index d52d3640..8420f687 100644 --- a/app/ycode/components/CMS.tsx +++ b/app/ycode/components/CMS.tsx @@ -112,6 +112,7 @@ interface SortableRowProps { isCollectionPublished: boolean; children: React.ReactNode; statusValue: import('./CollectionStatusPill').ItemStatusValue | null; + onEdit: () => void; onSetAsDraft: () => void; onStageForPublish: () => void; onSetAsPublished: () => void; @@ -120,7 +121,7 @@ interface SortableRowProps { lockInfo?: ItemLockInfo; } -function SortableRow({ item, isSaving, isManualMode, isCollectionPublished, children, statusValue, onSetAsDraft, onStageForPublish, onSetAsPublished, onDuplicate, onDelete, lockInfo }: SortableRowProps) { +function SortableRow({ item, isSaving, isManualMode, isCollectionPublished, children, statusValue, onEdit, onSetAsDraft, onStageForPublish, onSetAsPublished, onDuplicate, onDelete, lockInfo }: SortableRowProps) { const { attributes, listeners, @@ -147,7 +148,9 @@ function SortableRow({ item, isSaving, isManualMode, isCollectionPublished, chil handleEditItem(item)} onSetAsDraft={() => handleSetItemStatus(item.id, 'draft')} onStageForPublish={() => handleSetItemStatus(item.id, 'stage')} onSetAsPublished={() => handleSetItemStatus(item.id, 'publish')} @@ -1576,9 +1580,7 @@ const CMS = React.memo(function CMS() { className="pl-5 pr-3 py-3 w-12" onClick={(e) => { e.stopPropagation(); - if (!isManualMode) { - handleEditItem(item); - } + handleEditItem(item); }} >
@@ -1599,7 +1601,7 @@ const CMS = React.memo(function CMS() { !isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} > !isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} > {formatDateInTimezone(value, timezone, 'display')} @@ -1637,7 +1639,7 @@ const CMS = React.memo(function CMS() { !isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} > - @@ -1648,7 +1650,7 @@ const CMS = React.memo(function CMS() { !isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} >
{assetIds.slice(0, 3).map((assetId, idx) => { @@ -1716,7 +1718,7 @@ const CMS = React.memo(function CMS() { !isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} > - @@ -1727,7 +1729,7 @@ const CMS = React.memo(function CMS() { !isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} >
{assetIds.slice(0, 3).map((assetId, idx) => { @@ -1763,7 +1765,7 @@ const CMS = React.memo(function CMS() { !isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} > !isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} > {plainText || '-'} @@ -1828,7 +1830,7 @@ const CMS = React.memo(function CMS() { !isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} > {displayValue} @@ -1843,7 +1845,7 @@ const CMS = React.memo(function CMS() { !isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} >
!isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} >
!isManualMode && handleEditItem(item)} + onClick={() => handleEditItem(item)} > {value || '-'} diff --git a/app/ycode/components/CollectionItemContextMenu.tsx b/app/ycode/components/CollectionItemContextMenu.tsx index 812cbe16..9e5fea59 100644 --- a/app/ycode/components/CollectionItemContextMenu.tsx +++ b/app/ycode/components/CollectionItemContextMenu.tsx @@ -7,7 +7,9 @@ interface CollectionItemContextMenuProps { children: React.ReactNode; isPublishable: boolean; hasPublishedVersion: boolean; + isModified: boolean; isCollectionPublished: boolean; + onEdit: () => void; onSetAsDraft: () => void; onStageForPublish: () => void; onSetAsPublished: () => void; @@ -20,7 +22,9 @@ export default function CollectionItemContextMenu({ children, isPublishable, hasPublishedVersion, + isModified, isCollectionPublished, + onEdit, onSetAsDraft, onStageForPublish, onSetAsPublished, @@ -38,6 +42,10 @@ export default function CollectionItemContextMenu({ {children} + + Edit CMS item + + Stage for publish Set as published diff --git a/app/ycode/components/CollectionItemSheet.tsx b/app/ycode/components/CollectionItemSheet.tsx index 37ef6b49..860bb4ea 100644 --- a/app/ycode/components/CollectionItemSheet.tsx +++ b/app/ycode/components/CollectionItemSheet.tsx @@ -211,12 +211,14 @@ export default function CollectionItemSheet({ // Reset form when editing item changes useEffect(() => { + // Only include fillable/visible fields in form state to avoid + // sending computed fields (status, ID, timestamps) to the API + const editableFields = collectionFields.filter(f => f.fillable && !f.hidden); + if (editingItem) { - // Ensure all values are defined (not undefined) const values: Record = {}; - collectionFields.forEach(field => { + editableFields.forEach(field => { let value = editingItem.values[field.id] ?? ''; - // Normalize boolean values to strings if (field.type === 'boolean') { value = normalizeBooleanValue(value); } @@ -224,11 +226,9 @@ export default function CollectionItemSheet({ }); form.reset(values); } else { - // Reset with default values for new items const defaultValues: Record = {}; - collectionFields.forEach(field => { + editableFields.forEach(field => { let value = field.default || ''; - // Normalize boolean values to strings if (field.type === 'boolean') { value = normalizeBooleanValue(value); } @@ -607,17 +607,20 @@ export default function CollectionItemSheet({ ) : field.type === 'phone' ? ( ) : field.type === 'date' ? ( { const utcValue = localDatetimeToUTC(e.target.value, timezone); @@ -786,6 +789,7 @@ export default function CollectionItemSheet({ )} diff --git a/app/ycode/components/FieldFormDialog.tsx b/app/ycode/components/FieldFormDialog.tsx index 7f26c3f5..368103fd 100644 --- a/app/ycode/components/FieldFormDialog.tsx +++ b/app/ycode/components/FieldFormDialog.tsx @@ -190,6 +190,7 @@ export default function FieldFormDialog({ value={fieldName} onChange={(e) => setFieldName(e.target.value)} placeholder="Field name" + autoComplete="off" />
@@ -351,6 +352,7 @@ export default function FieldFormDialog({ value={fieldDefault} onChange={(e) => setFieldDefault(e.target.value)} placeholder="0" + autoComplete="off" /> ) : fieldType === 'date' ? ( setFieldDefault(e.target.value)} + autoComplete="off" /> ) : fieldType === 'email' ? ( setFieldDefault(e.target.value)} placeholder="email@example.com" + autoComplete="off" /> ) : fieldType === 'phone' ? ( setFieldDefault(e.target.value)} placeholder="+1 (555) 000-0000" + autoComplete="off" /> ) : ( setFieldDefault(e.target.value)} placeholder="Default value" + autoComplete="off" /> )}
diff --git a/app/ycode/components/RichTextEditor.tsx b/app/ycode/components/RichTextEditor.tsx index bae516d9..1f9c3142 100644 --- a/app/ycode/components/RichTextEditor.tsx +++ b/app/ycode/components/RichTextEditor.tsx @@ -445,7 +445,6 @@ const RichTextEditor = forwardRef(({ Code, RichTextImageWithNodeView, HorizontalRule, - RichTextImage, ]; // Always include heading extension so content with headings is preserved diff --git a/app/ycode/components/RichTextLinkSettings.tsx b/app/ycode/components/RichTextLinkSettings.tsx index d9e4a059..4e0aa44b 100644 --- a/app/ycode/components/RichTextLinkSettings.tsx +++ b/app/ycode/components/RichTextLinkSettings.tsx @@ -517,7 +517,7 @@ export default function RichTextLinkSettings({ {/* Link Type */}
-
+