Commit Graph

90 Commits

Author SHA1 Message Date
gsinghpal
80887d6098 changes 2026-05-25 08:17:29 -04:00
gsinghpal
5d5964a327 fix(plant-kanban): full-height bordered columns + viewport-pinned scrollbar
Two layout polish fixes after persona-walk feedback on the new Plant
Kanban surface (`fp_plant_kanban`).

1. Columns now run full board height with visible borders
   Was: `.col` had `background: $plant-bg` (= page bg, invisible) and
   no border, so only the header card (`.o_fp_col_header`) drew any
   outline. Empty columns (BAKING / DE-RACKING / SHIPPING) looked
   unbounded — operators couldn't tell where one column ended and
   another began.

   Now: `.col` is the bordered white card (Trello / Asana style),
   stretches full height via grid + flex. `.o_fp_col_header` drops
   its standalone border / radius / background and is just a bottom-
   divider band inside the column card.

2. Horizontal scrollbar pinned to viewport bottom
   Was: `.o_fp_plant_kanban` was `min-height: 100vh` (block flow) +
   `.board` had `min-height: 520px; overflow-x: auto`. Scrollbar
   showed at the bottom of the .board element (~520px from top of
   board), floating mid-page below the empty columns.

   Now: parent is `height: 100vh; display: flex; flex-direction:
   column`. Header is `flex: 0 0 auto`; `.board` is `flex: 1 1 auto;
   min-height: 0` so it fills all remaining vertical and its
   scrollbar sits at the viewport bottom.

`.col-scroll` switched from `max-height: calc(100vh - 260px)` to
`flex: 1 1 auto; min-height: 0` so it expands inside the now-full-
height column instead of being capped at a magic number.

Version: fusion_plating_shopfloor 19.0.33.1.13.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 20:40:41 -04:00
gsinghpal
80f80fb707 fix(tablet): ACL, loading hang, timer offset + FP-tz clock
Four fixes shipped together — all surfaced during tablet UX walkthrough
on entech.

1. sale.order ACL on step completion
   Technicians hit "Access Denied... sale.order" when starting/finishing
   any step. _fp_check_receiving_gate + the serial-promotion helpers +
   _fp_resolve_contract_review_part read step.job_id.sale_order_id (and
   sale_order_line_ids) without sudo. Per Rule 13m, denormalized cross-
   module reads in tablet controllers must sudo() the source recordset.

2. Workspace stuck on "Loading Job Workspace…" after Hand Off + relogin
   Action params aren't URL-encoded, so the workspace remounts with
   jobId=null. refresh() exited early, state.data stayed null, "Loading"
   shown indefinitely. onMounted now redirects to the plant kanban
   when jobId is null or the initial load returns no data.

3. 4-hour timer offset on active steps
   workspace_controller used fp_format() to serialize date_started —
   which converts naive UTC to user tz wall time first. JS then
   appended 'Z' and parsed as UTC, so timer was offset by the user's
   tz (4h on EDT). Switched to fp_isoformat_utc() (proper +00:00 ISO)
   and dropped the Z-append in formatActiveStepElapsed +
   isActiveStepOvertime.

4. Lock-screen clock follows FP regional setting
   tablet_lock.js used d.getHours() / d.toLocaleDateString() — browser
   tz. Now /fp/tablet/tiles returns tz_name (fp_user_tz resolution:
   user.tz → company.x_fc_default_tz → UTC) and the formatters use
   Intl.DateTimeFormat with the explicit timeZone option. plant_overview
   now consumes server_time (already fp_format'd) instead of toLocaleTime
   String. Same chain Odoo backend uses, so PDF / view / tablet all
   agree on what time it is.

Versions: fusion_plating_jobs 19.0.10.30.0,
fusion_plating_shopfloor 19.0.33.1.12.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 20:31:25 -04:00
gsinghpal
8d4c85cc52 fix(workspace): drop native confirm() on Close Receiving
Native browser confirm popups look out of place in the tablet UI.
Mark Counted is already a deliberate prior step, so requiring a
second confirmation on Close Receiving was just friction. If a
receiver hits Close prematurely, action_reset_to_counted on
fp.receiving from the back office is the recovery path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 19:36:48 -04:00
gsinghpal
fc17754996 fix(workspace): required-inputs gate fires + manager bypass dialog
Two bugs:

1. Gate silently passed when step.recipe_node_id was NULL — happened
   to every WO-30057 step after this morning's clone delete (the FK
   ON DELETE SET NULL wiped the link). _fp_missing_required_step_inputs
   returned an empty recordset when node was None, so the gate had
   nothing to fail on and button_finish succeeded with zero audit.
   Fix: _fp_check_step_inputs_complete now treats NULL recipe_node_id
   as an explicit "no recipe link" hard block. Operator can't finish;
   manager bypass posts chatter audit.

2. No tablet UI for the manager bypass. The gate's bypass was a
   Python context flag — invisible from the JS layer, so managers
   were stuck behind the same hard error as operators.
   Fix: new /fp/workspace/finish_step endpoint returns structured
   errors (gate type, missing_prompts list, bypass_available bool).
   Server-side enforces manager group when bypass=True (can't trust
   the client). New FpFinishBlockDialog OWL modal renders:
   - Non-manager: Cancel + Record Inputs
   - Manager:     Cancel + Record Inputs + ⚠ Bypass & Finish (audit)

JobWorkspace.onFinishStep routes plain finishes through the new
endpoint; signature-required steps still go through /fp/workspace/sign_off
(separate gate). Added is_manager to /fp/workspace/load payload so
the JS knows which dialog variant to render.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 19:30:39 -04:00
gsinghpal
0371624afb feat(workspace): live HH:MM:SS timer on active step
Pure client-side tick — 1s setInterval bumps state.tickNow which the
template reads via formatActiveStepElapsed(step). No RPC per tick.
Reads step.date_started_iso (UTC) from the existing payload, parses
to ms, displays elapsed since.

- Green pill (#d1fae5 bg, monospace tabular-nums) on the ACTIVE badge
- Flips red (#fee2e2 + pulse animation) when elapsed > 1.5x
  duration_expected — visual cue for the operator that the step is
  running long against the recipe target

Cleanup interval on onWillUnmount alongside the existing 15s refresh
interval.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 19:18:49 -04:00
gsinghpal
eed1c4619d feat(workspace): pre-recipe receiving card with box count + damage log
Adds the receiver workflow to the Job Workspace tablet view (was the
gap behind WO-30057 sitting in Receiving with no way to advance).
Receivers no longer need to go to the backend form.

Workspace card (renders above the step list when fp.receiving in
state draft/counted on the linked SO):
- Draft state: numeric box-count input + per-line received_qty /
  condition picker (good/damaged/mixed) + Damage Log panel + Mark
  Counted button. Autosaves on input blur.
- Counted state: read-only summary (boxes, parts, who/when) +
  Damage Log still editable + Close Receiving button.
- Closed: card disappears, recipe takes over.

New FpDamageDialog OWL modal:
- Severity pill picker (Cosmetic / Functional / Rejected) with
  color-coded active state
- Required description textarea
- Action Required pill picker (None / Notify / Return / As-Is)
- Photo capture: both "Take Photo" (input capture="environment"
  triggers tablet camera) AND "Upload" (file picker fallback).
  Multi-photo with preview grid + per-photo remove.

5 new endpoints on workspace_controller.py:
- receiving_save_lines (autosave box_count_in + per-line qty/cond)
- receiving_mark_counted (wraps action_mark_counted)
- receiving_close (wraps action_close)
- damage_create (creates fp.receiving.damage + attaches base64 photos)
- damage_delete (removes a damage row)

No model changes — wraps existing fp.receiving actions and damage
CRUD. C3 (outbound shipping carrier/label) is a separate spec.

Spec: in-conversation brainstorm (C1+C2) following the 2026-05-24
workspace step actions spec; no standalone doc since scope is small.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 19:08:30 -04:00
gsinghpal
170398ab6f feat(workspace): per-kind step action buttons in Job Workspace
Fix: in the Job Workspace tablet view, the Start button was buried
inside a parent t-if that required the step to already be in_progress
or blocked. So ready/paused steps showed no buttons at all -
operators couldn't advance the WO from this screen (the reason the
user couldn't complete anything on WO-30057).

Template restructure (job_workspace.xml):
- Always-visible line 1 (icon + step# + name + ACTIVE/PAUSED badge + meta)
- Non-terminal detail panel (chips + instructions + opt-out + GateViz)
  visible on every non-done step so operator reads ahead
- Action row dispatched per-kind via getStepActions() helper

Per-kind action dispatcher (job_workspace.js):
- in_progress -> Record Inputs, Pause, Finish (or Finish & Sign Off)
- paused      -> Resume, Record Inputs, Finish
- contract_review (ready) -> Open QA-005 Form
- gating (ready)          -> Mark Passed (1-click start+finish)
- requires_rack_assignment -> Start (Assign Rack) - opens FpRackPartsDialog
- else (ready)            -> Start

5 new handlers: onPauseStep / onResumeStep / onMarkPassed /
onOpenContractReview / onStartWithRack. Pause and Resume use ORM RPC
(button_pause/button_resume) since no HTTP endpoint exists.

New model method (fp.job.step.action_mark_gating_passed):
- 1-click pass for gating steps - does button_start + button_finish
  in one transaction, posts chatter "Gate X marked passed by Y"
- Raises UserError if called on a non-gating step (defensive)
- Bypasses S21 required-inputs gate (gating steps have no inputs)

Controller: workspace_controller.py adds requires_rack_assignment to
the step payload so the JS dispatcher can route correctly.

Spec: docs/superpowers/specs/2026-05-24-workspace-step-actions-design.md
Sub-B (Record Inputs tablet polish: inputmode/prefill/date pickers/
signature pad/camera) is brainstormed but deferred.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 18:38:22 -04:00
gsinghpal
67fc22237b cleanup(shopfloor): session_swap is the only tablet flow
Frontend cleanup completing Phase G of the tablet PIN session
redesign:

- tablet_lock.js: removed sessionMode branching (no legacy path).
  unlock() always calls /fp/tablet/unlock_session + reloads.
  handOff() always calls tabletSessionManager.lockBack('manual').
  isLocked uses currentUid vs kioskUid exclusively. _checkIdle
  still drives the warning UI via activity_tracker; the actual
  lock RPC is owned by tablet_session_manager.

- fp_rpc.js: simplified to a thin async pass-through around @web/core
  network rpc. tech_store-based tablet_tech_id injection is gone
  (the session uid IS the tech).

- tech_store.js: DELETED (replaced by per-session backend attribution
  + tablet_session_manager for lock state). Removed from manifest.

- Wrapper components (shopfloor_landing, job_workspace,
  manager_dashboard, plant_kanban): swapped useService('fp_shopfloor_tech_store')
  for useService('fp_tablet_session_manager'); techStore.lock() ->
  tabletSessionManager.lockBack('manual'). plant_kanban's defensive
  try/catch on the tech_store lookup is no longer needed.

- tablet_lock.xml: Hand-Off button no longer gated on sessionMode;
  always rendered.

- Tests: removed legacy TestTabletUnlock class from test_tablet_pin.py
  (covered the deleted /fp/tablet/unlock route). Dropped session_mode
  assertion from test_tiles_bootstrap_fields.py (the return key is
  gone post-Phase-G). kiosk_uid + current_uid assertions retained.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 14:36:12 -04:00
gsinghpal
7ff46af192 fix(shopfloor): Phase D review findings — defensive cleanups + bootstrap test
Important I1: tablet_session_manager.beginSession() now calls
_removeListeners() (and clears any pending _tickHandle) defensively
at start. Prevents DOM listener leak on dev hot-reload or any path
that re-bootstraps without a clean endSession() first.

Important I2: tablet_lock._checkIdle() early-returns in session_swap
mode. The tablet_session_manager owns idle tracking there (5s poll,
calls /fp/tablet/lock_session directly). Was previously dormant by
accident because session_swap never populates the legacy techStore;
explicit guard makes the decoupling intentional.

Minor M5: session_swap unlock success now resets selectedTileUserId
before window.location.reload(), matching the legacy path''s
cleanup pattern. Cosmetic before reload kicks in.

Minor M9: New test_tiles_bootstrap_fields with 3 HttpCase tests
asserting /fp/tablet/tiles returns tablet_session_mode, kiosk_uid,
and current_uid. The OWL lock screen branches on all three — a
contract regression would silently break session_swap.

Minor M10: Added inline comment near _sessionModeCache declaration
in fp_rpc.js explaining the page-reload-invalidates-cache lifecycle.

Deferred (for future polish, NOT in this commit):
- I3 (_getSessionMode ACL gap for tech users — functionally correct,
  just suboptimal; cache fallback to ''legacy'' kicks in)
- M6 (wrapper component Hand-Off buttons no-op in session_swap)
- M7 (hardcoded idle/ceiling thresholds — server-configurable later)
- M8 (timer divergence vs activity_tracker — unify later)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 13:30:29 -04:00
gsinghpal
6d4b6284ad feat(shopfloor): fpRpc skips tablet_tech_id injection in session_swap mode
When tablet_session_mode='session_swap', the server attributes every
write via request.env.user — there's no need to pass tablet_tech_id
in the RPC params. Caches the mode lookup at module level so we don't
round-trip on every RPC.

Legacy mode unchanged — fpRpc still injects tablet_tech_id from
techStore.currentTechId.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 13:18:33 -04:00
gsinghpal
d8456fb9a3 feat(shopfloor): tablet_lock branches on tablet_session_mode
When ir.config_parameter[fp.shopfloor.tablet_session_mode]='session_swap',
PIN submit calls /fp/tablet/unlock_session and reloads the page; the
new session manager service kicks in on next mount. handOff() calls
lockBack('manual') which destroys the tech session server-side and
re-auths as kiosk.

Legacy mode unchanged — same /fp/tablet/unlock + techStore flow.

The feature flag, kiosk_uid, and current_uid arrive via the existing
/fp/tablet/tiles bootstrap response (Task D0).

Adds a tablet_lock-owned Hand-Off button visible only in session_swap
mode (in legacy mode wrapper components own their own buttons that hit
techStore.lock(); session_swap renders our own button so the manual
hand-off goes through lockBack() + page reload).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 13:17:53 -04:00
gsinghpal
b41d9629e1 feat(shopfloor): tablet_session_manager OWL service
Tracks idle + ceiling timers for an unlocked tech session. Fires
/fp/tablet/lock_session when either trips, then reloads the page so
the browser re-bootstraps under the fresh kiosk session.

Defaults: 10min idle, 8hr ceiling, 5s tick interval. Listens for
click/touchstart/keydown/mousemove as activity signals.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 13:15:28 -04:00
gsinghpal
0f751d82cc feat(shopfloor): add Record Inputs button to Job Workspace step row
Operators trying to Finish a step with required step_input prompts
got the S21 gate error telling them to 'Click Record Inputs on the
step row' — but the workspace UI never exposed that button. Only the
job-form view had it.

Adds a 'Record Inputs' secondary button next to Finish/Finish & Sign
Off when the step is active. Click opens the fp_record_inputs_dialog
(via action_open_input_wizard on fp.job.step). On dialog close the
workspace refreshes so the step's progress chip updates.

Module version: 19.0.32.0.10 -> 19.0.32.0.11

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 10:34:09 -04:00
gsinghpal
e99cf20887 style(shopfloor): tablet lock clock 24h -> 12h with AM/PM
Operators read phone-style clocks; 24-hour was off-norm for North
American shop. Hour no longer zero-padded (1:05 PM, not 01:05 PM)
to match the iPhone/Android idiom.

Module version: 19.0.32.0.7 -> 19.0.32.0.8

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 10:16:17 -04:00
gsinghpal
cc5542833f style(shopfloor): tablet lock screen tile grid 3 -> 5 columns
Wider tablets fit 5 tiles per row comfortably; 3 was too sparse with
a 20-person operator roster (forced a long vertical scroll). Bumped
.o_fp_lock_tiles max-width from 480px to 800px so the tiles don't
stretch wide at 5 columns.

Module version: 19.0.32.0.6 -> 19.0.32.0.7

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 10:13:52 -04:00
gsinghpal
42036c23ab fix(plating-perms): Phase I post-deploy fixes (live entech test catches)
8 distinct bugs caught + fixed while testing the live admin DB on entech
after the migration was approved. Each surfaced a real Odoo 19 gotcha
now codified in CLAUDE.md (rules 13b-13l).

Picker architecture:
- res.users.x_fc_plating_landing_action_id and res.company.x_fc_default_landing_action_id
  now Many2one('ir.actions.actions') instead of ('ir.actions.act_window'),
  so the picker accepts BOTH window actions (Sale Orders / Quotations /
  Process Recipes) AND client actions (Manager Desk / Plant Kanban /
  Quality Dashboard). Picker went from 3 entries to 6.
- x_fc_pickable_landing field moved from the two subclasses to the
  ir.actions.actions base. Single source of truth.
- _render_resolved on the base dispatches to the correct subclass by
  action type.

Non-admin Preferences access:
- Added ACL grant: group_fp_technician (and all higher roles via
  implication) get read on ir.actions.actions. Without this, opening
  Preferences raised AccessError on the picker domain evaluation.
- Removed the accessible_landing_action_ids Many2many compute (failed
  for non-admins because field assignment requires write access on
  the comodel relation, even with sudo'd search). Picker now shows all
  6 entries to all users; resolver falls through gracefully if the
  user picks an action they can't reach.
- res.users SELF_WRITEABLE_FIELDS / SELF_READABLE_FIELDS extended via
  @property + super() (NOT class attribute — Odoo 19 changed the
  pattern). Non-admin users can now save the Preferences dialog with
  plating fields without hitting the standard write ACL.

Migration workflow:
- res.groups.users -> .user_ids (Odoo 19 rename; deprecated alias
  removed). Was crashing _fp_notify_owners and _cron_purge_expired.
- user.message_post -> user.partner_id.message_post (res.users uses
  _inherits delegation which doesn't expose mail.thread methods).
  Was crashing the Owner approval click.

Tablet lock screen:
- /fp/tablet/tiles points at group_fp_technician instead of the old
  group_fusion_plating_operator. Post-migration nobody holds the old
  group directly (only via implication), so res.groups.user_ids on
  the old xmlid returned empty — 'No operators configured' shown
  even with PIN set.
- PIN pad dots dark mode: empty dot now dark gray (#424245), filled
  dot now pure white. Previous version had both at light shades so
  user couldn't see PIN entry progress.
- Lock-screen logo frame dark mode: near-opaque white plate
  (rgba 0.95) so company logos designed for light backgrounds
  render correctly. Previous 0.08 alpha let the dark page bleed
  through.

Pre-deploy collision fix (already committed before deploy but
documented here for completeness):
- pre-migrate.py to rename old configurator's 'Shop Manager' group
  display name before new fp_security_v2.xml loads the new
  group_fp_shop_manager_v2 with the same display name (avoids
  res_groups_name_uniq violation).

Module versions bumped:
  fusion_plating: 19.0.21.1.0 -> 19.0.21.1.2
  fusion_plating_shopfloor: 19.0.32.0.4 -> 19.0.32.0.6

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 10:02:32 -04:00
gsinghpal
d89546bec7 fix(shopfloor): Back button + logo frame shape
Two fixes from live testing of the 2026-05-24 redesign:

1. Job Workspace Back button routed to deprecated component.
   onBack() hardcoded tag: 'fp_shopfloor_landing' so tapping a card on
   the new plant kanban -> opening the workspace -> clicking Back
   dropped the user into the OLD per-step kanban (the legacy OWL
   component the data-record redirects don't reach because doAction
   bypasses the data layer).
   Fix: change the hardcoded tag to 'fp_plant_kanban'. Grep
   confirmed it's the only such reference in JS.

2. Logo frame shape — wider, shorter, logo bigger.
   140x140 square -> 280x110 rectangle. Better fit for horizontal
   company logos (mark + name + tagline laid out left-to-right).
   Uniform 18px padding on all sides so the image breathes evenly.
   Image area is ~244x74 (vs old ~104x104), so a typical horizontal
   logo renders ~50% wider. border-radius 28->22 for the flatter
   rect; letter-mark placeholder font 52->44 to fit the shorter
   frame.

Also augmented CLAUDE.md 'Legacy-action redirect' rule with a new
'grep JS for hardcoded doAction' clause — the XML-record redirect
trick only covers ir.actions.client data; OWL components with inline
this.action.doAction({tag: ...}) calls bypass the data layer entirely
and need a separate sweep.

Asset cache cleared (3 stale attachments).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 22:11:51 -04:00
gsinghpal
818dfa3882 fix(shopfloor): bigger logo frame on the tablet lock screen
User feedback after live testing: the 84px logo frame felt too small
and the image inside used only a fraction of the frame area.

Bumped the frame to 140px (1.67x) — image scales with the container
via the existing max-width/max-height: 100% rule. Proportional
adjustments to padding (14→18), border-radius (20→28), margin-bottom
(12→16), and the letter-mark placeholder font (32→52).

SCSS-only change. Asset cache cleared (3 stale attachments).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 22:06:17 -04:00
gsinghpal
772107d25b feat(shopfloor): tablet lock-screen redesign — frontend + manifest
LS-T2..T6 of the tablet lock-screen redesign (LS-T1 backend shipped
separately in c6137100).

Files:
  - _tablet_lock_tokens.scss  (new — design tokens, dark/light branches
                               via $o-webclient-color-scheme, registered
                               first in manifest per project rule 8)
  - tablet_lock.scss          (full rewrite — gradient bg, glassmorphic
                               tiles, 4 entrance keyframes, hover lift,
                               click press, clocked-in pulse,
                               prefers-reduced-motion gate)
  - tablet_lock.xml           (extended — logo + clock + prompt blocks
                               wrapping the existing tile loop; tile
                               inner shape updated for avatar gradient,
                               has_photo conditional, is_clocked_in
                               modifier)
  - tablet_lock.js            (extended — state.clockText / dateText /
                               company, setInterval(60s) clock tick,
                               _formatTime / _formatDate / tileStyle /
                               avatarClass helpers per project rule 20)
  - __manifest__.py           (19.0.31.0.0 -> 19.0.32.0.0, registered
                               new tokens SCSS BEFORE tablet_lock.scss)

Verified live on entech:
  - Module upgrade clean, registry loaded in 15.5s
  - 6 stale asset attachments cleared
  - Helpers in tablet_controller.py emit company payload + initials +
    gradients correctly (Garry Singh -> GS, EN Tech -> ET, uid=5 ->
    pink gradient)
  - res.company.logo present (has_logo: True)
  - All animations gated by prefers-reduced-motion per spec §6

CLAUDE.md updated with new Critical Rule 22 about Odoo 19 HTML fields
auto-wrapping plain-string writes — caught during Task 1 testing when
the original 'tagline equality' test failed because res.company.report
_header is an HTML field that wraps writes with <p> tags.

Closes the 6-task plan in
  docs/superpowers/plans/2026-05-24-tablet-lock-screen-redesign-plan.md
Spec: docs/superpowers/specs/2026-05-24-tablet-lock-screen-redesign-design.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 21:56:32 -04:00
gsinghpal
fd2b2908f3 fix(shopfloor): plant-view card sizing — match the mockup proportions
User feedback after live testing: cards were too cramped on the 9-column
board. Restoring the Variant C mockup proportions and letting the board
scroll horizontally on smaller viewports (user explicitly accepted
side-scrolling).

Changes:
  - .board grid: repeat(9, 1fr) → repeat(9, minmax(300px, 1fr))
    plus overflow-x: auto. Each column ~300px so the card has room to
    breathe. ~6 columns visible on 1920px desktop, ~4 on 1366px tablet,
    smooth horizontal scroll for the rest.
  - .col-scroll: gap 4→8, max-height eased so cards aren't packed.
  - .o_fp_plant_card: padding 8/10→12, gap 4→6, base font 11→12.
  - card-wo: 13→16 (matches mockup header size).
  - card-step: 12→14.
  - chips: padding 1/6→2/8, font 10→11, radius 10→12.
  - mini-timeline blocks: 8→16px tall (current step 11→22px), labels
    8→9px. Matches the mockup's punchy timeline strip.
  - progress bar: max-width 60→100, height 3→4.
  - operator pill / icon-row: bumped to match card scale.

No backend changes. SCSS-only. Asset cache cleared (3 attachments).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 21:20:01 -04:00
gsinghpal
8b9b4d60ad feat(shopfloor): Phase 4 — plant-view kanban frontend (OWL + SCSS + XML)
PV-Phase4 of the plant-view redesign. 19 new files implementing the
6-component OWL tree plus design tokens.

Components (each = JS + XML + SCSS triple):
  - FpMiniTimeline    — 9-step bar consuming mini_timeline_json
  - FpPlantCard       — Variant C card; 13 state-* CSS classes; tap
                        opens fp_job_workspace
  - FpColumnHeader    — column label + count badge + 'You're here'
                        badge when paired
  - FpKpiTile         — clickable KPI button with urgent/warn/good
                        variants and active state
  - FpFilterChip      — toggleable chip
  - FpPlantKanban     — top-level orchestrator: 10s polling, mode
                        toggle, search + 6 filter chips, board with
                        9 fixed columns, localStorage filter persistence

SCSS:
  - _plant_tokens.scss (loads first, exposes $plant-* vars to every
    later file — required because Odoo 19 forbids @import in custom
    SCSS, manifest order IS the concat order)
  - Dark mode via $o-webclient-color-scheme compile-time branch

Manifest registers all assets in dependency order: tokens → component
SCSS → component XML → leaf JS → top-level JS. Mirrors the existing
project pattern.

Critical patterns honored:
  - Project rule 20 (no String/Number/parseInt in OWL templates):
    all coercion in JS, string literals in foreach arrays.
  - No t-out without markup() (none in this batch — all card text is
    pre-formatted by the controller).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:57:55 -04:00
gsinghpal
1a3ca8704e feat(plating): session 2026-05-23 deploys — F1/F7/S22/S23 + UI fixes
Consolidated commit of session work already deployed to entech and
verified via the deep audit + the persona walk:

S22 — Signoff gate (fp.job.step.requires_signoff was 100% unenforced,
42/42 done steps had NULL signoff_user_id). Three-piece fix:
_fp_autosign_if_required (captures finisher on button_finish),
_fp_check_signoff_complete (raises UserError if NULL after autosign),
action_signoff (explicit supervisor pre-sign). Bypass:
fp_skip_signoff_gate=True.

S23 — Transition-form gate (same dormant-field shape as S22, caught
preventively before recipe authors flipped requires_transition_form
on). Model helpers on fp.job.step.move + controller gate in
move_controller (parts commit) + pre-reject in rack commit.

F7 — Chatter standardization: _fp_create_qc_check_if_needed,
_fp_fire_notification, _fp_create_delivery silent failures now also
post to job chatter instead of only logging to file.

UI fixes:
- Critical Rule 20 documented + applied: OWL templates only expose
  Math as a global. Calling String(d) inside t-on-click throws
  'v2 is not a function'. Fixed pin_pad.xml (string array instead of
  number array with String() coercion). Also swept parseInt/
  parseFloat in recipe_tree_editor + simple_recipe_editor.
- Notes panel HTML escape fix: chatter messages off /fp/workspace/load
  were rendered via t-out, escaping the HTML. Wrap with markup() in
  job_workspace.js refresh() before assigning to state.

Versions:
  fusion_plating         19.0.20.8.0 → 19.0.20.9.0
  fusion_plating_jobs    19.0.10.20.0 → 19.0.10.23.0
  fusion_plating_shopfloor 19.0.30.2.0 → 19.0.30.5.0

All deployed to entech (LXC 111) and verified live.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 20:37:17 -04:00
gsinghpal
5f03080374 feat(shopfloor): switch action-path RPCs to fpRpc + wire plant_overview/move_card (P6.3.5)
JobWorkspace, ShopfloorLanding, ManagerDashboard, and the embedded
FpHoldComposer now call fpRpc() for write-path endpoints (start/finish
step, hold create, sign-off, milestone advance, work-centre move,
assign-worker, assign-tank, manager takeover). fpRpc auto-injects
tablet_tech_id from the tech_store so the server can rebind env via
env_for_tablet_tech() and credit the right user.

Read-path RPCs (workspace/load, landing/kanban, manager/overview,
manager/funnel, manager/approval_inbox, manager/at_risk, shopfloor/scan)
stay as plain rpc() — no audit benefit, no need for the extra plumbing.

Also wires tablet_tech_id into /fp/shopfloor/plant_overview/move_card
which I missed in P6.3.3 — surfaced when grepping JS for write callers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:47:20 -04:00
gsinghpal
fee4219703 feat(fusion_plating_shopfloor): fpRpc wrapper + env_for_tablet_tech helper (P6.3.1)
Client-side fpRpc() is a drop-in for rpc() that automatically injects
tablet_tech_id from the tech_store into every action call. Read-only
endpoints can keep using plain rpc().

Server-side env_for_tablet_tech(env, tablet_tech_id) returns an env
scoped via with_user() when the id is a valid active user; otherwise
returns the original env unchanged. Controllers call this at the top
of action methods so all subsequent writes carry the right uid.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:37:02 -04:00
gsinghpal
d86c120969 feat(fusion_plating_shopfloor): FpPinSetup client action for self-service PIN (P6.2.6)
Registers fp_tablet_pin_setup as an ir.actions.client tag. Triggered
from res.users preferences via action_open_tablet_pin_setup (added
to res_users.py in P6.1.1). Three-stage flow:

  loading → check if user has existing PIN via search_count
  old     → enter current PIN (skipped if first-time)
  new     → choose new PIN
  confirm → enter new PIN again
  done    → success toast + auto-close 1.5s later

Each stage reuses FpPinPad with a different onSubmit + title. On
mismatch / server error, resets to the first stage with a notification.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:33:28 -04:00
gsinghpal
85609f99cd feat(fusion_plating_shopfloor): wire FpTabletLock + Hand-Off into Landing/Workspace/Manager (P6.2.5)
Three OWL client actions all wrap their root in <FpTabletLock>:

  ShopfloorLanding   wraps o_fp_landing
  JobWorkspace       wraps o_fp_ws
  ManagerDashboard   wraps o_fp_manager

Each adds FpTabletLock to static components, imports tech_store, and
gains a handOff() method that calls techStore.lock(). The Hand-Off
button (yellow, lock icon) lands next to the scan/QR controls in each
header — pressing it instantly returns the tablet to the tile grid
without waiting for the idle timer.

Component composition (per spec §6.5):
  FpTabletLock
    if isLocked → tile grid + FpPinPad
    else → existing client action (via <t t-slot="default"/>) + FpIdleWarning

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:32:52 -04:00
gsinghpal
29821bd541 feat(fusion_plating_shopfloor): FpTabletLock outer wrapper component (P6.2.4)
Top-level wrapper that renders lock screen (tile grid + PIN pad) when
no tech is signed in, and renders <t t-slot="default"/> otherwise.
Drives the auto-lock countdown via the activity_tracker service +
sends a /fp/tablet/ping heartbeat every 60s while a tech is signed in.

Tiles fetch from /fp/tablet/tiles using the localStorage station id
(set by ShopfloorLanding on QR pair / station picker selection).

State machine for the lock screen body:
  loadingTiles → tiles list → tile tapped → PinPad → unlock RPC
                                          ↑
                                          onPinCancel → back to tiles

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:29:24 -04:00
gsinghpal
1fdafd34d1 feat(fusion_plating_shopfloor): FpIdleWarning overlay (P6.2.3)
Fixed-position yellow-border overlay + countdown toast shown during
the last N (default 30) seconds before auto-lock. Pure props-driven —
secondsRemaining is the only input; parent (FpTabletLock) decides
when to mount and unmount. Box-shadow pulse animation runs CSS-only
so OWL doesn't need to re-render every tick.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:28:30 -04:00
gsinghpal
9584953467 feat(fusion_plating_shopfloor): FpPinPad numeric keypad component (P6.2.2)
Reusable 4-digit PIN pad. Auto-submits on the 4th digit via the
onSubmit prop. On wrong PIN, shake animation + dots clear + error
banner (caller controls the message via the returned {ok:false, error}).

Used by FpTabletLock (unlock flow) and FpPinSetup (set/change flow).

Dark-mode SCSS branch follows the same $o-webclient-color-scheme
pattern as the rest of the shopfloor components.

Also registers tech_store + activity_tracker services in the asset
bundle (assets/web.assets_backend) before the pin_pad files, since
the pin_pad/tablet_lock components consume them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:28:01 -04:00
gsinghpal
52097ca59b feat(fusion_plating_shopfloor): tech_store + activity_tracker OWL services (P6.2.1)
Two registry-level services:

tech_store    Shared reactive state holding currentTechId after a
              successful PIN unlock. Other components subscribe via
              useService("fp_shopfloor_tech_store") and read
              currentTechId to inject into action RPCs. setTech(id, name)
              on unlock; lock() on auto-lock / Hand-Off.

activity_tracker  Document-level event tracker for pointerdown / touchstart
              / keydown / visibilitychange. Mouse-move alone deliberately
              EXCLUDED — a tool resting on a tablet would otherwise keep
              the session alive indefinitely. Public API:
                bump(), getSecondsUntilLock(), getWarnThresholdSec()
              Reads thresholds from ir.config_parameter at start +
              every 5 min (so manager edits propagate within a shift).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 00:27:13 -04:00
gsinghpal
f00dda2abd feat(fusion_plating_shopfloor): Manager Desk 4-tab refactor (P4.5-P4.10)
Plan tasks P4.5 through P4.10 batched. Existing 3-column Plant Board
becomes one tab of four; adds Workflow Funnel (default), Approval
Inbox, and At-Risk siblings. Adds 2 new KPI tiles for Pending Cert +
At-Risk.

  WORKFLOW FUNNEL (default tab)
    Calls /fp/manager/funnel. Renders one row per fp.job.workflow.state
    with stage chip + count + top 5 WO cards. Tap a card → JobWorkspace.
    Bar chart bar behind each row scales with stage count.

  APPROVAL INBOX
    Calls /fp/manager/approval_inbox. Three strips: Holds to Release,
    Certs to Issue, Scrap to Review. Per-row open + Open Workspace
    buttons. Tab badge shows total pending count.

  PLANT BOARD (existing — relocated as one tab)
    The 3-column Needs Worker / In Progress / Team layout that already
    exists, wrapped in t-if="activeTab === 'plant_board'". No behaviour
    change — still uses /fp/manager/overview with 8s refresh.

  AT-RISK
    Calls /fp/manager/at_risk. 3 sub-panels: Trending Late (sorted by
    late_risk_ratio desc), Hold Reasons (read_group), Bottleneck heatmap
    (bottleneck_score from P4.1 with red/yellow/green bars).

  KPI STRIP (new conditional tiles)
    Pending Cert  — count from inbox.certs_to_issue, click to open Inbox tab.
    At-Risk       — count from at_risk.trending_late, click to open At-Risk.

Auto-refresh: 8s for /fp/manager/overview (existing); the active tab's
data also refreshes every 8s via refreshActiveTab().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 22:21:53 -04:00
gsinghpal
7f70785b79 feat(fusion_plating_shopfloor): ShopfloorLanding client action (P3.2-P3.4)
Plan tasks P3.2 + P3.3 + P3.4 batched. Full ShopfloorLanding OWL
client action — replaces fp_shopfloor_tablet AND folds in
fp_plant_overview.

  Header strip          Title, station chip, station picker dropdown,
                        Station/All-Plant mode toggle, QR scan controls,
                        last-refresh indicator.
  KPI strip             4 tech-relevant tiles: Ready · Running ·
                        Bakes Due (warning) · Holds (red when > 0).
  Search                Live debounced (200ms) across WO# + customer +
                        part. ESC clears.
  Kanban board          Columns = work centres from /fp/landing/kanban.
                        Cards = FpKanbanCard (Phase 1 — P1.7).
                        Drag-and-drop reuses existing
                        /fp/shopfloor/plant_overview/move_card.
  Card tap              doAction → fp_job_workspace with
                        {job_id, focus_step_id}.
  QR scan               FP-STATION pairs, FP-JOB / FP-STEP jump to the
                        Workspace.

Mode + station_id persist in localStorage (LS_STATION_ID, LS_MODE).
Auto-refresh every 15s; suppressed during a drop and for 5s after.

Registers client action `fp_shopfloor_landing`. Menu rewire + endpoint
stubs land in P3.5 + P3.6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 22:09:09 -04:00
gsinghpal
a18ef6c405 feat(fusion_plating_shopfloor): JobWorkspace client action (header/steps/side/rail)
Plan tasks P1.12 through P1.15 batched. Full-screen OWL component
registered as fp_job_workspace. Layout:

  STICKY HEADER       WO #, customer, part, qty/done, deadline,
                      WorkflowChip, holds badge
  STICKY WORKFLOW BAR 9-stage dots (passed/current/pending) +
                      Next-action button driving advance_milestone
  STEP LIST           All steps with state icons; active step
                      auto-expanded with recipe chips (thickness/
                      dwell/bake/sign-off) + instructions + Start/
                      Finish buttons; blocked steps show GateViz;
                      override-excluded steps faded
  SIDE PANEL          Customer spec PDF link, attachments list,
                      chatter notes
  STICKY ACTION RAIL  Create Hold (HoldComposer modal), Add Note
                      (chatter via message_post), Issue Cert (when
                      draft cert exists), Next Milestone

Auto-refresh every 15s. Sign-off steps route Finish through
SignaturePad → /fp/workspace/sign_off.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:52:26 -04:00
gsinghpal
a61bd05a5c feat(fusion_plating_shopfloor): KanbanCard shared OWL service
Plan task P1.7. Final shared service — standard WO card used on Landing
kanban, Manager Plant Board, and Workflow Funnel. Embeds WorkflowChip,
shows progress bar, priority dot, blocker badge from step.blocker_kind.

Density prop ('compact' vs 'normal') swaps padding for funnel use.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:48:13 -04:00
gsinghpal
8109b3ec76 feat(fusion_plating_shopfloor): HoldComposer shared OWL service
Plan task P1.6. Modal hold-creation form: reason picker, qty split,
optional photo (camera input on mobile), description, mark-for-scrap
toggle. Calls /fp/workspace/hold (added in P1.9). Reason list kept
client-side, keep in sync with fusion.plating.quality.hold.hold_reason.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:47:29 -04:00
gsinghpal
9d78bc4317 feat(fusion_plating_shopfloor): SignaturePad shared OWL service
Plan task P1.5. Modal canvas signature capture using HTML pointer events
+ Odoo Dialog service. Returns image/png dataURI via onSubmit callback;
caller decides what to do with it (e.g. /fp/workspace/sign_off attaches
to fp.job.step).

Canvas stays light even in dark mode for signature legibility.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:46:46 -04:00
gsinghpal
5c3c979f77 feat(fusion_plating_shopfloor): GateViz shared OWL service
Plan task P1.4. "Can't start yet — Waiting on Step N: X" block reused
across JobWorkspace step rows and Manager Plant Board cards. Icon set
maps to blocker_kind (predecessor/contract_review/parts_not_received/
racking_required/manager_input). Optional Jump button propagates to
parent via onJump callback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:46:07 -04:00
gsinghpal
b52fe01d07 feat(fusion_plating_shopfloor): WorkflowChip shared OWL service + dark-mode SCSS
Plan task P1.3. Bootstraps the tests/ dir and adds the first of 5
shared OWL services. Pill renders fp.job.workflow.state with color
mapping + optional next-action hint.

Per CLAUDE.md "Dark Mode" rule: registered once in web.assets_backend;
Odoo 19 auto-compiles into both bright and dark bundles via the
\$o-webclient-color-scheme SCSS branch.

Version bumped to 19.0.27.0.0 (Phase 1 — Workspace foundation).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:45:33 -04:00
gsinghpal
01a46e33e2 fix(process-tree): breadcrumb pile-up + dead "No MO selected" banner
Two issues on the Process Tree client action:

1. Back to Work Order kept growing breadcrumbs (WO -> Tree -> WO ->
   Tree -> ...) because onBack used action.doAction() which PUSHES
   a new act_window onto the stack instead of popping. Fixed by
   trying action.restore() first (pops the Tree off the stack and
   returns to the parent WO/Step controller). Falls through to
   explicit doAction only when there's no parent in the stack
   (direct URL access).

2. The empty-state banner referenced productionId, a dead variable
   from the bridge_mrp era when the tree was tied to mrp.production.
   Since the component now uses jobId (fp.job context key), the
   "No manufacturing order selected" message ALWAYS fired regardless
   of whether a job was loaded. Fixed by using jobId and updating
   the message to "No work order selected".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 09:05:27 -04:00
gsinghpal
dcbe8305d0 ui(process-tree): back to Work Order + pulsing green for done steps
Two improvements to the Process Tree visualization opened from the
Work Order's Process Tree header button:

  1. Back button returns to the Work Order (job form) instead of
     Plant Overview. fp.job.action_open_process_tree now passes
     back_job_id in the client-action context; process_tree.js
     reads it via a new backJobId getter, updates the button label
     to "Back to Work Order", and routes onBack to fp.job form.
     The Plant Overview fallback stays for callers that don't pass
     either back_step_id or back_job_id.

  2. Completed operation/step cards now have a green fill (#1e8449)
     and a subtle pulsing glow (box-shadow animation, 2.6s alternate)
     so finished work pops against still-pending dark cards. Hover
     pauses the animation so the click target is steady. Reuses the
     same green the workflow-state slice already used.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 08:13:46 -04:00
gsinghpal
7d3b8f132a fix(sub12c+): close 3 known gaps — rack travel ticket, cert statement, CoC actuals
Gap 1 — Rack Travel Ticket PDF (Sub 12b's Save+Print 404):
  + report_fp_rack_travel.xml in fusion_plating_reports — A5 landscape
    single page, big rack name, Code 128 of FP-RACK:<name>, tag chips,
    contained part-batches table.
  + ir.actions.report bound to fusion.plating.rack so it appears in
    the rack form's Print menu too.
  + Sub 12b's rack_parts_dialog.js Save+Print URL fixed to use the
    standard /report/pdf/<xmlid>/<id> route.

Gap 2 — Per-customer cert statement:
  + res.company.x_fc_default_cert_statement (company-level fallback).
  + res.partner.x_fc_cert_statement (per-customer override).
  + Surfaced on the partner form under the existing Cert + Document
    Routing block.
  + Chronological CoC body resolves: customer override → company
    default → hardcoded AS9100/ISO 9001 boilerplate. Three-tier
    fallback so existing certs without overrides keep working.

Gap 3 — Chronological CoC 'Actual' column:
  + Build a captured_values_by_input dict from the move's
    transition_input_value_ids (Sub 12b captures these on every
    Move Parts commit).
  + Render typed Actual: text → as-is, number → with target unit,
    boolean → PASS/FAIL, date → formatted, attachment → '[Attachment]'
    placeholder.
  + Falls back to prompts from the destination step's step_input list
    when no values were captured (still useful as audit-of-what-was-
    asked even if blank).

Version bumps:
  fusion_plating → 19.0.10.3.0
  fusion_plating_reports → 19.0.10.1.0
  fusion_plating_certificates → 19.0.5.3.0

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:55:48 -04:00
gsinghpal
11dbbf578e feat(sub12b): plant overview Racks pane (Task 16)
Controller: extend /fp/shopfloor/plant_overview return payload to
include 'racks' array (filtered to loaded/in_use/awaiting_unrack
states). Each entry has tag chips, part count, current node
breadcrumb, current step + tank code, and a precomputed
next_step_id (next sequence in the job's recipe — operator
overrides at runtime in the Move Rack dialog).

JS: state.racks populated from payload. New openMoveRackDialog()
method spawns FpMoveRackDialog. Notification when rack has no
successor (last step of job).

XML: top section above the existing work-centre columns. Renders
rack rows with tags, part count, breadcrumb, and primary MOVE RACK
button per row. Visible only when state.racks.length > 0.

SCSS: minimal styling for the racks pane (extends move_dialogs.scss
to keep all Sub 12b styles in one file).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:19:05 -04:00
gsinghpal
902f3e8398 feat(sub12b): wire Move Parts + Stop Timer dialogs into tablet (Task 15)
ShopfloorTablet component:
- Imports the 3 new OWL dialogs.
- useService('dialog') for spawning.
- Listens for 'fp-resolve-rack' window CustomEvent fired from inside
  FpMovePartsDialog → spawns FpRackPartsDialog inline.
- New methods: openMovePartsDialog(from, to) + openStopTimerDialog(id).
  Refresh tablet after commit/reconcile so the UI reflects new state.

Listener cleanup on unmount.

Note: the actual buttons that call these methods are added to the
existing tablet XML in a follow-up step — for now they are wired but
not surfaced. Operators get them after Task 16 + smoke test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:16:15 -04:00
gsinghpal
11bc0ca742 feat(sub12b): shared SCSS for Move/Rack/Timer dialogs (Task 14)
3-column grid layout for field rows (label / input / hint).
Compliance prompts + Blockers blocks have their own backgrounds.
Soft blockers amber + left border, hard blockers red + left border —
matches the spec's protocol.

Token pattern + dark-mode @if branch (CLAUDE.md rule: Odoo 19 doesn't
flip dark mode via runtime DOM class; we branch at SCSS compile time
on $o-webclient-color-scheme).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:15:12 -04:00
gsinghpal
270f427d7f feat(sub12b): Move Rack + Stop Timer OWL dialogs (Task 13)
Move Rack: rack name in title via getter, tag chips, batches list
(read-only), Type + To Node + To Station picker. Atomic Save commits
all batches via /fp/tablet/move_rack/commit.

Stop Timer: opens with state already at 'stopped' (server flipped on
load via /labor_timer/stop), pre-fills billed_* from accrued.
Operator edits → Save (state → reconciled).

Save & Start New Timer chains into a fresh timer for the same step
via the start_new=True flag — mirrors screen 10's right-most button.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:14:30 -04:00
gsinghpal
48c06c40c9 feat(sub12b): OWL Rack Parts sub-dialog (Task 12)
Mirrors screens 7-8. Searchable empty-rack picker with debounced
typeahead via /fp/tablet/rack/list_empty. QR Scan button prompts
operator for FP-RACK:<name> token, resolves via /fp/tablet/rack/
scan_qr.

Save commits the racking via /fp/tablet/rack_parts/commit. Save+Print
opens /web/report/pdf/fp.rack.travel/<id> in a new tab — that report
ships in Sub 12c, returns 404 until then. Plain Save works today.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:13:04 -04:00
gsinghpal
6d046f2881 feat(sub12b): OWL Move Parts dialog (Task 11)
Mirrors Steelhead screens 1-3, 14-15. Loads preview on mount,
re-checks hard-blockers on commit. MOVE (n) button disabled when
hard-blocked OR required prompt blank — improvement over Steelhead's
silent disabled state (we show a tooltip listing reasons).

Inline 'Resolve' button next to each blocker. For rack-required,
fires a window CustomEvent ('fp-resolve-rack') the parent tablet
catches to open the Rack Parts sub-dialog.

Typed input rendering by input_type — text/number/checkbox/select/
datetime, plus support for time_hms and signature/photo (text input
for now; full upload widget in Sub 12c).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:11:49 -04:00
gsinghpal
66cfe5f97f changes 2026-04-27 09:41:46 -04:00
gsinghpal
f51976cb08 changes 2026-04-27 08:48:55 -04:00