From 74cbeccb52eeb60eb67f14884632a7a5f086acbd Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 13 Jun 2026 18:01:02 +0200 Subject: [PATCH] feat(furni-editor): create furnidata entry when missing (upsert Save) When a furni has no furnidata entry (furniDataEntry === null), unlock the name/description fields instead of locking them: the Save button becomes "Create entry" and sends the existing FurniEditorUpdateFurnidataComposer (10046), which the emulator now upserts (creates a complete entry from items_base). The classname-mismatch case (entry resolved by id but for a different classname) stays locked to avoid an id collision. On success the hook already re-fetches the detail, so the panel flips to normal edit mode. Name input prefills (placeholder) from the DB Public Name. --- .../views/FurniEditorEditView.tsx | 45 ++++++++++++------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/src/components/furni-editor/views/FurniEditorEditView.tsx b/src/components/furni-editor/views/FurniEditorEditView.tsx index b5e3d35..928ac8e 100644 --- a/src/components/furni-editor/views/FurniEditorEditView.tsx +++ b/src/components/furni-editor/views/FurniEditorEditView.tsx @@ -245,6 +245,12 @@ export const FurniEditorEditView: FC = props => return cn ? (cn === itemCn) : true; }, [ furniDataEntry, item ]); + // No furnidata entry at all → the editor can CREATE one (the server upserts: + // it builds a complete entry from items_base on save). Distinct from the + // classname-mismatch case (an entry resolved by id but for a different + // classname), which stays locked to avoid an id collision. + const furnidataCreatable = useMemo(() => !furniDataEntry, [ furniDataEntry ]); + // True only when the name/description actually differ from the stored furnidata // entry. Used to gate the Save button: saving an unchanged value makes the // server writer return false, which the handler misreports as "Classname not @@ -364,16 +370,18 @@ export const FurniEditorEditView: FC = props => Display name & description { furnidataEditable ? LIVE - : NO FURNIDATA } + : furnidataCreatable + ? NEW + : NO FURNIDATA } { furnidataEditable && furnidataDirty && Unsaved } - { furnidataEditable ? ( + { (furnidataEditable || furnidataCreatable) ? ( <>
- setFurniName(e.target.value) } maxLength={ 256 } /> + setFurniName(e.target.value) } maxLength={ 256 } placeholder={ furnidataCreatable ? (form.publicName || form.itemName) : undefined } />
@@ -381,26 +389,31 @@ export const FurniEditorEditView: FC = props =>
- - - + + { furnidataEditable && + <> + + + } + { furnidataCreatable && + No furnidata entry yet — saving creates a complete one from the item data. } { importNote && { importNote } } ) : (
- This furni has no matching furnidata entry ({ furnidataMissReason.replace(/_/g, ' ') }), so its display name can't be edited here. Clients fall back to the DB Public Name below. + A furnidata entry resolved by id but for a different classname ({ furnidataMissReason.replace(/_/g, ' ') }) — name editing is locked to avoid an id collision. Clients fall back to the DB Public Name below.
) }