Add bin types: reusable named dimension presets for bins

Bin types store a name, physical W×H in mm, and optional description.
When editing a bin, a type can be selected from a dropdown; this
pre-fills and locks the dimension inputs. Custom dimensions remain
available when no type is selected.

- lib/storage.mjs: bin type CRUD with bt: prefix
- server.mjs: /api/bin-types CRUD routes; type_id accepted on bin
  create/update routes; DELETE protected if any bin references the type;
  type dims copied onto bin when type_id is set
- public/lib/api.mjs: bin type wrappers; rename_bin → update_bin (accepts
  any fields)
- public/templates.html: Types tab in bins section; t-bin-type-row;
  t-dialog-bin-type; type selector in bin editor dialog
- public/app.mjs: all_bin_types state loaded at startup; render_bin_types_list();
  open_bin_type_dialog(); type selector in open_bin_editor(); /bins/types routing
- public/style.css: bin types list styles

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-03 03:04:04 +00:00
parent 320c6f1bd9
commit 090f6f3154
6 changed files with 318 additions and 35 deletions

View File

@@ -617,11 +617,13 @@
<div class="tab-bar">
<button class="tab-btn" id="btn-tab-bins">Bins</button>
<button class="tab-btn" id="btn-tab-bin-sources">Source images</button>
<button class="tab-btn" id="btn-tab-bin-types">Types</button>
</div>
<label class="btn btn-secondary" id="btn-upload-bin-sources" hidden>
+ Upload
<input type="file" accept="image/*" multiple hidden id="bin-source-upload-input">
</label>
<button class="btn btn-primary" id="btn-add-bin-type" hidden>+ Add type</button>
</div>
<div id="tab-bins-content">
<div class="bin-gallery" id="bin-gallery"></div>
@@ -629,9 +631,26 @@
<div id="tab-bin-sources-content" hidden>
<div id="bin-source-image-list" class="source-gallery"></div>
</div>
<div id="tab-bin-types-content" hidden>
<div id="bin-types-list" class="bin-types-list"></div>
</div>
</section>
</template>
<template id="t-bin-type-row">
<div class="bin-type-row">
<div class="bin-type-info">
<span class="bin-type-name"></span>
<span class="bin-type-dims"></span>
<span class="bin-type-desc"></span>
</div>
<span class="row-actions">
<button class="btn-icon btn-edit" title="Edit"></button>
<button class="btn-icon btn-danger btn-delete" title="Delete"></button>
</span>
</div>
</template>
<template id="t-bin-card">
<div class="bin-card">
<div class="bin-card-img-wrap">
@@ -657,6 +676,12 @@
<input type="text" id="bin-editor-name" autocomplete="off">
</div>
<div class="form-row">
<label>Type</label>
<select id="bin-editor-type">
<option value="">— Custom —</option>
</select>
</div>
<div class="form-row" id="bin-editor-dims-row">
<label>Dimensions (mm)</label>
<div class="bin-editor-dims">
<input type="number" id="bin-editor-width" placeholder="W" min="1" step="1">
@@ -673,6 +698,32 @@
</dialog>
</template>
<template id="t-dialog-bin-type">
<dialog id="dialog-bin-type" class="app-dialog">
<h2 class="dialog-title"></h2>
<div class="form-row">
<label>Name</label>
<input type="text" id="bt-name" autocomplete="off" placeholder="e.g. Sortimo L-Boxx small">
</div>
<div class="form-row">
<label>Dimensions (mm)</label>
<div class="bin-editor-dims">
<input type="number" id="bt-width" placeholder="W" min="1" step="1">
<span>×</span>
<input type="number" id="bt-height" placeholder="H" min="1" step="1">
</div>
</div>
<div class="form-row">
<label>Description</label>
<input type="text" id="bt-description" autocomplete="off" placeholder="Optional">
</div>
<div class="dialog-actions">
<button type="button" class="btn btn-secondary" id="bt-cancel">Cancel</button>
<button type="button" class="btn btn-primary" id="bt-save">Save</button>
</div>
</dialog>
</template>
<!-- ===== CELL INVENTORY OVERLAY ===== -->
<template id="t-cell-inventory">
<div class="cell-inventory-overlay" id="cell-inventory-overlay">