diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/scss/_fp_shopfloor_tokens.scss b/fusion_plating/fusion_plating_shopfloor/static/src/scss/_fp_shopfloor_tokens.scss index 4f601aea..6a779835 100644 --- a/fusion_plating/fusion_plating_shopfloor/static/src/scss/_fp_shopfloor_tokens.scss +++ b/fusion_plating/fusion_plating_shopfloor/static/src/scss/_fp_shopfloor_tokens.scss @@ -1,115 +1,131 @@ // ============================================================================= -// Fusion Plating — Shop-Floor Design Tokens -// Copyright 2026 Nexa Systems Inc. -// License OPL-1 (Odoo Proprietary License v1.0) +// Fusion Plating — Shop Floor Design System (v2, 2026-04) +// Copyright 2026 Nexa Systems Inc. · License OPL-1 // -// Single source of truth for the look-and-feel of every shop-floor OWL page. -// All values use CSS custom properties (--bs-*, --o-*) so the tokens adapt -// automatically between Odoo's light and dark themes — no media queries or -// duplicate palettes. -// -// Gradients use `color-mix(in srgb, var(--bs-foo) X%, transparent)` because -// transparent mixes with the page background, which already obeys the theme. +// Design philosophy: +// * NO card borders — depth comes from elevation (shadow) only +// * Generous whitespace, calm surfaces, one accent colour +// * Semantic colours (success/warning/danger) reserved for STATUS — not +// decoration +// * Type-first hierarchy: big headings + big numbers + small helpers +// * Every value resolves from Odoo CSS custom properties, so light +// and dark themes work without duplicate palettes // ============================================================================= +// ---------- Spacing scale (8-pt baseline) ------------------------------------ +$fp-space-1 : 4px; +$fp-space-2 : 8px; +$fp-space-3 : 12px; +$fp-space-4 : 16px; +$fp-space-5 : 20px; +$fp-space-6 : 24px; +$fp-space-7 : 32px; +$fp-space-8 : 40px; +$fp-space-9 : 48px; +$fp-space-10 : 64px; -// ---------- Radii ------------------------------------------------------------ -$fp-radius-sm : 8px; -$fp-radius-md : 12px; -$fp-radius-lg : 16px; -$fp-radius-xl : 20px; -$fp-radius-pill : 999px; +// ---------- Radius ----------------------------------------------------------- +$fp-radius-sm : 10px; +$fp-radius-md : 14px; +$fp-radius-lg : 20px; +$fp-radius-xl : 28px; +$fp-radius-pill: 999px; +// ---------- Surfaces — depth by TINT, not by border -------------------------- +// The page gets a slightly tinted background; cards sit on a lighter +// surface. That tint difference replaces the need for card borders. +$fp-page : color-mix(in srgb, var(--bs-body-color) 2.5%, + var(--o-view-background-color, var(--bs-body-bg))); +$fp-card : var(--o-view-background-color, var(--bs-body-bg)); +$fp-card-soft : color-mix(in srgb, var(--bs-body-color) 4%, + var(--o-view-background-color, var(--bs-body-bg))); -// ---------- Elevation (shadows that respect dark mode) ----------------------- -// Shadows are built on the page's foreground colour so they darken in light -// mode and deepen (but never go pure black) in dark mode. -$fp-shadow-xs : 0 1px 2px color-mix(in srgb, var(--bs-body-color) 6%, transparent); -$fp-shadow-sm : 0 2px 6px color-mix(in srgb, var(--bs-body-color) 9%, transparent); -$fp-shadow-md : 0 4px 14px color-mix(in srgb, var(--bs-body-color) 10%, transparent), - 0 1px 3px color-mix(in srgb, var(--bs-body-color) 6%, transparent); -$fp-shadow-lg : 0 10px 28px color-mix(in srgb, var(--bs-body-color) 14%, transparent), - 0 2px 6px color-mix(in srgb, var(--bs-body-color) 6%, transparent); -$fp-shadow-glow-primary : 0 0 0 4px color-mix(in srgb, var(--o-action) 12%, transparent); +// ---------- Text tiers ------------------------------------------------------- +$fp-ink : var(--bs-body-color); +$fp-ink-soft : color-mix(in srgb, var(--bs-body-color) 70%, transparent); +$fp-ink-mute : color-mix(in srgb, var(--bs-body-color) 48%, transparent); +$fp-ink-faint : color-mix(in srgb, var(--bs-body-color) 28%, transparent); +// ---------- Elevation — soft, layered, theme-safe ---------------------------- +// Shadows built on the foreground colour so they darken appropriately in +// light mode and show a subtle halo in dark mode. +$fp-elev-1 : 0 1px 2px color-mix(in srgb, var(--bs-body-color) 5%, transparent), + 0 1px 3px color-mix(in srgb, var(--bs-body-color) 7%, transparent); +$fp-elev-2 : 0 2px 4px color-mix(in srgb, var(--bs-body-color) 6%, transparent), + 0 6px 14px color-mix(in srgb, var(--bs-body-color) 9%, transparent); +$fp-elev-3 : 0 4px 8px color-mix(in srgb, var(--bs-body-color) 8%, transparent), + 0 12px 28px color-mix(in srgb, var(--bs-body-color) 12%, transparent); +$fp-elev-hover : 0 6px 12px color-mix(in srgb, var(--bs-body-color) 10%, transparent), + 0 18px 36px color-mix(in srgb, var(--bs-body-color) 14%, transparent); -// ---------- Surface layering ------------------------------------------------- -// A "raised" surface tint — sits slightly above the base background. Uses -// the foreground colour to tint so it deepens correctly in dark mode. -$fp-surface : var(--o-view-background-color, var(--bs-body-bg)); -$fp-surface-raised : color-mix(in srgb, - var(--bs-body-color) 2%, - var(--o-view-background-color, var(--bs-body-bg))); -$fp-surface-sunken : color-mix(in srgb, - var(--bs-body-color) 4%, - var(--o-view-background-color, var(--bs-body-bg))); +// ---------- Semantic colour helpers (NOT gradients) -------------------------- +$fp-accent : var(--o-action); // the one action colour +$fp-ok : var(--bs-success); +$fp-warn : var(--bs-warning); +$fp-bad : var(--bs-danger); +$fp-info : var(--bs-info); +// Softened backgrounds for status pills / banners +@function fp-wash($color-var, $strength: 12%) { + @return color-mix(in srgb, var(#{$color-var}) #{$strength}, transparent); +} -// ---------- Border-tints ----------------------------------------------------- -$fp-border : var(--bs-border-color); -$fp-border-strong : color-mix(in srgb, var(--bs-body-color) 18%, transparent); -$fp-border-accent : color-mix(in srgb, var(--o-action) 40%, var(--bs-border-color)); +// ---------- Type scale ------------------------------------------------------ +// Shop-floor tablets are read from 18" — baseline bumped from Odoo default. +$fp-text-xs : 0.75rem; // 12px small labels +$fp-text-sm : 0.875rem; // 14px helper text +$fp-text-base : 1rem; // 16px body +$fp-text-md : 1.125rem; // 18px emphasis +$fp-text-lg : 1.25rem; // 20px sub-headings +$fp-text-xl : 1.5rem; // 24px section headings +$fp-text-2xl : 2rem; // 32px page title +$fp-text-3xl : 2.75rem; // 44px KPI number +$fp-text-4xl : clamp(2rem, 5vw, 3rem); // hero +$fp-weight-medium : 500; +$fp-weight-semibold : 600; +$fp-weight-bold : 700; -// ---------- Typography ------------------------------------------------------- -// Shop-floor tablets and phones get read from 18" away — bumps over Odoo's -// default body size. Numbers are tabular so KPIs don't jitter on refresh. -$fp-font-base : 1rem; // 16px -$fp-font-sm : 0.875rem; // 14px -$fp-font-xs : 0.78rem; // 12.5px -$fp-font-lg : 1.125rem; // 18px -$fp-font-xl : 1.375rem; // 22px -$fp-font-2xl : 1.75rem; // 28px -$fp-font-3xl : 2.25rem; // 36px -$fp-font-kpi : 2.5rem; // 40px — headline number -$fp-font-hero : clamp(1.5rem, 3.5vw, 2.25rem); +$fp-font-stack : -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + "Inter", "Helvetica Neue", Arial, sans-serif; - -// ---------- Animation -------------------------------------------------------- +// ---------- Motion ----------------------------------------------------------- $fp-ease : cubic-bezier(0.22, 1, 0.36, 1); -$fp-ease-snap : cubic-bezier(0.34, 1.56, 0.64, 1); -$fp-dur-fast : 140ms; -$fp-dur : 220ms; +$fp-ease-out : cubic-bezier(0.33, 1, 0.68, 1); +$fp-dur-fast : 120ms; +$fp-dur : 200ms; $fp-dur-slow : 360ms; - -// ---------- Semantic gradients (tone-based, theme-safe) ---------------------- -// Each tone mixes the named Bootstrap token with transparent so it lays -// naturally over whatever page background it's on — light OR dark. -@mixin fp-grad($color-var, $strong: 18%, $soft: 6%) { - background-image: linear-gradient( - 135deg, - color-mix(in srgb, var(#{$color-var}) #{$strong}, transparent) 0%, - color-mix(in srgb, var(#{$color-var}) #{$soft}, transparent) 100% - ); -} - -@mixin fp-grad-solid($color-var, $from: 100%, $to: 75%) { - background-image: linear-gradient( - 135deg, - color-mix(in srgb, var(#{$color-var}) #{$from}, transparent) 0%, - color-mix(in srgb, var(#{$color-var}) #{$to}, transparent) 100% - ); - color: var(--o-we-text-on-action, #fff); -} +// ---------- Touch ------------------------------------------------------------ +$fp-touch-min : 48px; // larger than Apple's 44px minimum — shop floor -// ---------- Tonal helpers ---------------------------------------------------- -// Border + tint combo used on banners / pills — reads as "slightly -// saturated panel" in both themes. -@mixin fp-tone($color-var, $strength: 14%) { - background-color: color-mix(in srgb, var(#{$color-var}) #{$strength}, transparent); - color: var(#{$color-var}); - border: 1px solid color-mix(in srgb, var(#{$color-var}) 38%, transparent); -} +// ============================================================================= +// Mixins +// ============================================================================= - -// ---------- Focus ring ------------------------------------------------------- -@mixin fp-focus-ring() { +// Focus ring — used on all interactive inputs/buttons +@mixin fp-focus-ring { outline: none; - box-shadow: 0 0 0 3px color-mix(in srgb, var(--o-action) 35%, transparent); + box-shadow: 0 0 0 3px color-mix(in srgb, #{$fp-accent} 35%, transparent); } +// Card surface — shadow-based, no border +@mixin fp-card($elev: $fp-elev-1) { + background-color: $fp-card; + border-radius: $fp-radius-lg; + box-shadow: $elev; +} -// ---------- Touch target ----------------------------------------------------- -$fp-touch-min : 44px; // Apple HIG minimum +// Status pill (soft tint + colored text) +@mixin fp-pill($color-var) { + background-color: color-mix(in srgb, var(#{$color-var}) 14%, transparent); + color: var(#{$color-var}); +} + +// Hide hover styles on touch devices (stuck hover = bad UX on phones) +@mixin fp-hover-only { + @media (hover: hover) { + @content; + } +} diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/scss/fusion_plating_shopfloor.scss b/fusion_plating/fusion_plating_shopfloor/static/src/scss/fusion_plating_shopfloor.scss index 6bca12a0..aaab4aeb 100644 --- a/fusion_plating/fusion_plating_shopfloor/static/src/scss/fusion_plating_shopfloor.scss +++ b/fusion_plating/fusion_plating_shopfloor/static/src/scss/fusion_plating_shopfloor.scss @@ -1,669 +1,563 @@ // ============================================================================= -// Fusion Plating — Shop-Floor Tablet Station -// Copyright 2026 Nexa Systems Inc. -// License OPL-1 (Odoo Proprietary License v1.0) +// Fusion Plating — Tablet Station (Worker view) +// Copyright 2026 Nexa Systems Inc. · License OPL-1 // -// Modernised 2026-04 design: hero banner, gradient KPI cards, tap-first -// action rows. Works in Odoo's light + dark themes because every colour -// resolves from CSS custom properties or color-mix'd tokens. -// -// Variables / mixins come from _fp_shopfloor_tokens.scss — loaded FIRST -// in the asset bundle (see __manifest__.py). No @import; Odoo 19 forbids -// local @import statements in custom SCSS. +// Shop-floor operators' home screen. Built fresh from the design system in +// _fp_shopfloor_tokens.scss. No card borders — depth by shadow + tint. // ============================================================================= -// ============================================================================= -// Global touch / hover suppression (kept from previous rev) -// ============================================================================= +// --- Hover suppression on touch devices -------------------------------------- @media (hover: none) { - .o_fp_tablet .o_fp_queue_row:hover, - .o_fp_tablet .o_fp_tile:hover, - .o_fp_tablet .o_fp_tablet_card:hover { - background-color: inherit !important; - border-color: var(--bs-border-color) !important; - box-shadow: none !important; + .o_fp_tablet [class*="o_fp_"]:hover { + transform: none !important; + box-shadow: inherit !important; } } // ============================================================================= -// Root container +// Page root // ============================================================================= .o_fp_tablet { - background-color: $fp-surface-sunken; - color: var(--bs-body-color); + font-family: $fp-font-stack; + background-color: $fp-page; + color: $fp-ink; min-height: 100%; - padding: 24px 28px; - font-size: $fp-font-base; + padding: $fp-space-6 $fp-space-7; display: flex; flex-direction: column; - gap: 18px; + gap: $fp-space-6; - @media (max-width: 900px) { padding: 16px; gap: 14px; } - @media (max-width: 600px) { padding: 10px 10px 16px; gap: 10px; } + @media (max-width: 900px) { padding: $fp-space-4; gap: $fp-space-4; } + @media (max-width: 600px) { padding: $fp-space-3; gap: $fp-space-4; } - // ========================================================================= - // Hero banner — sits at the top, shows who's logged in + station - // ========================================================================= + // ------------------------------------------------------------------------- + // Hero — greeting + station chip + actions + // ------------------------------------------------------------------------- .o_fp_tablet_header { - position: relative; - overflow: hidden; - border-radius: $fp-radius-lg; - padding: 20px 24px; - background-color: $fp-surface-raised; - border: 1px solid $fp-border; - box-shadow: $fp-shadow-sm; - - // Brand gradient wash — primary + success blend reads as - // "productive activity". Low opacity so text stays readable. - &::before { - content: ""; - position: absolute; - inset: 0; - background-image: - radial-gradient(circle at 0% 0%, - color-mix(in srgb, var(--o-action) 22%, transparent) 0%, - transparent 55%), - radial-gradient(circle at 100% 100%, - color-mix(in srgb, var(--bs-success) 20%, transparent) 0%, - transparent 55%); - pointer-events: none; - z-index: 0; - } - display: grid; grid-template-columns: 1fr auto; - gap: 16px; - align-items: center; - > * { position: relative; z-index: 1; } + gap: $fp-space-5; + align-items: end; @media (max-width: 600px) { - padding: 14px 14px 12px; grid-template-columns: 1fr; - gap: 10px; + gap: $fp-space-3; } } .o_fp_tablet_title { - font-size: $fp-font-hero; - font-weight: 700; + font-size: $fp-text-2xl; + font-weight: $fp-weight-bold; + letter-spacing: -0.02em; line-height: 1.1; - letter-spacing: -0.01em; - color: var(--bs-body-color); - display: flex; align-items: center; gap: 10px; + margin: 0; + color: $fp-ink; + display: flex; align-items: center; gap: $fp-space-3; } .o_fp_tablet_subtitle { - margin-top: 4px; - font-size: $fp-font-sm; - color: var(--bs-secondary-color); - display: flex; flex-wrap: wrap; gap: 10px; + margin-top: $fp-space-2; + font-size: $fp-text-sm; + color: $fp-ink-mute; + display: flex; flex-wrap: wrap; gap: $fp-space-3; align-items: center; } .o_fp_tablet_chip { display: inline-flex; align-items: center; - gap: 6px; + gap: $fp-space-1; padding: 4px 12px; border-radius: $fp-radius-pill; - font-size: $fp-font-xs; - font-weight: 600; - letter-spacing: 0.02em; - @include fp-tone(--o-action, 14%); + font-size: $fp-text-xs; + font-weight: $fp-weight-semibold; + background-color: color-mix(in srgb, #{$fp-accent} 12%, transparent); + color: $fp-accent; } .o_fp_tablet_header_actions { - display: flex; gap: 8px; align-items: center; + display: flex; + gap: $fp-space-2; + align-items: center; + @media (max-width: 600px) { - width: 100%; flex-wrap: wrap; + width: 100%; > * { flex: 1; min-width: 0; } } } .o_fp_station_picker { min-width: 240px; - max-width: 340px; min-height: $fp-touch-min; - padding: 8px 14px; + padding: $fp-space-2 $fp-space-4; + border: none; border-radius: $fp-radius-md; - background-color: $fp-surface; - border: 1px solid $fp-border; - color: var(--bs-body-color); - font-size: $fp-font-base; - transition: border-color $fp-dur $fp-ease, box-shadow $fp-dur $fp-ease; - &:focus { @include fp-focus-ring; border-color: var(--o-action); } - @media (max-width: 600px) { min-width: 0; max-width: 100%; } + background-color: $fp-card; + color: $fp-ink; + font-size: $fp-text-base; + box-shadow: $fp-elev-1; + cursor: pointer; + transition: box-shadow $fp-dur $fp-ease; + + &:focus { @include fp-focus-ring; } + @media (max-width: 600px) { min-width: 0; width: 100%; } } .o_fp_scan_toggle { min-height: $fp-touch-min; - padding: 8px 18px; + padding: 0 $fp-space-5; + border: none; border-radius: $fp-radius-md; - font-weight: 600; - transition: transform $fp-dur-fast $fp-ease; + background-color: $fp-card; + color: $fp-ink; + font-weight: $fp-weight-semibold; + font-size: $fp-text-base; + cursor: pointer; + box-shadow: $fp-elev-1; + transition: transform $fp-dur-fast $fp-ease, box-shadow $fp-dur $fp-ease; + + @include fp-hover-only { &:hover { box-shadow: $fp-elev-2; } } &:active { transform: scale(0.97); } } - // ========================================================================= + // ------------------------------------------------------------------------- // Scan drawer - // ========================================================================= + // ------------------------------------------------------------------------- .o_fp_scan_drawer { - display: flex; gap: 10px; - padding: 14px; + display: flex; + gap: $fp-space-3; + padding: $fp-space-4; border-radius: $fp-radius-lg; - background-color: $fp-surface-raised; - border: 1px dashed $fp-border-accent; - box-shadow: $fp-shadow-xs; + background-color: $fp-card; + box-shadow: $fp-elev-1; } - - - // ========================================================================= - // Flash message - // ========================================================================= - .o_fp_tablet_message { - padding: 12px 16px; + .o_fp_scan_input { + flex: 1; + min-height: $fp-touch-min; + padding: 0 $fp-space-4; + font-size: $fp-text-md; + border: none; border-radius: $fp-radius-md; - font-size: $fp-font-base; - font-weight: 500; - display: flex; align-items: center; gap: 10px; - box-shadow: $fp-shadow-xs; - - &.o_fp_msg_info { @include fp-tone(--bs-info); } - &.o_fp_msg_success { @include fp-tone(--bs-success); } - &.o_fp_msg_warning { @include fp-tone(--bs-warning); } - &.o_fp_msg_danger { @include fp-tone(--bs-danger); } + background-color: $fp-page; + color: $fp-ink; + &:focus { @include fp-focus-ring; } + &::placeholder { color: $fp-ink-faint; } + } + .o_fp_big_button { + min-height: $fp-touch-min; + padding: 0 $fp-space-5; + border: none; + border-radius: $fp-radius-md; + background-color: $fp-accent; + color: var(--o-we-text-on-action, #fff); + font-weight: $fp-weight-semibold; + font-size: $fp-text-base; + cursor: pointer; + transition: transform $fp-dur-fast $fp-ease, filter $fp-dur-fast $fp-ease; + &:hover:not(:disabled) { filter: brightness(1.06); } + &:active:not(:disabled) { transform: scale(0.97); } + &:disabled { opacity: 0.5; cursor: not-allowed; } } - // ========================================================================= - // KPI strip — gradient tiles, big numbers - // ========================================================================= + // ------------------------------------------------------------------------- + // Flash message — inline banner + // ------------------------------------------------------------------------- + .o_fp_tablet_message { + display: flex; align-items: center; gap: $fp-space-3; + padding: $fp-space-3 $fp-space-4; + border-radius: $fp-radius-md; + font-size: $fp-text-base; + font-weight: $fp-weight-medium; + box-shadow: $fp-elev-1; + background-color: $fp-card; + color: $fp-ink; + + &::before { + content: ""; + width: 6px; align-self: stretch; + border-radius: 3px; + background-color: $fp-info; + } + &.o_fp_msg_info { &::before { background-color: $fp-info; } } + &.o_fp_msg_success { &::before { background-color: $fp-ok; } } + &.o_fp_msg_warning { &::before { background-color: $fp-warn; } } + &.o_fp_msg_danger { &::before { background-color: $fp-bad; } } + } + + + // ------------------------------------------------------------------------- + // KPI tiles — minimal, big number + // ------------------------------------------------------------------------- .o_fp_kpi_strip { display: grid; - grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); - gap: 12px; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: $fp-space-4; @media (max-width: 600px) { grid-template-columns: repeat(2, 1fr); - gap: 8px; + gap: $fp-space-3; } } .o_fp_kpi { position: relative; - overflow: hidden; - padding: 18px 18px 16px; + padding: $fp-space-5; border-radius: $fp-radius-lg; - background-color: $fp-surface-raised; - border: 1px solid $fp-border; - box-shadow: $fp-shadow-sm; - transition: transform $fp-dur $fp-ease, - box-shadow $fp-dur $fp-ease, - border-color $fp-dur $fp-ease; - display: flex; - flex-direction: column; - gap: 4px; - min-height: 104px; + background-color: $fp-card; + box-shadow: $fp-elev-1; + display: flex; flex-direction: column; gap: $fp-space-2; + transition: transform $fp-dur $fp-ease, box-shadow $fp-dur $fp-ease; - // Accent bar along the top edge - &::before { - content: ""; - position: absolute; - top: 0; left: 0; right: 0; height: 3px; - background-image: linear-gradient(90deg, transparent, currentColor, transparent); - opacity: 0.7; + @include fp-hover-only { &:hover { transform: translateY(-2px); box-shadow: $fp-elev-2; } } + + > .fa { + font-size: 1.1rem; + color: $fp-ink-mute; } - // Soft tone overlay + .o_fp_kpi_value { + font-size: $fp-text-3xl; + font-weight: $fp-weight-bold; + line-height: 1; + letter-spacing: -0.03em; + font-variant-numeric: tabular-nums; + color: $fp-ink; + } + .o_fp_kpi_label { + font-size: $fp-text-sm; + color: $fp-ink-mute; + font-weight: $fp-weight-medium; + } + + // Accent dot keyed to tone (top-right) — tiny, confident &::after { content: ""; position: absolute; - inset: 0; - opacity: 0.35; - pointer-events: none; - z-index: 0; + top: $fp-space-4; right: $fp-space-4; + width: 10px; height: 10px; + border-radius: 50%; + background-color: $fp-ink-faint; } - - @media (hover: hover) { - &:hover { - transform: translateY(-2px); - box-shadow: $fp-shadow-md; - } - } - > * { position: relative; z-index: 1; } - - > .fa { - position: absolute; - right: 14px; top: 14px; - font-size: 1.5rem; - opacity: 0.28; - color: currentColor; - } - - .o_fp_kpi_value { - font-size: $fp-font-kpi; - font-weight: 800; - line-height: 1; - letter-spacing: -0.02em; - font-variant-numeric: tabular-nums; - color: var(--bs-body-color); - } - .o_fp_kpi_label { - font-size: $fp-font-xs; - color: var(--bs-secondary-color); - text-transform: uppercase; - letter-spacing: 0.06em; - font-weight: 600; - } - - &.o_fp_kpi_info { color: var(--bs-info); &::after { @include fp-grad(--bs-info); } } - &.o_fp_kpi_success { color: var(--bs-success); &::after { @include fp-grad(--bs-success); } } - &.o_fp_kpi_warning { color: var(--bs-warning); &::after { @include fp-grad(--bs-warning); } } + &.o_fp_kpi_info { &::after { background-color: $fp-info; } } + &.o_fp_kpi_success { &::after { background-color: $fp-ok; } } + &.o_fp_kpi_warning { &::after { background-color: $fp-warn; } } &.o_fp_kpi_danger { - color: var(--bs-danger); - border-color: color-mix(in srgb, var(--bs-danger) 45%, var(--bs-border-color)); - &::after { @include fp-grad(--bs-danger, 22%, 8%); } - .o_fp_kpi_value { color: var(--bs-danger); } + &::after { background-color: $fp-bad; } + .o_fp_kpi_value { color: $fp-bad; } } - &.o_fp_kpi_muted { color: var(--bs-secondary-color); &::after { opacity: 0; } } + &.o_fp_kpi_muted { &::after { display: none; } } @media (max-width: 600px) { - min-height: 84px; - padding: 12px 12px 10px; - .o_fp_kpi_value { font-size: 1.6rem; } - .o_fp_kpi_label { font-size: 0.68rem; } - > .fa { font-size: 1rem; top: 10px; right: 10px; } + padding: $fp-space-4; + .o_fp_kpi_value { font-size: $fp-text-2xl; } + .o_fp_kpi_label { font-size: $fp-text-xs; } } } - // ========================================================================= - // Active WO banner — green gradient "you're running this right now" - // ========================================================================= + // ------------------------------------------------------------------------- + // Active WO banner — unmistakable when a job is running + // ------------------------------------------------------------------------- .o_fp_active_wo { - position: relative; - overflow: hidden; display: flex; justify-content: space-between; align-items: center; - gap: 14px; - padding: 16px 20px; + gap: $fp-space-4; + padding: $fp-space-5; border-radius: $fp-radius-lg; - border: 1px solid color-mix(in srgb, var(--bs-success) 45%, $fp-border); - box-shadow: $fp-shadow-sm; - @include fp-grad(--bs-success, 14%, 4%); + background-color: fp-wash(--bs-success, 14%); + box-shadow: $fp-elev-1; + color: $fp-ink; @media (max-width: 600px) { - flex-direction: column; align-items: stretch; gap: 10px; - padding: 14px; border-radius: $fp-radius-md; + flex-direction: column; align-items: stretch; > .btn { width: 100%; min-height: $fp-touch-min; } } } .o_fp_active_wo_left { - display: flex; align-items: center; gap: 14px; + display: flex; align-items: center; gap: $fp-space-3; + min-width: 0; } .o_fp_active_wo_title { - font-weight: 700; font-size: $fp-font-lg; letter-spacing: -0.01em; + font-weight: $fp-weight-bold; + font-size: $fp-text-md; + letter-spacing: -0.01em; + color: $fp-ink; } - .o_fp_active_wo_meta { - color: var(--bs-secondary-color); font-size: $fp-font-sm; margin-top: 2px; + .o_fp_active_wo_meta { + font-size: $fp-text-sm; + color: $fp-ink-mute; + margin-top: 2px; } .o_fp_active_wo_pulse { flex-shrink: 0; - width: 14px; height: 14px; border-radius: 50%; - background-color: var(--bs-success); + width: 12px; height: 12px; + border-radius: 50%; + background-color: $fp-ok; animation: o_fp_pulse 1.4s ease-in-out infinite; } @keyframes o_fp_pulse { - 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--bs-success) 60%, transparent); } - 70% { box-shadow: 0 0 0 12px color-mix(in srgb, var(--bs-success) 0%, transparent); } - 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--bs-success) 0%, transparent); } + 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, #{$fp-ok} 55%, transparent); } + 50% { box-shadow: 0 0 0 10px color-mix(in srgb, #{$fp-ok} 0%, transparent); } } - // ========================================================================= + // ------------------------------------------------------------------------- // Dashboard layout - // ========================================================================= + // ------------------------------------------------------------------------- .o_fp_tablet_dashboard { display: grid; - grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); - gap: 16px; + grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); + gap: $fp-space-5; + @media (max-width: 1100px) { grid-template-columns: 1fr; } - @media (max-width: 600px) { gap: 10px; } + @media (max-width: 600px) { gap: $fp-space-3; } } .o_fp_right_col { - display: flex; flex-direction: column; gap: 16px; - @media (max-width: 600px) { gap: 10px; } + display: flex; flex-direction: column; gap: $fp-space-5; + @media (max-width: 600px) { gap: $fp-space-3; } } - // ========================================================================= - // Panels (reusable card) - // ========================================================================= + // ------------------------------------------------------------------------- + // Panel (section container) + // ------------------------------------------------------------------------- .o_fp_panel { - background-color: $fp-surface-raised; - border: 1px solid $fp-border; - border-radius: $fp-radius-lg; - padding: 18px 20px; - box-shadow: $fp-shadow-xs; - - @media (max-width: 600px) { - padding: 12px 14px; border-radius: $fp-radius-md; - } + @include fp-card($fp-elev-1); + padding: $fp-space-5; + @media (max-width: 600px) { padding: $fp-space-4; } } .o_fp_panel_head { - display: flex; justify-content: space-between; align-items: center; - margin-bottom: 14px; - padding-bottom: 10px; - border-bottom: 1px solid $fp-border; + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: $fp-space-4; h3 { - font-size: $fp-font-lg; - font-weight: 700; - margin: 0; - display: inline-flex; align-items: center; gap: 10px; + font-size: $fp-text-lg; + font-weight: $fp-weight-bold; letter-spacing: -0.01em; + margin: 0; + display: inline-flex; align-items: center; gap: $fp-space-2; + color: $fp-ink; } } .o_fp_panel_count { - min-width: 34px; - padding: 3px 12px; + font-size: $fp-text-sm; + font-weight: $fp-weight-semibold; + color: $fp-ink-mute; + background-color: $fp-card-soft; + padding: 2px 12px; border-radius: $fp-radius-pill; - font-weight: 700; - font-size: $fp-font-sm; - font-variant-numeric: tabular-nums; - @include fp-tone(--bs-body-color, 8%); } - // ========================================================================= - // Empty state - // ========================================================================= + // ------------------------------------------------------------------------- + // Empty states — friendly, not boxed + // ------------------------------------------------------------------------- .o_fp_empty { - padding: 32px 16px; + padding: $fp-space-7 $fp-space-4; text-align: center; - color: var(--bs-secondary-color); - font-size: $fp-font-base; + color: $fp-ink-mute; + font-size: $fp-text-base; i.fa { display: block; - font-size: 2.25rem; - margin-bottom: 10px; - opacity: 0.55; + font-size: 2.75rem; + opacity: 0.5; + margin-bottom: $fp-space-3; } } - // ========================================================================= - // My Queue - // ========================================================================= + // ------------------------------------------------------------------------- + // Queue — flat list, big rows, no borders + // ------------------------------------------------------------------------- .o_fp_queue_list { list-style: none; margin: 0; padding: 0; - display: flex; flex-direction: column; gap: 10px; + display: flex; flex-direction: column; gap: $fp-space-2; } .o_fp_queue_row { display: grid; - grid-template-columns: 44px 1fr auto; + grid-template-columns: 48px 1fr auto; align-items: center; - gap: 12px; - padding: 12px 14px; - border: 1px solid $fp-border; + gap: $fp-space-3; + padding: $fp-space-3 $fp-space-4; border-radius: $fp-radius-md; - background-color: $fp-surface; + background-color: $fp-page; min-height: 64px; - transition: background-color $fp-dur $fp-ease, - border-color $fp-dur $fp-ease, - transform $fp-dur-fast $fp-ease, - box-shadow $fp-dur $fp-ease; + transition: background-color $fp-dur $fp-ease, transform $fp-dur-fast $fp-ease; - @media (hover: hover) { + @include fp-hover-only { &:hover { - transform: translateY(-1px); - background-color: color-mix(in srgb, var(--o-action) 4%, $fp-surface); - border-color: $fp-border-accent; - box-shadow: $fp-shadow-sm; + background-color: color-mix(in srgb, #{$fp-accent} 6%, $fp-page); + transform: translateX(2px); } } @media (max-width: 600px) { - grid-template-columns: 36px 1fr; - padding: 10px 12px; - + grid-template-columns: 40px 1fr; .o_fp_queue_actions { grid-column: 1 / -1; - justify-content: stretch; - margin-top: 6px; + margin-top: $fp-space-2; > .btn { flex: 1; } } } } .o_fp_queue_body { cursor: pointer; min-width: 0; } .o_fp_queue_label { - font-weight: 700; - font-size: $fp-font-base; - color: var(--bs-body-color); + font-weight: $fp-weight-semibold; + font-size: $fp-text-base; + color: $fp-ink; } .o_fp_queue_desc { - font-size: $fp-font-sm; - color: var(--bs-secondary-color); + font-size: $fp-text-sm; + color: $fp-ink-mute; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .o_fp_queue_actions { - display: flex; gap: 6px; align-items: center; - .btn { min-height: $fp-touch-min; font-weight: 600; border-radius: $fp-radius-md; } - } .o_fp_queue_pri { display: flex; align-items: center; justify-content: center; - width: 40px; height: 40px; border-radius: $fp-radius-md; - font-weight: 800; - font-size: 0.78rem; - letter-spacing: 0.02em; + width: 40px; height: 40px; + border-radius: $fp-radius-md; + font-weight: $fp-weight-bold; + font-size: 0.72rem; + letter-spacing: 0.04em; - &[data-priority="high"] { - @include fp-grad(--bs-danger, 30%, 12%); - color: var(--bs-danger); - border: 1px solid color-mix(in srgb, var(--bs-danger) 40%, transparent); - } - &[data-priority="med"] { - @include fp-grad(--bs-warning, 28%, 10%); - color: var(--bs-warning); - border: 1px solid color-mix(in srgb, var(--bs-warning) 40%, transparent); - } - &[data-priority="low"] { - background-color: color-mix(in srgb, var(--bs-body-color) 5%, transparent); - color: var(--bs-secondary-color); - border: 1px solid $fp-border; + &[data-priority="high"] { @include fp-pill(--bs-danger); } + &[data-priority="med"] { @include fp-pill(--bs-warning); } + &[data-priority="low"] { background-color: $fp-card-soft; color: $fp-ink-mute; } + } + .o_fp_queue_actions { + display: flex; gap: $fp-space-2; + .btn { + min-height: $fp-touch-min; + padding: 0 $fp-space-4; + border-radius: $fp-radius-md; + font-weight: $fp-weight-semibold; + border: none; + transition: transform $fp-dur-fast $fp-ease, filter $fp-dur-fast $fp-ease; + &:hover { filter: brightness(1.06); } + &:active { transform: scale(0.97); } } } - // ========================================================================= - // Bath / Gate / Hold tiles + rows - // ========================================================================= + // ------------------------------------------------------------------------- + // Bath tiles + // ------------------------------------------------------------------------- .o_fp_tile_grid { display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 12px; - @media (max-width: 600px) { grid-template-columns: 1fr 1fr; gap: 8px; } + grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); + gap: $fp-space-3; + @media (max-width: 600px) { grid-template-columns: 1fr 1fr; } } .o_fp_tile { - position: relative; overflow: hidden; - border: 1px solid $fp-border; + padding: $fp-space-4; border-radius: $fp-radius-md; - padding: 14px 16px; - background-color: $fp-surface; + background-color: $fp-page; cursor: pointer; - transition: border-color $fp-dur $fp-ease, - transform $fp-dur-fast $fp-ease, - box-shadow $fp-dur $fp-ease; + transition: transform $fp-dur-fast $fp-ease, background-color $fp-dur $fp-ease; - @media (hover: hover) { + @include fp-hover-only { &:hover { - transform: translateY(-2px); - border-color: $fp-border-accent; - box-shadow: $fp-shadow-sm; + transform: translateY(-1px); + background-color: color-mix(in srgb, #{$fp-accent} 6%, $fp-page); } } - - // Accent stripe by tone - &::before { - content: ""; - position: absolute; - left: 0; top: 0; bottom: 0; width: 4px; - } - &[data-tone="success"]::before { background: var(--bs-success); } - &[data-tone="info"]::before { background: var(--bs-info); } - &[data-tone="warning"]::before { background: var(--bs-warning); } - &[data-tone="danger"]::before { background: var(--bs-danger); } - &[data-tone="muted"]::before { background: $fp-border-strong; } } .o_fp_tile_title { - font-weight: 700; font-size: $fp-font-base; - margin-bottom: 4px; padding-left: 6px; + font-weight: $fp-weight-bold; font-size: $fp-text-base; + color: $fp-ink; } - .o_fp_tile_meta { - font-size: $fp-font-sm; color: var(--bs-secondary-color); - margin-bottom: 8px; padding-left: 6px; + .o_fp_tile_meta { + font-size: $fp-text-sm; color: $fp-ink-mute; + margin: $fp-space-1 0 $fp-space-2; } .o_fp_tile_chips { - display: flex; flex-wrap: wrap; gap: 6px; padding-left: 6px; + display: flex; flex-wrap: wrap; gap: $fp-space-1; } - // ========================================================================= - // Status chips - // ========================================================================= + // ------------------------------------------------------------------------- + // Status pills + // ------------------------------------------------------------------------- .o_fp_chip { - display: inline-flex; - align-items: center; - gap: 4px; - padding: 3px 10px; + display: inline-flex; align-items: center; + padding: 2px 10px; border-radius: $fp-radius-pill; - font-size: 0.72rem; - font-weight: 700; + font-size: 0.7rem; + font-weight: $fp-weight-bold; text-transform: uppercase; - letter-spacing: 0.04em; - line-height: 1.4; + letter-spacing: 0.06em; - &.o_fp_chip_info { @include fp-tone(--bs-info); } - &.o_fp_chip_success { @include fp-tone(--bs-success); } - &.o_fp_chip_warning { @include fp-tone(--bs-warning); } - &.o_fp_chip_danger { @include fp-tone(--bs-danger); } - &.o_fp_chip_muted { - background-color: color-mix(in srgb, var(--bs-body-color) 6%, transparent); - color: var(--bs-secondary-color); - border: 1px solid $fp-border; - } + &.o_fp_chip_info { @include fp-pill(--bs-info); } + &.o_fp_chip_success { @include fp-pill(--bs-success); } + &.o_fp_chip_warning { @include fp-pill(--bs-warning); } + &.o_fp_chip_danger { @include fp-pill(--bs-danger); } + &.o_fp_chip_muted { background-color: $fp-card-soft; color: $fp-ink-mute; } } - // ========================================================================= - // Bake / Gate / Hold rows — accent-left banding by state - // ========================================================================= + // ------------------------------------------------------------------------- + // Bake / Gate / Hold rows + // ------------------------------------------------------------------------- .o_fp_bake_list { list-style: none; margin: 0; padding: 0; - display: flex; flex-direction: column; gap: 10px; + display: flex; flex-direction: column; gap: $fp-space-2; } .o_fp_bake_row { - position: relative; display: grid; grid-template-columns: 1fr auto auto; align-items: center; - gap: 12px; - padding: 12px 14px 12px 18px; - border: 1px solid $fp-border; + gap: $fp-space-3; + padding: $fp-space-3 $fp-space-4; border-radius: $fp-radius-md; - background-color: $fp-surface; + background-color: $fp-page; min-height: 64px; - transition: transform $fp-dur-fast $fp-ease, box-shadow $fp-dur $fp-ease; - @media (hover: hover) { &:hover { box-shadow: $fp-shadow-sm; } } - - // Left accent bar by state - &::before { - content: ""; - position: absolute; - top: 8px; bottom: 8px; left: 6px; - width: 4px; border-radius: 4px; - background-color: $fp-border-strong; + // State-coloured left bar (6px wide, rounded) + box-shadow: inset 4px 0 0 0 $fp-ink-faint; + &[data-state="awaiting_bake"], &[data-state="pending"] { + box-shadow: inset 4px 0 0 0 $fp-warn; } - &[data-state="awaiting_bake"]::before, &[data-state="pending"]::before { - background-color: var(--bs-warning); - } - &[data-state="bake_in_progress"]::before, &[data-state="under_review"]::before { - background-color: var(--bs-info); - } - &[data-state="missed_window"]::before, &[data-state="fail"]::before, &[data-state="on_hold"]::before { - background-color: var(--bs-danger); + &[data-state="bake_in_progress"], &[data-state="under_review"] { + box-shadow: inset 4px 0 0 0 $fp-info; } &[data-state="missed_window"], &[data-state="fail"], &[data-state="on_hold"] { - background-color: color-mix(in srgb, var(--bs-danger) 6%, $fp-surface); + box-shadow: inset 4px 0 0 0 $fp-bad; + background-color: fp-wash(--bs-danger, 5%); } - &[data-state="baked"]::before, &[data-state="pass"]::before { - background-color: var(--bs-success); + &[data-state="baked"], &[data-state="pass"] { + box-shadow: inset 4px 0 0 0 $fp-ok; } @media (max-width: 600px) { grid-template-columns: 1fr; - gap: 8px; - .o_fp_bake_time, .o_fp_bake_actions { justify-self: stretch; } .o_fp_bake_actions { - display: flex; gap: 6px; + display: flex; gap: $fp-space-2; .btn { flex: 1; min-height: $fp-touch-min; } } } } - .o_fp_bake_name { font-weight: 700; font-size: $fp-font-base; } - .o_fp_bake_meta { font-size: $fp-font-sm; margin-top: 2px; } + .o_fp_bake_main { min-width: 0; padding-left: $fp-space-2; } + .o_fp_bake_name { font-weight: $fp-weight-semibold; font-size: $fp-text-base; color: $fp-ink; } + .o_fp_bake_meta { font-size: $fp-text-sm; color: $fp-ink-mute; margin-top: 2px; } .o_fp_bake_actions { - display: flex; gap: 6px; + display: flex; gap: $fp-space-2; .btn { - min-height: $fp-touch-min; font-weight: 600; - border-radius: $fp-radius-md; padding: 6px 14px; + min-height: $fp-touch-min; + padding: 0 $fp-space-3; + border-radius: $fp-radius-md; + font-weight: $fp-weight-semibold; + border: none; + font-size: $fp-text-sm; } } - // ========================================================================= + // ------------------------------------------------------------------------- // Footer - // ========================================================================= + // ------------------------------------------------------------------------- .o_fp_tablet_footer { - text-align: right; - padding-top: 10px; - border-top: 1px dashed $fp-border; - color: var(--bs-secondary-color); - font-size: $fp-font-xs; + text-align: center; + padding-top: $fp-space-3; + color: $fp-ink-faint; + font-size: $fp-text-xs; } } - - -// ============================================================================= -// Big QR scan input + action button — shared outside .o_fp_tablet scope too -// ============================================================================= -.o_fp_scan_input { - flex: 1 1 auto; - min-height: 48px; - padding: 10px 16px; - font-size: $fp-font-base; - border: 2px solid var(--bs-border-color); - border-radius: $fp-radius-md; - background-color: var(--bs-body-bg); - color: var(--bs-body-color); - transition: border-color $fp-dur $fp-ease, box-shadow $fp-dur $fp-ease; - - &:focus { @include fp-focus-ring; border-color: var(--o-action); } - &::placeholder { color: var(--bs-secondary-color); } -} - -.o_fp_big_button { - min-height: 48px; - min-width: 110px; - padding: 10px 22px; - font-size: $fp-font-base; - font-weight: 600; - border-radius: $fp-radius-md; - border: 1px solid var(--o-action); - background-color: var(--o-action); - color: var(--o-we-text-on-action, #fff); - cursor: pointer; - transition: filter $fp-dur-fast $fp-ease, transform $fp-dur-fast $fp-ease; - - &:hover:not(:disabled) { filter: brightness(1.06); } - &:active:not(:disabled) { transform: scale(0.97); } - &:disabled { opacity: 0.55; cursor: not-allowed; } -} diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/scss/manager_dashboard.scss b/fusion_plating/fusion_plating_shopfloor/static/src/scss/manager_dashboard.scss index 2132882b..136284a5 100644 --- a/fusion_plating/fusion_plating_shopfloor/static/src/scss/manager_dashboard.scss +++ b/fusion_plating/fusion_plating_shopfloor/static/src/scss/manager_dashboard.scss @@ -1,383 +1,343 @@ // ============================================================================= // Fusion Plating — Manager Desk -// Copyright 2026 Nexa Systems Inc. -// License OPL-1 (Odoo Proprietary License v1.0) +// Copyright 2026 Nexa Systems Inc. · License OPL-1 // -// Shares design tokens + panel / KPI / chip classes from the Tablet Station -// SCSS. Only the manager-specific components live here: hero banner with -// live dot, 3-column workload grid, richer MO cards, gradient avatars. -// -// Variables / mixins come from _fp_shopfloor_tokens.scss — loaded FIRST in -// the asset bundle (see __manifest__.py). No @import; Odoo 19 forbids it. +// Shared tokens from _fp_shopfloor_tokens.scss (loaded first in the bundle). +// Shared components re-used from tablet: .o_fp_panel, .o_fp_empty, .o_fp_chip. +// This file owns only the manager-specific layout. // ============================================================================= -// Touch-device hover suppression +// --- Hover suppression on touch ----------------------------------------------- @media (hover: none) { - .o_fp_manager .o_fp_mgr_card:hover, - .o_fp_manager .o_fp_team_card:hover { - background-color: inherit !important; - border-color: var(--bs-border-color) !important; + .o_fp_manager [class*="o_fp_"]:hover { transform: none !important; + box-shadow: inherit !important; } } .o_fp_manager { - background-color: $fp-surface-sunken; - color: var(--bs-body-color); + font-family: $fp-font-stack; + background-color: $fp-page; + color: $fp-ink; min-height: 100%; - padding: 24px 28px; + padding: $fp-space-6 $fp-space-7; display: flex; flex-direction: column; - gap: 18px; + gap: $fp-space-6; - @media (max-width: 900px) { padding: 16px; gap: 14px; } - @media (max-width: 600px) { padding: 10px 10px 16px; gap: 10px; } + @media (max-width: 900px) { padding: $fp-space-4; gap: $fp-space-4; } + @media (max-width: 600px) { padding: $fp-space-3; gap: $fp-space-4; } - // ========================================================================= - // Hero banner — gradient wash, live dot, action cluster - // ========================================================================= + // ------------------------------------------------------------------------- + // Hero row + // ------------------------------------------------------------------------- .o_fp_manager_header { - position: relative; - overflow: hidden; - border-radius: $fp-radius-lg; - padding: 20px 24px; - background-color: $fp-surface-raised; - border: 1px solid $fp-border; - box-shadow: $fp-shadow-sm; - - &::before { - content: ""; - position: absolute; - inset: 0; - background-image: - radial-gradient(circle at 0% 50%, - color-mix(in srgb, var(--o-action) 22%, transparent) 0%, - transparent 55%), - radial-gradient(circle at 100% 0%, - color-mix(in srgb, var(--bs-info) 18%, transparent) 0%, - transparent 55%); - pointer-events: none; - } - display: grid; grid-template-columns: 1fr auto; - gap: 16px; - align-items: center; - > * { position: relative; z-index: 1; } - + gap: $fp-space-5; + align-items: end; @media (max-width: 600px) { - grid-template-columns: 1fr; - gap: 10px; - padding: 14px; + grid-template-columns: 1fr; gap: $fp-space-3; } } .o_fp_manager_title { - font-size: $fp-font-hero; - font-weight: 700; - letter-spacing: -0.01em; - display: inline-flex; align-items: center; gap: 10px; + font-size: $fp-text-2xl; + font-weight: $fp-weight-bold; + letter-spacing: -0.02em; + line-height: 1.1; + margin: 0; + color: $fp-ink; + display: inline-flex; align-items: center; gap: $fp-space-3; + } + .o_fp_manager_subtitle { + margin-top: $fp-space-2; + font-size: $fp-text-sm; + color: $fp-ink-mute; + display: flex; flex-wrap: wrap; gap: $fp-space-3; align-items: center; } - // Small breathing dot — green at rest, brighter pulse while polling + // Live indicator — calm dot that pulses during a fetch .o_fp_live_dot { - display: inline-block; - width: 11px; height: 11px; border-radius: 50%; - background-color: color-mix(in srgb, var(--bs-success) 75%, transparent); + width: 10px; height: 10px; + border-radius: 50%; + background-color: color-mix(in srgb, #{$fp-ok} 70%, transparent); transition: background-color $fp-dur $fp-ease; &[data-active="y"] { - background-color: var(--bs-success); + background-color: $fp-ok; animation: o_fp_live_pulse 1.1s ease-in-out infinite; } } @keyframes o_fp_live_pulse { - 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--bs-success) 60%, transparent); } - 70% { box-shadow: 0 0 0 10px color-mix(in srgb, var(--bs-success) 0%, transparent); } - 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--bs-success) 0%, transparent); } - } - .o_fp_manager_subtitle { - font-size: $fp-font-sm; - color: var(--bs-secondary-color); - margin-top: 4px; + 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, #{$fp-ok} 55%, transparent); } + 50% { box-shadow: 0 0 0 8px color-mix(in srgb, #{$fp-ok} 0%, transparent); } } + .o_fp_manager_head_actions { - display: flex; gap: 8px; align-items: center; + display: flex; gap: $fp-space-2; .btn { min-height: $fp-touch-min; - padding: 8px 16px; + padding: 0 $fp-space-4; border-radius: $fp-radius-md; - font-weight: 600; + font-weight: $fp-weight-semibold; + border: none; + background-color: $fp-card; + color: $fp-ink; + box-shadow: $fp-elev-1; + transition: transform $fp-dur-fast $fp-ease, box-shadow $fp-dur $fp-ease; + @include fp-hover-only { &:hover { box-shadow: $fp-elev-2; } } + &:active { transform: scale(0.97); } + + &.btn-primary { + background-color: $fp-accent; + color: var(--o-we-text-on-action, #fff); + } } @media (max-width: 600px) { - width: 100%; flex-wrap: wrap; - > .btn { flex: 1; } + width: 100%; > .btn { flex: 1; } } } - // ========================================================================= - // KPI strip — same token system as the tablet - // ========================================================================= - .o_fp_kpi_strip { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); - gap: 12px; - @media (max-width: 600px) { - grid-template-columns: repeat(2, 1fr); - gap: 8px; - } - } - .o_fp_kpi { - position: relative; overflow: hidden; - padding: 18px 18px 16px; - border-radius: $fp-radius-lg; - background-color: $fp-surface-raised; - border: 1px solid $fp-border; - box-shadow: $fp-shadow-sm; - transition: transform $fp-dur $fp-ease, box-shadow $fp-dur $fp-ease; - display: flex; flex-direction: column; gap: 4px; - min-height: 104px; + // ------------------------------------------------------------------------- + // Flash message — reused styling + // ------------------------------------------------------------------------- + .o_fp_tablet_message { + display: flex; align-items: center; gap: $fp-space-3; + padding: $fp-space-3 $fp-space-4; + border-radius: $fp-radius-md; + font-size: $fp-text-base; + font-weight: $fp-weight-medium; + background-color: $fp-card; + box-shadow: $fp-elev-1; + color: $fp-ink; &::before { content: ""; - position: absolute; - top: 0; left: 0; right: 0; height: 3px; - background-image: linear-gradient(90deg, transparent, currentColor, transparent); - opacity: 0.7; + width: 6px; align-self: stretch; + border-radius: 3px; + background-color: $fp-info; + } + &.o_fp_msg_info { &::before { background-color: $fp-info; } } + &.o_fp_msg_success { &::before { background-color: $fp-ok; } } + &.o_fp_msg_warning { &::before { background-color: $fp-warn; } } + &.o_fp_msg_danger { &::before { background-color: $fp-bad; } } + } + + + // ------------------------------------------------------------------------- + // KPI strip — same language as tablet + // ------------------------------------------------------------------------- + .o_fp_kpi_strip { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: $fp-space-4; + @media (max-width: 600px) { grid-template-columns: repeat(2, 1fr); gap: $fp-space-3; } + } + .o_fp_kpi { + position: relative; + padding: $fp-space-5; + border-radius: $fp-radius-lg; + background-color: $fp-card; + box-shadow: $fp-elev-1; + display: flex; flex-direction: column; gap: $fp-space-2; + transition: transform $fp-dur $fp-ease, box-shadow $fp-dur $fp-ease; + + @include fp-hover-only { &:hover { transform: translateY(-2px); box-shadow: $fp-elev-2; } } + + > .fa { font-size: 1.1rem; color: $fp-ink-mute; } + .o_fp_kpi_value { + font-size: $fp-text-3xl; + font-weight: $fp-weight-bold; + line-height: 1; + letter-spacing: -0.03em; + font-variant-numeric: tabular-nums; + color: $fp-ink; + } + .o_fp_kpi_label { + font-size: $fp-text-sm; color: $fp-ink-mute; + font-weight: $fp-weight-medium; } &::after { content: ""; - position: absolute; inset: 0; opacity: 0.35; pointer-events: none; - } - > * { position: relative; z-index: 1; } - - @media (hover: hover) { - &:hover { transform: translateY(-2px); box-shadow: $fp-shadow-md; } - } - - > .fa { position: absolute; - right: 14px; top: 14px; - font-size: 1.5rem; - opacity: 0.28; + top: $fp-space-4; right: $fp-space-4; + width: 10px; height: 10px; + border-radius: 50%; + background-color: $fp-ink-faint; } - .o_fp_kpi_value { - font-size: $fp-font-kpi; - font-weight: 800; - line-height: 1; - letter-spacing: -0.02em; - font-variant-numeric: tabular-nums; - color: var(--bs-body-color); - } - .o_fp_kpi_label { - font-size: $fp-font-xs; - color: var(--bs-secondary-color); - text-transform: uppercase; - letter-spacing: 0.06em; - font-weight: 600; - } - - &.o_fp_kpi_info { color: var(--bs-info); &::after { @include fp-grad(--bs-info); } } - &.o_fp_kpi_success { color: var(--bs-success); &::after { @include fp-grad(--bs-success); } } - &.o_fp_kpi_warning { color: var(--bs-warning); &::after { @include fp-grad(--bs-warning); } } + &.o_fp_kpi_info { &::after { background-color: $fp-info; } } + &.o_fp_kpi_success { &::after { background-color: $fp-ok; } } + &.o_fp_kpi_warning { &::after { background-color: $fp-warn; } } &.o_fp_kpi_danger { - color: var(--bs-danger); - border-color: color-mix(in srgb, var(--bs-danger) 45%, $fp-border); - &::after { @include fp-grad(--bs-danger, 22%, 8%); } - .o_fp_kpi_value { color: var(--bs-danger); } + &::after { background-color: $fp-bad; } + .o_fp_kpi_value { color: $fp-bad; } } - &.o_fp_kpi_muted { color: var(--bs-secondary-color); &::after { opacity: 0; } } + &.o_fp_kpi_muted { &::after { display: none; } } @media (max-width: 600px) { - min-height: 84px; padding: 12px 12px 10px; - .o_fp_kpi_value { font-size: 1.6rem; } - .o_fp_kpi_label { font-size: 0.68rem; } - > .fa { font-size: 1rem; top: 10px; right: 10px; } + padding: $fp-space-4; + .o_fp_kpi_value { font-size: $fp-text-2xl; } + .o_fp_kpi_label { font-size: $fp-text-xs; } } } - // ========================================================================= - // Flash message (shares tablet styling) - // ========================================================================= - .o_fp_tablet_message { - padding: 12px 16px; - border-radius: $fp-radius-md; - font-size: $fp-font-base; - font-weight: 500; - display: flex; align-items: center; gap: 10px; - box-shadow: $fp-shadow-xs; - - &.o_fp_msg_info { @include fp-tone(--bs-info); } - &.o_fp_msg_success { @include fp-tone(--bs-success); } - &.o_fp_msg_warning { @include fp-tone(--bs-warning); } - &.o_fp_msg_danger { @include fp-tone(--bs-danger); } - } - - - // ========================================================================= - // 3-column workload grid - // ========================================================================= + // ------------------------------------------------------------------------- + // Workload grid + // ------------------------------------------------------------------------- .o_fp_manager_grid { display: grid; - grid-template-columns: minmax(0, 1.15fr) minmax(0, 1.15fr) minmax(0, 0.85fr); - gap: 16px; + grid-template-columns: minmax(0, 1.1fr) minmax(0, 1.1fr) minmax(0, 0.85fr); + gap: $fp-space-5; @media (max-width: 1280px) { grid-template-columns: 1fr 1fr; .o_fp_panel_team { grid-column: span 2; } } @media (max-width: 900px) { - grid-template-columns: 1fr; gap: 10px; + grid-template-columns: 1fr; .o_fp_panel_team { grid-column: auto; } } } - // ========================================================================= - // Panels with coloured top accent - // ========================================================================= + // ------------------------------------------------------------------------- + // Panels + // ------------------------------------------------------------------------- .o_fp_panel { - position: relative; - overflow: hidden; - background-color: $fp-surface-raised; - border: 1px solid $fp-border; - border-radius: $fp-radius-lg; - padding: 18px 20px; - box-shadow: $fp-shadow-sm; - @media (max-width: 600px) { padding: 12px 14px; border-radius: $fp-radius-md; } - - &::before { - content: ""; - position: absolute; top: 0; left: 0; right: 0; height: 4px; - background-color: $fp-border-strong; - } + @include fp-card($fp-elev-1); + padding: $fp-space-5; + @media (max-width: 600px) { padding: $fp-space-4; } } - .o_fp_panel_unassigned::before { - background-image: linear-gradient(90deg, - var(--bs-warning), color-mix(in srgb, var(--bs-warning) 50%, transparent)); - } - .o_fp_panel_active::before { - background-image: linear-gradient(90deg, - var(--bs-success), color-mix(in srgb, var(--bs-success) 50%, transparent)); - } - .o_fp_panel_team::before { - background-image: linear-gradient(90deg, - var(--bs-info), color-mix(in srgb, var(--bs-info) 50%, transparent)); - } - .o_fp_panel_head { - display: flex; justify-content: space-between; align-items: center; - margin-bottom: 14px; padding-bottom: 12px; - border-bottom: 1px solid $fp-border; + display: flex; + justify-content: space-between; + align-items: baseline; + margin-bottom: $fp-space-4; h3 { - font-size: $fp-font-lg; - font-weight: 700; - margin: 0; - display: inline-flex; align-items: center; gap: 10px; + font-size: $fp-text-lg; + font-weight: $fp-weight-bold; letter-spacing: -0.01em; + margin: 0; + display: inline-flex; align-items: center; gap: $fp-space-2; + color: $fp-ink; } } .o_fp_panel_count { - min-width: 34px; - padding: 3px 12px; + font-size: $fp-text-sm; + font-weight: $fp-weight-semibold; + color: $fp-ink-mute; + background-color: $fp-card-soft; + padding: 2px 12px; border-radius: $fp-radius-pill; - font-weight: 700; - font-size: $fp-font-sm; - font-variant-numeric: tabular-nums; - background-color: color-mix(in srgb, var(--bs-body-color) 8%, transparent); - color: var(--bs-body-color); - border: 1px solid $fp-border; } + // Panel accent by tone — a single coloured dot next to the title + .o_fp_panel_unassigned .o_fp_panel_head h3::before, + .o_fp_panel_active .o_fp_panel_head h3::before, + .o_fp_panel_team .o_fp_panel_head h3::before { + content: ""; + width: 10px; height: 10px; + border-radius: 50%; + margin-right: $fp-space-1; + } + .o_fp_panel_unassigned .o_fp_panel_head h3::before { background-color: $fp-warn; } + .o_fp_panel_active .o_fp_panel_head h3::before { background-color: $fp-ok; } + .o_fp_panel_team .o_fp_panel_head h3::before { background-color: $fp-info; } + + + // ------------------------------------------------------------------------- + // Empty state + // ------------------------------------------------------------------------- .o_fp_empty { - padding: 32px 16px; + padding: $fp-space-7 $fp-space-4; text-align: center; - color: var(--bs-secondary-color); - i.fa { display: block; font-size: 2.25rem; opacity: 0.55; margin-bottom: 8px; } + color: $fp-ink-mute; + font-size: $fp-text-base; + + i.fa { + display: block; + font-size: 2.75rem; + opacity: 0.5; + margin-bottom: $fp-space-3; + } } - // ========================================================================= - // MO card list (Unassigned + In Progress columns) - // ========================================================================= + // ------------------------------------------------------------------------- + // MO cards — NO borders, depth by shadow + surface tint + // ------------------------------------------------------------------------- .o_fp_mgr_card_list { - display: flex; flex-direction: column; gap: 10px; + display: flex; flex-direction: column; gap: $fp-space-3; } .o_fp_mgr_card { position: relative; - overflow: hidden; - border: 1px solid $fp-border; + background-color: $fp-page; border-radius: $fp-radius-md; - background-color: $fp-surface; - transition: border-color $fp-dur $fp-ease, - box-shadow $fp-dur $fp-ease, - transform $fp-dur-fast $fp-ease; + overflow: hidden; + transition: transform $fp-dur-fast $fp-ease, box-shadow $fp-dur $fp-ease; - @media (hover: hover) { - &:hover { - border-color: $fp-border-accent; - box-shadow: $fp-shadow-sm; - transform: translateY(-1px); - } - } + @include fp-hover-only { &:hover { box-shadow: $fp-elev-1; transform: translateX(2px); } } + // Priority stripe (4px) on the left — only when priority is set &[data-priority="2"] { - border-color: color-mix(in srgb, var(--bs-danger) 50%, $fp-border); + background-color: color-mix(in srgb, #{$fp-bad} 4%, $fp-page); &::before { content: ""; - position: absolute; top: 0; left: 0; bottom: 0; width: 4px; - background: var(--bs-danger); + position: absolute; left: 0; top: 0; bottom: 0; + width: 4px; background-color: $fp-bad; } } - &[data-priority="1"] { - &::before { - content: ""; - position: absolute; top: 0; left: 0; bottom: 0; width: 4px; - background: var(--bs-warning); - } + &[data-priority="1"]::before { + content: ""; + position: absolute; left: 0; top: 0; bottom: 0; + width: 4px; background-color: $fp-warn; } } .o_fp_mgr_card_head { display: flex; justify-content: space-between; align-items: center; - padding: 12px 14px; cursor: pointer; gap: 10px; - min-height: 60px; - @media (max-width: 600px) { flex-wrap: wrap; padding: 12px; } + padding: $fp-space-3 $fp-space-4; + cursor: pointer; + min-height: 64px; + gap: $fp-space-3; + @media (max-width: 600px) { flex-wrap: wrap; } } .o_fp_mgr_card_title { - font-weight: 700; font-size: $fp-font-base; + font-weight: $fp-weight-bold; + font-size: $fp-text-base; + color: $fp-ink; letter-spacing: -0.01em; } .o_fp_mgr_card_sub { - color: var(--bs-secondary-color); font-size: $fp-font-sm; - margin-top: 3px; - } - .o_fp_mgr_card_chips { - display: flex; gap: 6px; flex-wrap: wrap; + font-size: $fp-text-sm; color: $fp-ink-mute; margin-top: 2px; } + .o_fp_mgr_card_chips { display: flex; gap: $fp-space-1; flex-wrap: wrap; } + .o_fp_mgr_card_body { - border-top: 1px dashed $fp-border; - padding: 12px 14px; - display: flex; flex-direction: column; gap: 10px; - background-color: color-mix(in srgb, var(--bs-body-color) 2%, transparent); + padding: $fp-space-3 $fp-space-4 $fp-space-4; + display: flex; flex-direction: column; gap: $fp-space-2; + background-color: color-mix(in srgb, var(--bs-body-color) 3%, transparent); } - // Per-WO row inside the expanded card + + // ------------------------------------------------------------------------- + // WO row inside expanded card + // ------------------------------------------------------------------------- .o_fp_mgr_wo_row { display: grid; grid-template-columns: 1fr auto auto auto auto; - gap: 8px; + gap: $fp-space-2; align-items: center; - padding: 8px 10px; - background-color: $fp-surface; - border: 1px solid $fp-border; + padding: $fp-space-2 $fp-space-3; + background-color: $fp-card; border-radius: $fp-radius-sm; - font-size: $fp-font-sm; + font-size: $fp-text-sm; @media (max-width: 1400px) { grid-template-columns: 1fr auto auto; @@ -386,7 +346,6 @@ } @media (max-width: 600px) { grid-template-columns: 1fr; - gap: 6px; .o_fp_mgr_picker { max-width: 100% !important; width: 100%; } .btn { min-height: $fp-touch-min; } } @@ -394,91 +353,93 @@ .o_fp_mgr_wo_info { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - font-weight: 600; + font-weight: $fp-weight-semibold; + color: $fp-ink; } .o_fp_mgr_picker { - min-width: 130px; max-width: 200px; - min-height: 38px; - padding: 4px 10px; + min-width: 140px; max-width: 220px; + min-height: 40px; + padding: 0 $fp-space-3; + border: none; border-radius: $fp-radius-sm; - background-color: $fp-surface; - border: 1px solid $fp-border; - color: var(--bs-body-color); - font-size: $fp-font-sm; - &:focus { @include fp-focus-ring; border-color: var(--o-action); } + background-color: $fp-card-soft; + color: $fp-ink; + font-size: $fp-text-sm; + &:focus { @include fp-focus-ring; } + } + .o_fp_mgr_wo_row .btn { + min-height: 40px; + padding: 0 $fp-space-3; + border: none; + border-radius: $fp-radius-sm; + font-size: $fp-text-sm; + font-weight: $fp-weight-semibold; + background-color: $fp-card-soft; + color: $fp-ink; + transition: filter $fp-dur-fast $fp-ease; + &:hover { filter: brightness(0.95); } } - // ========================================================================= - // Status chips (shared styles in tablet scss, but redefined in case - // manager is loaded standalone) - // ========================================================================= + // ------------------------------------------------------------------------- + // Status chips (reused) + // ------------------------------------------------------------------------- .o_fp_chip { - display: inline-flex; - align-items: center; - padding: 3px 10px; + display: inline-flex; align-items: center; + padding: 2px 10px; border-radius: $fp-radius-pill; - font-size: 0.72rem; - font-weight: 700; + font-size: 0.7rem; + font-weight: $fp-weight-bold; text-transform: uppercase; - letter-spacing: 0.04em; + letter-spacing: 0.06em; - &.o_fp_chip_info { @include fp-tone(--bs-info); } - &.o_fp_chip_success { @include fp-tone(--bs-success); } - &.o_fp_chip_warning { @include fp-tone(--bs-warning); } - &.o_fp_chip_danger { @include fp-tone(--bs-danger); } - &.o_fp_chip_muted { - background-color: color-mix(in srgb, var(--bs-body-color) 6%, transparent); - color: var(--bs-secondary-color); - border: 1px solid $fp-border; - } + &.o_fp_chip_info { @include fp-pill(--bs-info); } + &.o_fp_chip_success { @include fp-pill(--bs-success); } + &.o_fp_chip_warning { @include fp-pill(--bs-warning); } + &.o_fp_chip_danger { @include fp-pill(--bs-danger); } + &.o_fp_chip_muted { background-color: $fp-card-soft; color: $fp-ink-mute; } } - // ========================================================================= - // Team column — avatar grid - // ========================================================================= + // ------------------------------------------------------------------------- + // Team column — avatar + name + load + // ------------------------------------------------------------------------- .o_fp_team_grid { - display: flex; flex-direction: column; gap: 10px; + display: flex; flex-direction: column; gap: $fp-space-2; } .o_fp_team_card { - position: relative; display: grid; - grid-template-columns: 56px 1fr; + grid-template-columns: 48px 1fr; align-items: center; - gap: 14px; - padding: 12px 14px; - border: 1px solid $fp-border; + gap: $fp-space-3; + padding: $fp-space-3 $fp-space-4; border-radius: $fp-radius-md; - background-color: $fp-surface; + background-color: $fp-page; cursor: pointer; min-height: 72px; - transition: border-color $fp-dur $fp-ease, - box-shadow $fp-dur $fp-ease, - transform $fp-dur-fast $fp-ease; + transition: transform $fp-dur-fast $fp-ease, background-color $fp-dur $fp-ease; - @media (hover: hover) { + @include fp-hover-only { &:hover { - border-color: $fp-border-accent; - box-shadow: $fp-shadow-sm; - transform: translateY(-1px); + background-color: color-mix(in srgb, #{$fp-accent} 6%, $fp-page); + transform: translateX(2px); } } } .o_fp_team_avatar { - width: 48px; height: 48px; + width: 44px; height: 44px; border-radius: 50%; object-fit: cover; - border: 2px solid $fp-border; - background-color: color-mix(in srgb, var(--o-action) 15%, $fp-surface); + background-color: color-mix(in srgb, #{$fp-accent} 12%, $fp-card); } - .o_fp_team_info { flex: 1; min-width: 0; } + .o_fp_team_info { min-width: 0; } .o_fp_team_name { - font-weight: 700; - font-size: $fp-font-base; + font-weight: $fp-weight-semibold; + font-size: $fp-text-base; + color: $fp-ink; letter-spacing: -0.01em; } .o_fp_team_load { - display: flex; gap: 6px; margin-top: 6px; + display: flex; gap: $fp-space-1; margin-top: 4px; } } diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/scss/plant_overview.scss b/fusion_plating/fusion_plating_shopfloor/static/src/scss/plant_overview.scss index 25cef175..5a193e20 100644 --- a/fusion_plating/fusion_plating_shopfloor/static/src/scss/plant_overview.scss +++ b/fusion_plating/fusion_plating_shopfloor/static/src/scss/plant_overview.scss @@ -1,482 +1,260 @@ // ============================================================================= -// Fusion Plating — Plant Overview Dashboard -// Copyright 2026 Nexa Systems Inc. -// License OPL-1 (Odoo Proprietary License v1.0) +// Fusion Plating — Plant Overview (Kanban) +// Copyright 2026 Nexa Systems Inc. · License OPL-1 // -// Modernised 2026-04: gradient column headers, card depth, theme-safe -// using shared design tokens. Variables / mixins come from -// _fp_shopfloor_tokens.scss — loaded FIRST in the asset bundle -// (see __manifest__.py). No @import; Odoo 19 forbids it. +// Kanban of work orders grouped by work centre. Clean, shadow-based, +// no heavy chrome. Shared tokens from _fp_shopfloor_tokens.scss. // ============================================================================= +@media (hover: none) { + .o_fp_plant_overview [class*="o_fp_"]:hover { + transform: none !important; + box-shadow: inherit !important; + } +} + + .o_fp_plant_overview { + font-family: $fp-font-stack; + background-color: $fp-page; + color: $fp-ink; + min-height: 100%; display: flex; flex-direction: column; - height: 100%; - min-height: 0; - background: $fp-surface-sunken; - padding: 0; } -// ---- Header ----------------------------------------------------------------- +// ---------- Header ---------------------------------------------------------- .o_fp_po_header { - position: relative; - overflow: hidden; display: flex; - align-items: center; justify-content: space-between; + align-items: center; flex-wrap: wrap; - gap: 12px; - padding: 20px 24px; - background-color: $fp-surface-raised; - border-bottom: 1px solid $fp-border; - box-shadow: $fp-shadow-xs; - - &::before { - content: ""; - position: absolute; inset: 0; - background-image: - radial-gradient(circle at 0% 50%, - color-mix(in srgb, var(--o-action) 18%, transparent) 0%, - transparent 55%); - pointer-events: none; - } - > * { position: relative; z-index: 1; } + gap: $fp-space-3; + padding: $fp-space-5 $fp-space-6; + background-color: $fp-page; .o_fp_po_header_left { display: flex; align-items: center; } .o_fp_po_title { + font-size: $fp-text-xl; + font-weight: $fp-weight-bold; + letter-spacing: -0.02em; margin: 0; - font-size: $fp-font-xl; - font-weight: 700; - letter-spacing: -0.01em; - color: var(--bs-body-color); + color: $fp-ink; } - .o_fp_po_refresh_ts { font-size: $fp-font-xs; } + .o_fp_po_refresh_ts { + font-size: $fp-text-xs; color: $fp-ink-mute; + } .o_fp_po_header_right { - display: flex; align-items: center; gap: 10px; + display: flex; align-items: center; gap: $fp-space-2; + } + + @media (max-width: 600px) { + padding: $fp-space-4; + flex-direction: column; align-items: stretch; + > * { width: 100%; } } } -// ---- Search ----------------------------------------------------------------- +// ---------- Search ---------------------------------------------------------- .o_fp_po_search_box { position: relative; display: flex; align-items: center; .o_fp_po_search_icon { - position: absolute; - left: 10px; - color: var(--bs-secondary-color); + position: absolute; left: 14px; + color: $fp-ink-mute; pointer-events: none; } - .o_fp_po_search_input { - padding: 6px 32px 6px 32px; - border: 1px solid var(--bs-border-color); - border-radius: 6px; - font-size: 0.875rem; - width: 260px; - outline: none; - transition: border-color 0.15s; - background-color: var(--bs-body-bg); - color: var(--bs-body-color); - - &:focus { - border-color: var(--o-action); - box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--o-action) 15%, transparent); - } - } - - .o_fp_po_search_clear { - position: absolute; - right: 6px; - background: none; + padding: 0 $fp-space-4 0 $fp-space-7; + min-height: $fp-touch-min; border: none; - color: var(--bs-secondary-color); - cursor: pointer; - padding: 2px 6px; + border-radius: $fp-radius-md; + background-color: $fp-card; + color: $fp-ink; + box-shadow: $fp-elev-1; + width: 260px; + font-size: $fp-text-base; + transition: box-shadow $fp-dur $fp-ease; - &:hover { - color: var(--bs-body-color); - } + &:focus { @include fp-focus-ring; } + @media (max-width: 600px) { width: 100%; } + } + .o_fp_po_search_clear { + position: absolute; right: 6px; + background: none; border: none; + color: $fp-ink-mute; padding: $fp-space-1 $fp-space-2; + cursor: pointer; + &:hover { color: $fp-ink; } } } .o_fp_po_refresh_btn { - width: 36px; - height: 36px; - padding: 0; - display: flex; - align-items: center; - justify-content: center; - border-radius: 6px; + width: $fp-touch-min; height: $fp-touch-min; + display: flex; align-items: center; justify-content: center; + border: none; + border-radius: $fp-radius-md; + background-color: $fp-card; + color: $fp-ink; + box-shadow: $fp-elev-1; + cursor: pointer; + transition: transform $fp-dur-fast $fp-ease; + &:active { transform: scale(0.95); } } -// ---- Columns container ------------------------------------------------------ +// ---------- Columns container ----------------------------------------------- .o_fp_po_columns { display: flex; - gap: 12px; - padding: 16px 20px; + gap: $fp-space-4; + padding: 0 $fp-space-6 $fp-space-6; overflow-x: auto; flex: 1; min-height: 0; align-items: flex-start; + + @media (max-width: 900px) { + padding: 0 $fp-space-4 $fp-space-4; + } + @media (max-width: 600px) { + flex-direction: column; + padding: 0 $fp-space-3 $fp-space-3; + } } -// ---- Single column (work centre) -------------------------------------------- +// ---------- Column (work centre lane) --------------------------------------- .o_fp_po_column { - flex: 0 0 280px; - min-width: 260px; - max-width: 320px; + flex: 0 0 300px; + min-width: 280px; + max-width: 340px; display: flex; flex-direction: column; - background-color: $fp-surface-raised; - border: 1px solid $fp-border; + background-color: $fp-card; border-radius: $fp-radius-lg; - box-shadow: $fp-shadow-sm; - max-height: calc(100vh - 160px); + box-shadow: $fp-elev-1; + max-height: calc(100vh - 180px); overflow: hidden; + + @media (max-width: 600px) { + flex: 1 1 auto; + min-width: 100%; max-width: 100%; + max-height: none; + } } .o_fp_po_col_header { - position: relative; - overflow: hidden; - display: flex; - align-items: center; - justify-content: space-between; - padding: 14px 16px; - border-bottom: 1px solid $fp-border; - background-color: $fp-surface-sunken; - - // Subtle gradient stripe at the top — turns the header into a "tab" - &::before { - content: ""; - position: absolute; top: 0; left: 0; right: 0; height: 3px; - background-image: linear-gradient(90deg, - var(--o-action), - color-mix(in srgb, var(--o-action) 30%, transparent)); - } - > * { position: relative; z-index: 1; } + display: flex; align-items: center; justify-content: space-between; + padding: $fp-space-4 $fp-space-4 $fp-space-3; .o_fp_po_col_name { - font-weight: 700; - font-size: $fp-font-sm; - color: var(--bs-body-color); + font-weight: $fp-weight-bold; + font-size: $fp-text-sm; + color: $fp-ink; text-transform: uppercase; - letter-spacing: 0.05em; + letter-spacing: 0.08em; } - .o_fp_po_col_count { - @include fp-tone(--o-action, 14%); - font-weight: 700; - font-size: 0.72rem; - min-width: 26px; + font-weight: $fp-weight-semibold; + font-size: 0.75rem; padding: 2px 10px; border-radius: $fp-radius-pill; + background-color: $fp-card-soft; + color: $fp-ink-mute; } } .o_fp_po_col_body { overflow-y: auto; - padding: 8px; + padding: $fp-space-2 $fp-space-3 $fp-space-3; flex: 1; - transition: background-color 0.15s, border-color 0.15s; - border: 2px solid transparent; - border-radius: 0 0 10px 10px; + transition: background-color $fp-dur $fp-ease; + border-radius: 0 0 $fp-radius-lg $fp-radius-lg; - // Drop target highlight when dragging a card over this column &.o_fp_drop_target { - background-color: color-mix(in srgb, var(--o-action) 8%, transparent); - border-color: color-mix(in srgb, var(--o-action) 40%, transparent); + background-color: color-mix(in srgb, #{$fp-accent} 8%, transparent); } } -// ---- Card ------------------------------------------------------------------- +// ---------- Card ------------------------------------------------------------ .o_fp_po_card { - background-color: $fp-surface; - border: 1px solid $fp-border; + background-color: $fp-page; border-radius: $fp-radius-md; - padding: 12px 14px; - margin-bottom: 10px; + padding: $fp-space-3 $fp-space-4; + margin-bottom: $fp-space-2; cursor: grab; - box-shadow: $fp-shadow-xs; - transition: box-shadow $fp-dur $fp-ease, - transform $fp-dur-fast $fp-ease, + transition: transform $fp-dur-fast $fp-ease, + box-shadow $fp-dur $fp-ease, opacity $fp-dur $fp-ease, - border-color $fp-dur $fp-ease; + background-color $fp-dur $fp-ease; - &:hover { - box-shadow: $fp-shadow-md; - transform: translateY(-2px); - border-color: $fp-border-accent; - border-color: darken($border-color, 10%); + @include fp-hover-only { + &:hover { + background-color: $fp-card; + box-shadow: $fp-elev-2; + transform: translateY(-2px); + } } - &:active { + &:active, &.o_fp_po_dragging { cursor: grabbing; + opacity: 0.6; } - &:last-child { - margin-bottom: 0; + // Priority left bar — only visible when a priority is set + position: relative; overflow: hidden; + &[data-priority="2"] { + background-color: color-mix(in srgb, #{$fp-bad} 6%, $fp-page); + box-shadow: inset 4px 0 0 0 $fp-bad; + padding-left: calc(#{$fp-space-4} + 4px); } - - // Dragging ghost state - &.o_fp_dragging { - opacity: 0.4; - border-style: dashed; - box-shadow: none; - transform: none; + &[data-priority="1"] { + box-shadow: inset 4px 0 0 0 $fp-warn; + padding-left: calc(#{$fp-space-4} + 4px); } - - // State variants - &.o_fp_card_progress { - border-left: 4px solid var(--bs-warning); - } - &.o_fp_card_ready { - border-left: 4px solid var(--bs-primary); - } - &.o_fp_card_done { - border-left: 4px solid var(--bs-success); - opacity: 0.75; - } - &.o_fp_card_pending { - border-left: 4px solid var(--bs-warning); - } -} - -// ---- Card top row (image + title + step badge) -------------------------------- - -.o_fp_po_card_top { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 4px; -} - -.o_fp_po_card_img { - width: 32px; - height: 32px; - border-radius: 4px; - object-fit: cover; - flex-shrink: 0; -} - -.o_fp_po_card_img_placeholder { - width: 32px; - height: 32px; - border-radius: 4px; - background: var(--bs-tertiary-bg); - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; - color: var(--bs-secondary-color); - font-size: 14px; } .o_fp_po_card_title { - flex: 1; - min-width: 0; - font-size: 0.9rem; - color: var(--bs-body-color); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.o_fp_po_card_step_badge { - width: 24px; - height: 24px; - border-radius: 50%; - background: var(--bs-info); - color: #fff; - font-size: 0.7rem; - font-weight: bold; - display: flex; - align-items: center; - justify-content: center; - flex-shrink: 0; -} - -// ---- Priority card borders --------------------------------------------------- - -.o_fp_po_card_hot { - border-left: 4px solid var(--bs-danger) !important; - background: color-mix(in srgb, var(--bs-danger) 8%, var(--bs-body-bg)); -} - -.o_fp_po_card_urgent { - border-left: 4px solid var(--bs-warning) !important; - background: color-mix(in srgb, var(--bs-warning) 8%, var(--bs-body-bg)); -} - -// ---- Product name and step display ------------------------------------------- - -.o_fp_po_card_product { - margin-bottom: 4px; -} - -.o_fp_po_card_step { - margin-bottom: 4px; -} - -.o_fp_po_card_customer { - font-size: 0.9rem; + font-weight: $fp-weight-semibold; + font-size: $fp-text-base; + color: $fp-ink; margin-bottom: 2px; - color: var(--bs-body-color); + letter-spacing: -0.01em; } - -.o_fp_po_card_refs { - font-size: 0.8rem; - color: var(--bs-secondary-color); - margin-bottom: 6px; +.o_fp_po_card_sub { + font-size: $fp-text-sm; + color: $fp-ink-mute; + margin-bottom: $fp-space-2; } - -// ---- Parts progress bar ----------------------------------------------------- - -.o_fp_po_card_parts { - margin-bottom: 6px; -} - -.o_fp_po_parts_bar { - height: 6px; - background: var(--bs-tertiary-bg); - border-radius: 3px; - overflow: hidden; - margin-bottom: 2px; -} - -.o_fp_po_parts_fill { - height: 100%; - background: var(--bs-warning); - border-radius: 3px; - transition: width 0.3s ease; -} - -.o_fp_po_parts_label { - font-size: 0.75rem; - color: var(--bs-secondary-color); -} - -.o_fp_po_card_last { - font-size: 0.75rem; - margin-bottom: 6px; -} - -// ---- Tags + date footer ----------------------------------------------------- - -.o_fp_po_card_footer { - display: flex; - align-items: center; - justify-content: space-between; - gap: 6px; - flex-wrap: wrap; -} - -.o_fp_po_card_tags { - display: flex; - flex-wrap: wrap; - gap: 4px; +.o_fp_po_card_meta { + display: flex; gap: $fp-space-2; flex-wrap: wrap; align-items: center; + font-size: $fp-text-xs; color: $fp-ink-mute; } .o_fp_po_tag { - display: inline-block; - font-size: 0.65rem; - font-weight: 700; + padding: 1px 8px; + border-radius: $fp-radius-pill; + font-size: 0.68rem; + font-weight: $fp-weight-bold; text-transform: uppercase; - letter-spacing: 0.4px; - padding: 2px 6px; - border-radius: 4px; - line-height: 1.4; - - &.o_fp_tag_hot { - background: var(--bs-danger); - color: #fff; - } - &.o_fp_tag_priority { - background: var(--bs-success); - color: #fff; - } - &.o_fp_tag_attention { - background: var(--bs-warning); - color: var(--bs-body-color); - } - &.o_fp_tag_default { - background: var(--bs-tertiary-bg); - color: var(--bs-secondary-color); - } + letter-spacing: 0.06em; + &[data-tone="hot"] { @include fp-pill(--bs-danger); } + &[data-tone="priority"] { @include fp-pill(--bs-warning); } } -.o_fp_po_card_date { - font-size: 0.75rem; - font-weight: 600; - color: var(--bs-secondary-color); - background: var(--bs-tertiary-bg); - padding: 1px 6px; - border-radius: 4px; - white-space: nowrap; -} - -// ---- Empty / no-cards ------------------------------------------------------- - -.o_fp_po_no_cards { - font-size: 0.85rem; -} - -// ---- Responsive ------------------------------------------------------------- - -@media (max-width: 768px) { - .o_fp_po_columns { - flex-direction: column; - align-items: stretch; - padding: 12px; - gap: 10px; - } - - .o_fp_po_column { - flex: 1 1 auto; - min-width: 100%; - max-width: 100%; - max-height: none; - } - - .o_fp_po_search_input { - width: 180px !important; - } - - .o_fp_po_header { - padding: 12px; - flex-wrap: wrap; - gap: 8px; - } -} - -// Phone — further tighten + touch-first cards -@media (max-width: 600px) { - .o_fp_po_columns { padding: 8px; gap: 8px; } - .o_fp_po_col_body { padding: 6px; } - .o_fp_po_card { - padding: 10px 12px; - min-height: 64px; // comfortable tap zone - } - .o_fp_po_search_input { width: 100% !important; } - .o_fp_po_header { - flex-direction: column; - align-items: stretch; - > * { width: 100%; } - } -} - -// Touch devices: disable hover-only highlights (they stick on tap) -@media (hover: none) { - .o_fp_po_card:hover { background: inherit !important; } + +// ---------- Empty state ------------------------------------------------------ +.o_fp_po_empty { + padding: $fp-space-6 $fp-space-3; + text-align: center; + color: $fp-ink-mute; + font-size: $fp-text-sm; } diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/xml/manager_dashboard.xml b/fusion_plating/fusion_plating_shopfloor/static/src/xml/manager_dashboard.xml index 993a8177..7e0cecc1 100644 --- a/fusion_plating/fusion_plating_shopfloor/static/src/xml/manager_dashboard.xml +++ b/fusion_plating/fusion_plating_shopfloor/static/src/xml/manager_dashboard.xml @@ -1,60 +1,58 @@
- -
+ +
-
- Manager Desk - + Manager Desk + -
+
- · live · updated + · Live · updated
- -
-
+ - +
- + - +
- +
- +
@@ -78,13 +76,13 @@
- +
- +
-

Needs a Worker

+

Needs a Worker

@@ -94,46 +92,41 @@
-
+ t-att-data-priority="card.priority_any"> +
- - · + + ·
· · Qty - - · - + ·
- HOT - Urgent + HOT + Urgent WO
-
- + ·
- - - - @@ -169,10 +162,10 @@
- +
-

In Progress

+

In Progress

@@ -182,13 +175,13 @@
-
+ t-att-data-priority="card.priority_any"> +
- - · + + ·
@@ -196,8 +189,7 @@
- HOT + HOT WO @@ -208,7 +200,7 @@
- + @@ -220,11 +212,11 @@ - - @@ -236,10 +228,10 @@
- +
-

Team

+

Team

@@ -268,8 +260,10 @@
+
- Loading manager data… + +
Loading manager data…
diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/xml/shopfloor_tablet.xml b/fusion_plating/fusion_plating_shopfloor/static/src/xml/shopfloor_tablet.xml index 4cf97e61..7ed7286e 100644 --- a/fusion_plating/fusion_plating_shopfloor/static/src/xml/shopfloor_tablet.xml +++ b/fusion_plating/fusion_plating_shopfloor/static/src/xml/shopfloor_tablet.xml @@ -1,38 +1,34 @@
- -
+ +
-
- Shop Floor Tablet -
+

+ + Tablet Station +

- - - - - - - — - - - + + + + + + · + +
- -
-
+ - +
@@ -66,13 +61,13 @@
- +
- +
@@ -83,45 +78,39 @@
- +
- - Active Work Order: + Active:
MO · · Qty / - - @ - + @
-
- +
- +
-

My Queue

- - - +

My Queue

+
-
+
All caught up — nothing waiting on you.
@@ -140,30 +129,27 @@
-
- +
-
-

Baths

+

Baths

@@ -173,12 +159,9 @@
-
- Tank -
+
Tank
@@ -187,19 +170,15 @@ t-att-class="'o_fp_chip o_fp_chip_' + stateBadge(b.last_log_status)"> log: - - MTO -
-
-

Bake Windows

+

Bake Windows

@@ -211,10 +190,10 @@
  • - - + +
    -
    +
    · Qty · Lot @@ -227,16 +206,16 @@
    - @@ -246,10 +225,9 @@
  • -
    -

    First-Piece Gates

    +

    First-Piece Gates

    @@ -261,10 +239,10 @@
  • - - + +
    -
    +
    · Bath
    @@ -276,16 +254,16 @@
    - @@ -295,10 +273,9 @@
  • -
    -

    Quality Holds

    +

    Quality Holds

      @@ -306,17 +283,18 @@
    • - - + +
      -
      +
      Qty · ·
      +
      - @@ -328,17 +306,15 @@
      - +
      - Loading shop-floor data… + +
      Loading shop-floor data…
      - -