Skip to content

Roadmap to a polished Technikpool v1: labels, scanner app, UI consistency, DGUV, offers/invoices, images #2

Description

@hrueger

Destination

A locked spec/decision for each of seven roadmap fronts standing between Technikpool's current state and a polished v1: asset label generation, a Flutter check-in/check-out companion app, a UI consistency pass, DGUV inspection due-date tracking, offer/invoice document generation, product-image & manufacturer-logo handling (with background removal/crop), and org visual identity (a color + short avatar label per org, used to identify the owning org at a glance in the calendar, device lists, and elsewhere). "Done" for this map means every front has enough decided — data model, workflow, architecture, UI approach — that implementation can start without further design discussion. This map does not build the features; it charts the way to specs ready for handoff.

Notes

  • Stack: SvelteKit + Svelte 5 + Prisma 7/Postgres + Better-Auth, per this repo's CLAUDE.md. Remote functions in src/lib/remote/*.remote.ts.
  • Grounding already done while charting (don't re-derive): no DGUV/inspection fields on Asset today; no Offer/Invoice models; the existing delivery-note route is a packing-list-style print view off Production data, not a persisted document; Product.imageUrl is a plain pasted URL (no upload, no storage backend, no image processing lib in package.json); Manufacturer has no logo field; a check-in/check-out flow already exists server-side (src/lib/remote/checkout.remote.tsscanAsset, checkoutAssets, keyed by assetTag) and is used today by a browser page at src/routes/checkout/+page.svelte. The Flutter app front is about giving this existing logic a mobile-native surface, not inventing new business logic from scratch.
  • This map is plan-only by default (no execution override): tickets resolve to decisions/specs, not shipped code, except wayfinder:task tickets which may do concrete unblocking work.
  • The /grilling, /domain-modeling, /research, and /prototype companion skills referenced by the Wayfinder skill are not installed in this repo (only wayfinder itself is, per skills-lock.json). Sessions should conduct grilling/prototyping conversationally in their place, one question at a time, and use a plain subagent for research tickets in place of a dedicated /research skill.
  • Tracker: GitHub Issues on RuegerEvents/technikpool. Sub-issue and blocking relationships are wired via GitHub's native addSubIssue / addBlockedBy GraphQL mutations (visible in the issue's sidebar).
  • "Prototype a canonical reference page for the UI polish baseline" (issue Prototype a canonical reference page for the UI polish baseline #6) was closed as a duplicate/merge into Calendar devices-mode: org filter + owning-org display: the user chose the devices-mode calendar page to serve double duty as the UI-polish reference once it lands, rather than reworking a separate page. Prototype full-page production device-list editor was rewired to be blocked by Calendar devices-mode: org filter + owning-org display #14 instead of Prototype a canonical reference page for the UI polish baseline #6.

Decisions so far

  • Prototype full-page production device-list editor — resolved via iterated prototype (artifact): a full, fluid-width page (not a div-replacement) with three panes — category quick-filter (left), available devices with search/org/location filters (center), booked-for-this-production (right, grouped by category). Rows use a − count + quantity stepper (not a single add button) since multiple identical units can be needed — this moves the underlying model from per-Asset add/remove to per-Product-quantity, with the system auto-assigning specific Asset records. The right pane is never filtered by the center pane's controls — it always shows the full booking, plus a summary strip of the orgs/locations represented. Reached via a "Manage equipment" link from the production detail page, replacing the current inline add-panel.
  • DGUV inspection due-date data model & overdue visibilityOrganization.defaultInspectionIntervalMonths is copied onto Asset.inspectionIntervalMonths at creation (a snapshot, not a live reference — later org-default changes don't retroactively affect existing assets, and the per-asset value stays individually editable). A new dedicated Inspection model (not reusing AssetTransaction) records assetId, performedAt, result (plain string, e.g. "PASSED"/"FAILED"), notes, inspectorName (free text — the certifying inspector is typically external, not an app user). Asset.nextInspectionDue is a materialized field kept in sync for fast overdue queries; assets with no interval set are excluded from tracking. Overdue status surfaces via a dashboard stat tile and a dedicated report page — no asset-list column for now.
  • Prototype the image upload/crop/background-removal UI flow — resolved via an interactive prototype (artifact, reviewed and approved): fixed 1:1 square crop for both product photos and manufacturer logos (no aspect choice), defaulting to a square bounding the photo's longer side so the whole image fits uncropped, with the resize handle able to expand past the photo's edges (transparent padding) or shrink for a tighter crop. Flow: upload → crop → background removal (adjustable strength, prototype uses a rough corner-color threshold standing in for the real @imgly/background-removal from Research: background-removal approaches for product/logo photos #11). Covers both Product.imageUrl and a new (not-yet-schemad) Manufacturer logo field; uploads go through the storage backend from File storage backend for uploaded images #10.
  • Calendar devices-mode: org filter + owning-org display — a multi-select org checklist filters the calendar's assets-mode rows (unfiltered by default), org filter state lives in a URL query param, and each asset row's org name gets prefixed with a small colored badge showing the org's 2-letter avatarLabel on its color. This is the landed UI-polish reference pattern (merged from Prototype a canonical reference page for the UI polish baseline #6): the badge component and filter-checklist pattern become the baseline other pages are measured against. Unblocked Prototype full-page production device-list editor.
  • Org visual identity: color + two-letter avatar prefix — new Organization.color (hex, @unique) and Organization.avatarLabel (exactly 2 uppercase letters, @unique) fields; assetIdPrefix can't double as the label (it's numeric-only, 3 digits). Always manually set by an org owner/admin (no auto-generation), required on createOrg/editable via updateOrg following the assetIdPrefix pattern. Existing orgs get a one-off backfill script assigning temporary unique values before the fields go not-null; owners edit to real values afterward. This ticket covers data model + org-settings input only — Calendar devices-mode is the queued first consuming surface.
  • Label content, code type, and print mechanism — resolved by the already-shipped sticker sheet generator (/stickers, hand-tested and confirmed by the user): Data Matrix code via bwip-js encoding a payload template keyed to Asset.assetTag's prefix+number scheme, server-rendered PDF sheets (not browser print, not dedicated label-printer/ZPL), sized for precut sticker paper or flag/cable-tail labels; a single range-based generator page covers both single and bulk printing (no per-asset print button, no bulk-select-from-created-assets flow).
  • Research: background-removal approaches for product/logo photos — default to client-side @imgly/background-removal (in-browser WASM, ~40MB model, no per-call cost, photos never leave the device); self-hosted rembg (u2net/isnet, CPU) as an optional server-side fallback if quality falls short; hosted APIs (remove.bg, ~$0.20–0.27/image) ruled out as a default. Flags a licensing trap: rembg's higher-quality bria-rmbg backend is CC BY-NC 4.0 (non-commercial only) — avoid unless a commercial license is bought.
  • Research: German legal requirements for offer/invoice documents — invoices cannot stay purely computed like delivery-note; §14(4) UStG unique-numbering and GoBD immutability require a persisted, uniquely-numbered Invoice record with a fixed content snapshot at issuance. Offers (Angebote) are not tax documents and carry no such requirement — can stay a purely computed print view. Also flags the 2027/2028 German e-invoicing mandate as a second reason invoices need structured, persisted data.
  • File storage backend for uploaded images — self-hosted, S3-compatible object storage (RustFS recommended, coded against the generic S3 API so it stays swappable); uploads proxy through a new SvelteKit +server.ts endpoint (client sends the already-cropped/bg-removed image as multipart/form-data; server holds credentials and does the PUT), not a presigned direct-to-storage flow.
  • Offer/invoice data model & generation workflow — pricing lives on Asset (net purchase price + optional purchase date), multiplied by an org-configurable default percentage per Category to derive a daily rate, then by production day count; both the rate and day count are overridable per document, plus one overall discount (% or €). Orgs get an isKleinunternehmer flag — prices always stored net, shown as gross to Kleinunternehmer orgs on entry. Both Offer and Invoice are persisted, copyable entities (copy-to-new-customer, copy-to-invoice as a snapshot conversion), revising the earlier assumption that offers could stay a purely computed print view.

Not yet specified

Out of scope

  • A real billing/accounting module for offers & invoices (pricing history, sent/accepted/paid status tracking) — user chose document generation only, off existing production data, no payment tracking.
  • Active reminders/notifications (email/push) for overdue DGUV inspections — user chose passive overdue visibility (dashboard/list surfacing) only, not an alerting system.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions