diff --git a/frontend/src/pages/entity-configuration.vue b/frontend/src/pages/entity-configuration.vue index b105dc9..e14a4d8 100644 --- a/frontend/src/pages/entity-configuration.vue +++ b/frontend/src/pages/entity-configuration.vue @@ -8,6 +8,7 @@ interface FieldDraft { name: string + identifier: string type: FieldType targetEntityId?: number relationshipType: RelationshipType @@ -16,6 +17,7 @@ interface EntityField { id: number name: string + identifier: string type: FieldType targetEntityId?: number targetEntityName?: string @@ -25,6 +27,7 @@ interface EntityDefinition { id: number name: string + identifier: string fields: EntityField[] } @@ -33,11 +36,14 @@ const saving = ref(false) const errorMessage = ref('') const newEntityName = ref('') + const newEntityIdentifier = ref('') const editingEntityId = ref() const editingEntityName = ref('') + const editingEntityIdentifier = ref('') const newFields = reactive>({}) const editingFieldId = ref() const editingFieldName = ref('') + const editingFieldIdentifier = ref('') const editingFieldType = ref('TEXT') const editingTargetEntityId = ref() const editingRelationshipType = ref('ONE_TO_ONE') @@ -67,34 +73,74 @@ } function ensureNewField (entityId: number) { - newFields[entityId] ??= { name: '', type: 'TEXT', relationshipType: 'ONE_TO_ONE' } + newFields[entityId] ??= { name: '', identifier: '', type: 'TEXT', relationshipType: 'ONE_TO_ONE' } + } + + function toIdentifier (name: string) { + const words = name + .normalize('NFD') + .replace(/\p{Diacritic}/gu, '') + .replace(/['’-]/g, '') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') + .replace(/([a-z0-9])([A-Z])/g, '$1 $2') + .replace(/[^a-z0-9]+/gi, ' ') + .trim() + .split(/\s+/) + .filter(Boolean) + + return words + .map((word, index) => { + const lower = word.toLowerCase() + return index === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1) + }) + .join('') + } + + function updateNewEntityIdentifier () { + newEntityIdentifier.value = toIdentifier(newEntityName.value) + } + + function updateEditingEntityIdentifier () { + editingEntityIdentifier.value = toIdentifier(editingEntityName.value) + } + + function updateNewFieldIdentifier (entityId: number) { + newFields[entityId].identifier = toIdentifier(newFields[entityId].name) + } + + function updateEditingFieldIdentifier () { + editingFieldIdentifier.value = toIdentifier(editingFieldName.value) } async function addEntity () { const name = newEntityName.value.trim() - if (!name) return + const identifier = newEntityIdentifier.value.trim() + if (!name || !identifier) return await mutate(async () => { - const response = await request('/api/entity-definitions', 'POST', { name }) + const response = await request('/api/entity-definitions', 'POST', { name, identifier }) if (!response.ok) throw new Error('Entity create failed') const entity = await response.json() as EntityDefinition entities.value.push(entity) ensureNewField(entity.id) newEntityName.value = '' + newEntityIdentifier.value = '' }) } function startEntityEdit (entity: EntityDefinition) { editingEntityId.value = entity.id editingEntityName.value = entity.name + editingEntityIdentifier.value = entity.identifier } async function saveEntity (entity: EntityDefinition) { const name = editingEntityName.value.trim() - if (!name) return + const identifier = editingEntityIdentifier.value.trim() + if (!name || !identifier) return await mutate(async () => { - const response = await request(`/api/entity-definitions/${entity.id}`, 'PATCH', { name }) + const response = await request(`/api/entity-definitions/${entity.id}`, 'PATCH', { name, identifier }) if (!response.ok) throw new Error('Entity update failed') - entity.name = name + Object.assign(entity, await response.json() as EntityDefinition) editingEntityId.value = undefined }) } @@ -112,10 +158,12 @@ async function addField (entity: EntityDefinition) { const draft = newFields[entity.id] const name = draft.name.trim() - if (!name) return + const identifier = draft.identifier.trim() + if (!name || !identifier) return await mutate(async () => { const values: Record = { name, + identifier, type: draft.type, } if (draft.type === 'RELATIONSHIP') { @@ -126,13 +174,14 @@ const response = await request(`/api/entity-definitions/${entity.id}/fields`, 'POST', values) if (!response.ok) throw new Error('Field create failed') entity.fields.push(await response.json() as EntityField) - newFields[entity.id] = { name: '', type: 'TEXT', relationshipType: 'ONE_TO_ONE' } + newFields[entity.id] = { name: '', identifier: '', type: 'TEXT', relationshipType: 'ONE_TO_ONE' } }) } function startFieldEdit (field: EntityField) { editingFieldId.value = field.id editingFieldName.value = field.name + editingFieldIdentifier.value = field.identifier editingFieldType.value = field.type editingTargetEntityId.value = field.targetEntityId editingRelationshipType.value = field.relationshipType ?? 'ONE_TO_ONE' @@ -140,10 +189,12 @@ async function saveField (entity: EntityDefinition, field: EntityField) { const name = editingFieldName.value.trim() - if (!name) return + const identifier = editingFieldIdentifier.value.trim() + if (!name || !identifier) return await mutate(async () => { const values: Record = { name, + identifier, type: editingFieldType.value, } if (editingFieldType.value === 'RELATIONSHIP') { @@ -210,7 +261,16 @@
- + + + +
@@ -223,13 +283,18 @@
- + + +