feat: layout export/import as JSON files

Export saves layout + elements with datapoint names for re-linking.
Import creates new layout and re-links datapoints by name.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 08:42:41 +02:00
co-authored by Claude Opus 4.6
parent 07bb4acb57
commit 52dddfd74f
3 changed files with 142 additions and 1 deletions
+30 -1
View File
@@ -137,6 +137,31 @@ export default function Layouts() {
} catch (e) { setError((e as Error).message); }
}
function handleExport() {
if (!selected) return;
window.open(`/api/layouts/${selected.id}/export`, '_blank');
}
async function handleImport() {
const input = document.createElement('input');
input.type = 'file';
input.accept = '.json';
input.onchange = async (e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (!file) return;
try {
const text = await file.text();
const data = JSON.parse(text);
const result = await api.post<any>('/api/layouts/import', data);
loadLayouts();
selectLayout(result.id);
} catch (e) {
setError((e as Error).message);
}
};
input.click();
}
function NumInput({ value, onChange, label, w }: { value: number; onChange: (v: number) => void; label: string; w?: string }) {
return (
<div className={w || 'w-16'}>
@@ -165,10 +190,13 @@ export default function Layouts() {
</div>
))}
</div>
<form onSubmit={handleAddLayout} className="flex gap-2">
<form onSubmit={handleAddLayout} className="flex gap-2 mb-2">
<input placeholder="Neues Layout..." className="flex-1 border border-gray-300 rounded px-3 py-1.5 text-sm" value={newName} onChange={(e) => setNewName(e.target.value)} />
<button type="submit" className="bg-sky-500 hover:bg-sky-600 text-white text-sm px-3 py-1.5 rounded">+</button>
</form>
<button onClick={handleImport} className="w-full text-xs text-gray-500 hover:text-sky-600 border border-dashed border-gray-300 rounded py-1.5 hover:border-sky-400">
Layout importieren (.json)
</button>
</div>
{/* Right: element editor */}
@@ -183,6 +211,7 @@ export default function Layouts() {
<button onClick={() => handleAddElement('value')} className="bg-green-500 hover:bg-green-600 text-white text-xs px-3 py-1.5 rounded">+ Wert</button>
<button onClick={() => handleAddElement('label')} className="bg-blue-500 hover:bg-blue-600 text-white text-xs px-3 py-1.5 rounded">+ Label</button>
<button onClick={() => handleAddElement('line')} className="bg-gray-500 hover:bg-gray-600 text-white text-xs px-3 py-1.5 rounded">+ Linie</button>
<button onClick={handleExport} className="border border-gray-300 text-gray-600 hover:bg-gray-100 text-xs px-3 py-1.5 rounded">Exportieren</button>
</div>
</div>