Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion plans/ux-overhaul.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Editor-Factory-Muster, `AccordionSection`, `TabbedTranslatableFields`, `ElementP
## Phase 4 — Editor-Konsistenz & Wartbarkeit
- [ ] `useElementUpdate`-Hook (dedupliziert Handler-Boilerplate über ~12 Editoren).
- [ ] Listen-Editoren vereinheitlichen (ChipGroup-Dialog vs. SingleSelection-Tabelle).
- [ ] Pflichtfeld-Markierung, Feld-ID prominenter, Hilfetexte, Duplikat-Optionen verhindern.
- [~] **Feld-ID-Prominenz** *(PR Phase 4)*: gemeinsame `common/FieldIdField` (Pflicht-Marker, Hilfetext, Leer-Warnung) **zentral** im `EnhancedElementEditorFactory` für alle wertführenden Typen (Boolean/String/Number/Date/SingleSelection). Befund: Feld-ID war zuvor uneinheitlich — bei Number/Date/Boolean/SingleSelection gar nicht editierbar, bei String als String statt `{field_name}`. Jetzt überall sichtbar + einheitlich als `{field_name}` geschrieben. Offen: Duplikat-Optionen verhindern, breitere Pflichtfeld-Markierungen, `useElementUpdate`-Hook.

## Phase 5 — Seiten & Flow-Metadaten
- [ ] `pattern_type`-Auswahl bei Seitenanlage; Layout-Vorschau (`PageNavigator.tsx`, `EditPageDialog.tsx`).
Expand Down
25 changes: 25 additions & 0 deletions src/components/HybridEditor/EnhancedElementEditorFactory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@ import React from 'react';
import { PatternLibraryElement } from '../../models/listingFlow';
import { ElementEditorFactory } from '../PropertyEditor/ElementEditorFactory';
import { Box, Typography } from '@mui/material';
import FieldIdField from '../PropertyEditor/common/FieldIdField';

interface EnhancedElementEditorFactoryProps {
element: PatternLibraryElement;
onUpdate: (updatedElement: PatternLibraryElement) => void;
}

// Wertführende Eingabe-Typen, die zwingend eine Feld-ID brauchen (dort landet der Wert).
const FIELD_ID_TYPES = [
'BooleanUIElement',
'StringUIElement',
'NumberUIElement',
'DateUIElement',
'SingleSelectionUIElement',
];

/**
* Brückenkomponente, die die ElementEditorFactory in den EnhancedPropertyEditor integriert.
* Diese Komponente dient als Adapter zwischen dem EnhancedPropertyEditor und den spezialisierten
Expand All @@ -27,8 +37,23 @@ export const EnhancedElementEditorFactory: React.FC<EnhancedElementEditorFactory
);
}

const elementType = element.element?.pattern_type;
const showFieldId = FIELD_ID_TYPES.includes(elementType);
// field_id robust lesen (manche Altdaten/Editoren hielten es als String statt {field_name}).
const rawFieldId = (element.element as any).field_id;
const fieldName = typeof rawFieldId === 'string' ? rawFieldId : rawFieldId?.field_name ?? '';
const handleFieldIdChange = (name: string) => {
onUpdate({
...element,
element: { ...element.element, field_id: { field_name: name } } as any,
});
};

return (
<Box sx={{ mt: 2 }}>
{showFieldId && (
<FieldIdField value={fieldName} onChange={handleFieldIdChange} />
)}
<ElementEditorFactory element={element} onUpdate={onUpdate} />
</Box>
);
Expand Down
55 changes: 55 additions & 0 deletions src/components/PropertyEditor/common/FieldIdField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import { Box, TextField, Typography } from '@mui/material';
import KeyOutlinedIcon from '@mui/icons-material/KeyOutlined';

interface FieldIdFieldProps {
/** Aktueller field_name (Wert von field_id.field_name). */
value: string;
/** Liefert den neuen field_name. */
onChange: (fieldName: string) => void;
required?: boolean;
helpText?: string;
}

/**
* Prominentes, einheitliches Editierfeld für die Feld-ID (`field_id.field_name`).
* Eingabe-Elemente brauchen eine Feld-ID — dort landet der erfasste Wert. Früher war
* dieses Feld in den Editoren uneinheitlich, versteckt oder gar nicht vorhanden;
* hier zentral mit Pflicht-Markierung, Hilfetext und Leer-Warnung.
*/
const FieldIdField: React.FC<FieldIdFieldProps> = ({ value, onChange, required = true, helpText }) => {
const empty = required && !value.trim();

return (
<Box
sx={{
mb: 2,
p: 1.5,
borderRadius: 1,
border: '1px solid',
borderColor: empty ? 'error.light' : 'rgba(0,0,0,0.12)',
bgcolor: 'rgba(0,159,100,0.04)',
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 0.75 }}>
<KeyOutlinedIcon fontSize="small" sx={{ color: 'action.active' }} />
<Typography variant="subtitle2">Feld-ID{required ? ' *' : ''}</Typography>
</Box>
<TextField
fullWidth
size="small"
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder="z. B. heating_type"
error={empty}
helperText={
empty
? 'Pflichtfeld — ohne Feld-ID wird der erfasste Wert nicht gespeichert.'
: helpText ?? 'Eindeutiger Name, unter dem der erfasste Wert gespeichert wird (snake_case).'
}
/>
</Box>
);
};

export default FieldIdField;
17 changes: 0 additions & 17 deletions src/components/PropertyEditor/editors/StringElementEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import TextFieldsIcon from '@mui/icons-material/TextFields';
import TitleIcon from '@mui/icons-material/Title';
import TuneIcon from '@mui/icons-material/Tune';
import VerifiedIcon from '@mui/icons-material/Verified';
import SettingsIcon from '@mui/icons-material/Settings';
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';

interface StringElementEditorProps {
Expand Down Expand Up @@ -230,22 +229,6 @@ const StringElementEditor: React.FC<StringElementEditorProps> = ({ element, onUp
</Box>
</AccordionSection>

<AccordionSection
title="Erweiterte Einstellungen"
icon={<SettingsIcon />}
defaultExpanded={false}
>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<TextField
label="Feld-ID"
value={stringElement.field_id || ''}
onChange={handleTextChange('field_id')}
fullWidth
size="small"
/>
</Box>
</AccordionSection>

<ElementPreview title="Vorschau">
<Box sx={{ p: 1 }}>
<Typography variant="subtitle2" gutterBottom>
Expand Down
Loading