diff --git a/public/app.mjs b/public/app.mjs index 907a610..1be1422 100644 --- a/public/app.mjs +++ b/public/app.mjs @@ -293,6 +293,18 @@ function render_detail_panel() { set_text(content, '.detail-name', component_display_name(comp)); set_text(content, '.detail-description', comp.description || ''); qs(content, '.detail-edit-btn').addEventListener('click', () => open_component_dialog(comp)); + qs(content, '.detail-duplicate-btn').addEventListener('click', async () => { + const result = await api.create_component({ + name: comp.name, + description: comp.description, + fields: { ...comp.fields }, + }); + all_components.push(result.component); + all_components.sort((a, b) => a.name.localeCompare(b.name)); + selected_component_id = result.component.id; + render(); + open_component_dialog(result.component); + }); qs(content, '.detail-delete-btn').addEventListener('click', () => confirm_delete( `Delete component "${component_display_name(comp)}"? Inventory entries will become orphaned.`, async () => { @@ -1622,7 +1634,7 @@ function open_inventory_dialog(entry = null, default_component_id = null, defaul Object.assign(document.createElement('option'), { value: '', textContent: '— select component —' }), ...all_components.map(c => Object.assign(document.createElement('option'), { value: c.id, - textContent: c.name, + textContent: component_display_name(c), })) ); comp_sel.value = entry?.component_id ?? default_component_id ?? ''; diff --git a/public/templates.html b/public/templates.html index b39a537..3a41039 100644 --- a/public/templates.html +++ b/public/templates.html @@ -37,6 +37,7 @@
+