feat: building record management (spec 1.4) #8

Merged
addison merged 22 commits from implement-spec-1-4-building-management into main 2026-07-03 16:32:29 -04:00
Collaborator

Implements spec 1.4 Building Record Management.

Backend

  • New internal/api package with a generic CRUD wiring abstraction: shared routing, JSON decoding, status codes, and error mapping; per-resource adapters and validation hooks.
  • Full CRUD under /api/buildings, /api/customers, /api/models, /api/features; read-only /api/statuses.
  • All backend services now mount under one /api namespace; update service migrated from /updates-api to /api/updates.
  • Building delete goes through the single coordinated Store.DeleteBuilding.
  • Error mapping: 404 not found, 400 validation, 409 duplicate serial, 503 storage unavailable.
  • Bugfix: DeleteBuilding leaked its transaction on the not-found path, deadlocking the single-connection SQLite store.

Frontend

  • Sidebar: Buildings, Customers, Models, Features, Updates.
  • Shared ResourceCrudPage for customers/models/features (table, modal editor, confirmed deletes).
  • Building form per spec: serial, integer length/width, color free-text autocomplete, model/customer comboboxes with inline add-new, status select, feature multi-select with inline add, notes.
  • Buildings list with search and resolved names; rewritten detail page with Edit/Delete.

Testing

  • 14 API tests (httptest against a temp-dir store): CRUD round-trips, feature/customer/model/status attachment, 404/400/409/503 paths.
  • go test ./..., go vet ./..., frontend build all pass; UI verified end-to-end in the running app.

Spec remains in-progress; will flip to completed after merge.

Implements [spec 1.4 Building Record Management](specs/001-mvp/004-building-record-management/SPEC.md). ## Backend - New `internal/api` package with a generic CRUD wiring abstraction: shared routing, JSON decoding, status codes, and error mapping; per-resource adapters and validation hooks. - Full CRUD under `/api/buildings`, `/api/customers`, `/api/models`, `/api/features`; read-only `/api/statuses`. - All backend services now mount under one `/api` namespace; update service migrated from `/updates-api` to `/api/updates`. - Building delete goes through the single coordinated `Store.DeleteBuilding`. - Error mapping: 404 not found, 400 validation, 409 duplicate serial, 503 storage unavailable. - Bugfix: `DeleteBuilding` leaked its transaction on the not-found path, deadlocking the single-connection SQLite store. ## Frontend - Sidebar: Buildings, Customers, Models, Features, Updates. - Shared `ResourceCrudPage` for customers/models/features (table, modal editor, confirmed deletes). - Building form per spec: serial, integer length/width, color free-text autocomplete, model/customer comboboxes with inline add-new, status select, feature multi-select with inline add, notes. - Buildings list with search and resolved names; rewritten detail page with Edit/Delete. ## Testing - 14 API tests (httptest against a temp-dir store): CRUD round-trips, feature/customer/model/status attachment, 404/400/409/503 paths. - `go test ./...`, `go vet ./...`, frontend build all pass; UI verified end-to-end in the running app. Spec remains `in-progress`; will flip to `completed` after merge.
Co-authored-by: Shelley <[email protected]>
Add internal/api with a shared generic CRUD abstraction: mount wires
list/create/read/update/delete routes per resource, with hook points
for validation and storage adapters. Buildings, customers, models, and
features share the same flow; statuses are exposed read-only.

Storage errors map to HTTP codes: not found 404, validation 400,
duplicate serial 409, storage unavailable 503. Building deletion goes
through the single Store.DeleteBuilding operation.

Also fix Store.DeleteBuilding leaving its transaction open on the
not-found path, which deadlocked the single-connection store.

Co-authored-by: Shelley <[email protected]>
Replace the separate /updates-api and /api service routes with a
single /api service that owns an http.ServeMux routing to health,
updates (now /api/updates), and the building-management CRUD API.
Update the Updates page fetch calls to the new paths.

Co-authored-by: Shelley <[email protected]>
Add Customers, Models, and Features pages sharing one list+create+edit+delete component, expand the sidebar to all five sections, and scope New <resource> header actions per section.

Co-authored-by: Shelley <[email protected]>
feat: building list, detail, and create/edit form
All checks were successful
PR Validation / validate (pull_request) Successful in 3m36s
3c4ff35f76
The buildings page lists serial, size, color, and resolved model/status/customer names with search and confirmed deletes. The create/edit form matches the spec's Building Form Shape: color autocomplete from existing values, model/customer/feature comboboxes with inline add-new, status select, and integer dimensions. The detail page shows resolved names with Edit and Delete affordances.

Co-authored-by: Shelley <[email protected]>
Replace hand-rolled bordered divs, heading/paragraph markup, and
AnnouncementBanner error strips with @immich/ui primitives:

- Card/CardHeader/CardTitle/CardDescription/CardBody for empty-state and
  not-found panels and the building detail layout
- Alert (warning) for storage-unavailable / backend-blocked states,
  Alert (danger) for load, delete, and form error banners
- Text/Heading/Stack/HStack instead of raw p/h2/dl styling classes
- LoadingSpinner + Text for loading indicators

Co-authored-by: Shelley <[email protected]>
- NumberInput with numeric bound values for length/width instead of
  string-typed Input type=number fields
- Alert for load/form error banners and the storage-unavailable state
- LoadingSpinner + Text loading indicator, HStack for the action row

Co-authored-by: Shelley <[email protected]>
feat: restyle combobox internals with Immich UI primitives
All checks were successful
PR Validation / validate (pull_request) Successful in 3m43s
74e9aab14c
Evaluated replacing MultiCombobox with @immich/ui MultiSelect: its API is
options + bindable values over a bits-ui Select trigger with a fixed
option list — no text input for filtering and no way to render an inline
'create new' row, both of which the building form requires (spec 1.4
autocomplete + inline record creation). Same for the single Select.
Decision: keep our combobox components but build their internals from
library primitives instead of hand-rolled markup:

- ListButton for dropdown option rows and the 'Add …' create row
  (selected checkmark marks the current value in single-select)
- Badge with onClose for the removable selected-feature chips
- CloseButton for the clear-selection control
- Text for the 'No matches' empty row

Co-authored-by: Shelley <[email protected]>
Co-authored-by: Shelley <[email protected]>
Co-authored-by: Shelley <[email protected]>
fix(ui): center combobox clear button
All checks were successful
PR Validation / validate (pull_request) Successful in 3m38s
a3365e13d4
Co-authored-by: Shelley <[email protected]>
fix: improve table text contrast
All checks were successful
PR Validation / validate (pull_request) Successful in 5m4s
e20da41d25
fix: move create actions into pages
All checks were successful
PR Validation / validate (pull_request) Successful in 5m16s
7a4f7e0e31
feat: add sidebar toggle
All checks were successful
PR Validation / validate (pull_request) Successful in 5m15s
964b90cfdd
fix: dialog
All checks were successful
PR Validation / validate (pull_request) Successful in 5m14s
77243097c7
fix: dialog
All checks were successful
PR Validation / validate (pull_request) Successful in 5m18s
0aec15a439
fix: detail page
All checks were successful
PR Validation / validate (pull_request) Successful in 4m59s
5cdd304ad4
fix: combobox
All checks were successful
PR Validation / validate (pull_request) Successful in 5m20s
a72658e367
fix: status options
All checks were successful
PR Validation / validate (pull_request) Successful in 5m11s
1197239a8d
fix: consistent pages
All checks were successful
PR Validation / validate (pull_request) Successful in 3m34s
4522236fa4
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kwila/shed-ledger!8
No description provided.