feat(plant-overview): live debounced search + bigger search bar

The search bar required Enter to fire, which felt clunky on a shop
floor where managers expect cards to filter as they type. Switched
to a 200ms-debounced live search — fast enough to feel instant on
keystrokes, slow enough to skip the network call when someone is
mid-word.

Search bar visual weight bumped:
  - Width 260px → 380px (320px on iPad, full width on phones)
  - Height 48px → 52px
  - Font-size base → md, weight medium
  - Search icon nudged 14px → 16px from the edge with a 1.05rem size
  - Placeholder uses the lighter $fp-ink-faint so the input feels
    inviting rather than already-filled

Behaviour:
  - Type → cards filter after 200ms of no input
  - Enter → fires immediately (skips debounce) for power users
  - Escape → clears the search (new shortcut)
  - Clear button → unchanged

Bumped shopfloor to 19.0.14.0.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-04-18 23:53:12 -04:00
parent 9b3b674197
commit 4065c6891b
3 changed files with 32 additions and 7 deletions

View File

@@ -5,7 +5,7 @@
{
'name': 'Fusion Plating — Shop Floor',
'version': '19.0.13.0.0',
'version': '19.0.14.0.0',
'category': 'Manufacturing/Plating',
'summary': 'Shop-floor tablet stations, QR scanning, bake window enforcer, '
'first-piece inspection gates.',

View File

@@ -74,18 +74,33 @@ export class PlantOverview extends Component {
}
// ----- Search ------------------------------------------------------------
//
// Live search with a 200ms debounce. The user types, the cards update
// as they go — no "press Enter" leap of faith. Debounce keeps us off
// the network on every keystroke when someone types fast.
onSearchInput(ev) {
this.state.searchTerm = ev.target.value;
this._debouncedSearch();
}
_debouncedSearch() {
if (this._searchTimer) clearTimeout(this._searchTimer);
this._searchTimer = setTimeout(() => this.loadData(), 200);
}
onSearchKey(ev) {
// Enter still works — fires immediately, skipping the debounce.
if (ev.key === "Enter") {
if (this._searchTimer) clearTimeout(this._searchTimer);
this.loadData();
} else if (ev.key === "Escape") {
this.onSearchClear();
}
}
onSearchClear() {
if (this._searchTimer) clearTimeout(this._searchTimer);
this.state.searchTerm = "";
this.loadData();
}

View File

@@ -65,26 +65,36 @@
align-items: center;
.o_fp_po_search_icon {
position: absolute; left: 14px;
position: absolute; left: 16px;
color: $fp-ink-mute; pointer-events: none;
font-size: 1.05rem;
}
.o_fp_po_search_input {
padding: 0 $fp-space-4 0 $fp-space-7;
min-height: $fp-touch-min;
// Bumped from 260px → 380px and slightly taller padding so the
// search bar carries proper visual weight on the toolbar. Live
// search (200ms debounce) makes the input feel like the
// primary affordance on the page, not an afterthought.
padding: 0 $fp-space-5 0 $fp-space-8;
min-height: 52px;
border: 1px solid #{$fp-border};
border-radius: $fp-radius-md;
background-color: $fp-card;
color: $fp-ink;
box-shadow: $fp-elev-1;
width: 260px;
font-size: $fp-text-base;
width: 380px;
font-size: $fp-text-md;
font-weight: $fp-weight-medium;
transition: box-shadow $fp-dur $fp-ease, border-color $fp-dur $fp-ease;
&::placeholder { color: $fp-ink-faint; font-weight: $fp-weight-medium; }
&:focus {
@include fp-focus-ring;
border-color: $fp-accent;
}
@media (max-width: 600px) { width: 100%; }
// Tablet — keep it generously sized but cap so the toolbar
// doesn't blow past the viewport.
@media (max-width: 1180px) { width: 320px; min-height: 48px; }
@media (max-width: 900px) { width: 100%; }
}
.o_fp_po_search_clear {
position: absolute; right: 6px;