This commit is contained in:
gsinghpal
2026-04-25 05:21:15 -04:00
parent 7f84e66b72
commit 22573e7ce3
6 changed files with 335 additions and 76 deletions

View File

@@ -0,0 +1,234 @@
// =============================================================================
// Fusion Plating — Job model design system (v1, 2026-04)
// File: fusion_plating_jobs/static/src/scss/_fp_jobs_tokens.scss
// Copyright 2026 Nexa Systems Inc. · License OPL-1
//
// Parallels fusion_plating_shopfloor/static/src/scss/_fp_shopfloor_tokens.scss
// — same spacing scale, same radius scale, same compile-time dark-mode
// branching pattern. Lives in fusion_plating_jobs so the new client-action
// SCSS files (job_process_tree, job_plant_overview, job_manager_dashboard,
// job_tablet) can reference these tokens without taking a hard SCSS-level
// dependency on the shopfloor module's bundle ordering.
//
// Design philosophy (same as shopfloor):
// * Three-layer contrast: page (grayest) → container (mid) → card
// (brightest) — that's what makes cards pop in BOTH themes.
// * Every value resolves from compile-time SCSS variables that branch
// on $o-webclient-color-scheme, so light and dark themes get distinct
// palettes without runtime CSS custom-property toggling (which Odoo
// 19 does NOT do for surface colours).
// * Semantic state colours (success/warning/danger/info) reserved for
// STATUS — not decoration.
// =============================================================================
// ---------- 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;
// ---------- Radius -----------------------------------------------------------
$fp-radius-sm : 10px;
$fp-radius-md : 14px;
$fp-radius-lg : 20px;
$fp-radius-xl : 28px;
$fp-radius-pill: 999px;
// ---------- Surfaces — COMPILE-TIME branch on Odoo's dark scheme -------------
//
// Odoo 19 compiles TWO asset bundles: web.assets_backend (light) and
// web.assets_web_dark (dark). The two bundles differ only in the value
// of the SCSS variable $o-webclient-color-scheme — `bright` for light,
// `dark` for dark (defined in primary_variables.scss /
// primary_variables.dark.scss in web_enterprise).
//
// Odoo does NOT redefine --bs-body-bg / --bs-card-bg as CSS custom
// properties at runtime. It bakes the chosen palette into the bundle
// at compile time via Bootstrap SCSS variables. So our tokens must do
// the same: branch on $o-webclient-color-scheme at compile time and
// emit the right hex values into each bundle.
$o-webclient-color-scheme: bright !default;
// Default (light / bright) palette
$_fp-page-hex : #f3f4f6;
$_fp-card-hex : #ffffff;
$_fp-card-soft-hex : #f1f3f5;
$_fp-border-hex : #d8dadd;
$_fp-border-strong-hex : #b6babf;
$_fp-ink-hex : #1f2937;
$_fp-ink-soft-hex : #4b5563;
$_fp-ink-mute-hex : #6b7280;
$_fp-ink-faint-hex : #9ca3af;
// Dark palette — engaged when the dark bundle is compiled
@if $o-webclient-color-scheme == dark {
$_fp-page-hex : #1a1d21 !global;
$_fp-card-hex : #22262d !global;
$_fp-card-soft-hex : #1c2027 !global;
$_fp-border-hex : #343942 !global;
$_fp-border-strong-hex : #4a505a !global;
$_fp-ink-hex : #e5e7eb !global;
$_fp-ink-soft-hex : #c8ccd2 !global;
$_fp-ink-mute-hex : #8a909a !global;
$_fp-ink-faint-hex : #5a606b !global;
}
// Public tokens — CSS custom property fallback chain remains so a
// deployment can still override via --fp-* without touching SCSS.
$fp-page : var(--fp-page-bg, $_fp-page-hex);
$fp-card : var(--fp-card-bg, $_fp-card-hex);
$fp-card-soft : var(--fp-card-soft-bg, $_fp-card-soft-hex);
$fp-border : var(--fp-border-color, $_fp-border-hex);
$fp-border-strong : var(--fp-border-strong, $_fp-border-strong-hex);
$fp-ink : var(--fp-ink, $_fp-ink-hex);
$fp-ink-soft : var(--fp-ink-soft, $_fp-ink-soft-hex);
$fp-ink-mute : var(--fp-ink-mute, $_fp-ink-mute-hex);
$fp-ink-faint : var(--fp-ink-faint, $_fp-ink-faint-hex);
// Action colour — Odoo's primary. Same in both bundles (brand purple).
$fp-accent : var(--o-action, #714B67);
// ---------- Elevation — explicit rgba shadows --------------------------------
// Explicit rgba values (not color-mix) so they render identically across
// browsers and themes. In dark mode the shadows still work against the
// darker surfaces because they're translucent.
$fp-elev-1 : 0 1px 2px rgba(0, 0, 0, 0.06),
0 1px 3px rgba(0, 0, 0, 0.08);
$fp-elev-2 : 0 2px 4px rgba(0, 0, 0, 0.06),
0 6px 14px rgba(0, 0, 0, 0.10);
$fp-elev-3 : 0 4px 8px rgba(0, 0, 0, 0.10),
0 12px 28px rgba(0, 0, 0, 0.14);
$fp-elev-hover : 0 6px 12px rgba(0, 0, 0, 0.12),
0 18px 36px rgba(0, 0, 0, 0.16);
// ---------- Semantic colour helpers ------------------------------------------
$fp-ok : var(--bs-success, #28a745);
$fp-warn : var(--bs-warning, #ffc107);
$fp-bad : var(--bs-danger, #dc3545);
$fp-info : var(--bs-info, #17a2b8);
// State-colour hexes (used directly for badges / borders / chips so the
// rendering doesn't depend on Bootstrap variable presence). Different
// hexes per scheme keep contrast crisp on both backgrounds.
$_fp-state-ready-hex : #ffc107;
$_fp-state-ready-text-hex : #b58105;
$_fp-state-progress-hex : #0d6efd;
$_fp-state-progress-text-hex : #084298;
$_fp-state-paused-hex : #fd7e14;
$_fp-state-paused-text-hex : #97480d;
$_fp-state-done-hex : #198754;
$_fp-state-done-text-hex : #0f5132;
$_fp-state-cancel-hex : #dc3545;
$_fp-state-cancel-text-hex : #842029;
$_fp-state-rush-hex : #dc3545;
$_fp-state-high-hex : #fd7e14;
$_fp-state-low-hex : #6c757d;
$_fp-state-pending-bg-hex : #e9ecef;
$_fp-state-pending-text-hex : #6c757d;
@if $o-webclient-color-scheme == dark {
// Slightly brighter / desaturated for legibility against the dark
// card surface ($_fp-card-hex = #22262d).
$_fp-state-ready-hex : #ffd866 !global;
$_fp-state-ready-text-hex : #ffd866 !global;
$_fp-state-progress-hex : #6ea8fe !global;
$_fp-state-progress-text-hex : #6ea8fe !global;
$_fp-state-paused-hex : #ffb86b !global;
$_fp-state-paused-text-hex : #ffb86b !global;
$_fp-state-done-hex : #75d4a4 !global;
$_fp-state-done-text-hex : #75d4a4 !global;
$_fp-state-cancel-hex : #f1aeb5 !global;
$_fp-state-cancel-text-hex : #f1aeb5 !global;
$_fp-state-rush-hex : #e85d6c !global;
$_fp-state-high-hex : #ff9a4d !global;
$_fp-state-low-hex : #8a909a !global;
$_fp-state-pending-bg-hex : #2a2f37 !global;
$_fp-state-pending-text-hex : #c8ccd2 !global;
}
$fp-state-ready : $_fp-state-ready-hex;
$fp-state-ready-text : $_fp-state-ready-text-hex;
$fp-state-progress : $_fp-state-progress-hex;
$fp-state-progress-text : $_fp-state-progress-text-hex;
$fp-state-paused : $_fp-state-paused-hex;
$fp-state-paused-text : $_fp-state-paused-text-hex;
$fp-state-done : $_fp-state-done-hex;
$fp-state-done-text : $_fp-state-done-text-hex;
$fp-state-cancel : $_fp-state-cancel-hex;
$fp-state-cancel-text : $_fp-state-cancel-text-hex;
$fp-state-rush : $_fp-state-rush-hex;
$fp-state-high : $_fp-state-high-hex;
$fp-state-low : $_fp-state-low-hex;
$fp-state-pending-bg : $_fp-state-pending-bg-hex;
$fp-state-pending-text : $_fp-state-pending-text-hex;
// Softened backgrounds for status pills / banners
@function fp-wash($color-var, $strength: 12%) {
@return color-mix(in srgb, var(#{$color-var}) #{$strength}, transparent);
}
// ---------- Type scale ------------------------------------------------------
$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;
$fp-font-stack : -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
"Inter", "Helvetica Neue", Arial, sans-serif;
// ---------- Motion -----------------------------------------------------------
$fp-ease : cubic-bezier(0.22, 1, 0.36, 1);
$fp-ease-out : cubic-bezier(0.33, 1, 0.68, 1);
$fp-dur-fast : 120ms;
$fp-dur : 200ms;
$fp-dur-slow : 360ms;
// ---------- Touch ------------------------------------------------------------
$fp-touch-min : 48px; // larger than Apple's 44px minimum — shop floor
// =============================================================================
// Mixins
// =============================================================================
// Focus ring — used on all interactive inputs/buttons
@mixin fp-focus-ring {
outline: none;
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;
}
// 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;
}
}

View File

@@ -3,77 +3,89 @@
// Copyright 2026 Nexa Systems Inc. · License OPL-1
//
// Class prefix: .o_fp_jpo_* (Job Plant Overview)
// Self-contained — no shopfloor token partial dependency.
//
// Theme-aware: every surface, border and text colour resolves through
// the design tokens defined in _fp_jobs_tokens.scss, which compile-time
// branch on $o-webclient-color-scheme so light and dark bundles get the
// right palette. NO hardcoded hex on theme-sensitive surfaces.
//
// Three-layer contrast:
// page = $fp-page (grayest)
// columns = $fp-card-soft (mid)
// cards = $fp-card (brightest)
// =============================================================================
.o_fp_job_plant_overview {
height: 100%;
display: flex;
flex-direction: column;
padding: 16px 24px;
gap: 16px;
background-color: var(--o-action, #f7f7f8);
color: var(--bs-body-color, #1a1d21);
padding: $fp-space-4 $fp-space-6;
gap: $fp-space-4;
background-color: $fp-page;
color: $fp-ink;
overflow: hidden;
@media (max-width: 600px) { padding: 12px; gap: 12px; }
@media (max-width: 600px) { padding: $fp-space-3; gap: $fp-space-3; }
// -------------------------------------------------------------------------
// Header strip
// Header strip — sits on the page, surfaced as a card layer
// -------------------------------------------------------------------------
.o_fp_jpo_header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
gap: $fp-space-4;
flex-wrap: wrap;
padding: 12px 16px;
background-color: var(--bs-body-bg, #ffffff);
border: 1px solid #d8dadd;
border-radius: 8px;
padding: $fp-space-3 $fp-space-4;
background-color: $fp-card;
border: 1px solid #{$fp-border};
border-radius: $fp-radius-md;
box-shadow: $fp-elev-1;
}
.o_fp_jpo_header_left {
display: flex;
align-items: baseline;
gap: 12px;
gap: $fp-space-3;
}
.o_fp_jpo_title {
font-size: 1.1rem;
font-weight: 700;
font-size: $fp-text-md;
font-weight: $fp-weight-bold;
margin: 0;
color: $fp-ink;
}
.o_fp_jpo_header_right {
display: flex;
align-items: center;
gap: 8px;
gap: $fp-space-2;
}
.o_fp_jpo_search_box {
display: inline-flex;
align-items: center;
background-color: var(--bs-tertiary-bg, #f1f3f5);
border: 1px solid #d8dadd;
border-radius: 999px;
background-color: $fp-card-soft;
border: 1px solid #{$fp-border};
border-radius: $fp-radius-pill;
padding: 4px 10px;
gap: 6px;
gap: $fp-space-2;
min-width: 240px;
}
.o_fp_jpo_search_icon { opacity: 0.6; }
.o_fp_jpo_search_icon { color: $fp-ink-mute; }
.o_fp_jpo_search_input {
border: none;
background: transparent;
outline: none;
font-size: 0.875rem;
font-size: $fp-text-sm;
flex: 1;
color: inherit;
color: $fp-ink;
&::placeholder { color: $fp-ink-faint; }
}
.o_fp_jpo_search_clear {
border: none;
background: transparent;
opacity: 0.55;
color: $fp-ink-mute;
padding: 0 2px;
cursor: pointer;
&:hover { opacity: 0.9; }
&:hover { color: $fp-ink; }
}
@@ -82,9 +94,11 @@
// -------------------------------------------------------------------------
.o_fp_jpo_empty,
.o_fp_jpo_loading {
background-color: var(--bs-body-bg, #ffffff);
border: 1px solid #d8dadd;
border-radius: 8px;
background-color: $fp-card;
color: $fp-ink-mute;
border: 1px solid #{$fp-border};
border-radius: $fp-radius-md;
box-shadow: $fp-elev-1;
}
@@ -93,7 +107,7 @@
// -------------------------------------------------------------------------
.o_fp_jpo_columns {
display: flex;
gap: 12px;
gap: $fp-space-3;
overflow-x: auto;
flex: 1 1 auto;
align-items: stretch;
@@ -103,9 +117,9 @@
flex: 0 0 280px;
display: flex;
flex-direction: column;
background-color: var(--bs-tertiary-bg, #eef0f2);
border: 1px solid #d8dadd;
border-radius: 8px;
background-color: $fp-card-soft;
border: 1px solid #{$fp-border};
border-radius: $fp-radius-md;
max-height: 100%;
overflow: hidden;
}
@@ -113,21 +127,23 @@
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
gap: $fp-space-2;
padding: 10px 12px 4px;
font-weight: 700;
font-weight: $fp-weight-bold;
font-size: 0.95rem;
color: $fp-ink;
}
.o_fp_jpo_col_subhead {
padding: 0 12px 6px;
opacity: 0.7;
color: $fp-ink-mute;
}
.o_fp_jpo_col_count {
background-color: rgba(0, 0, 0, 0.08);
color: inherit;
font-weight: 600;
background-color: color-mix(in srgb, #{$fp-ink} 8%, transparent);
color: $fp-ink-soft;
font-weight: $fp-weight-semibold;
font-size: 0.7rem;
padding: 2px 8px;
border-radius: $fp-radius-pill;
}
.o_fp_jpo_col_body {
flex: 1 1 auto;
@@ -135,40 +151,46 @@
padding: 6px 8px 10px;
display: flex;
flex-direction: column;
gap: 8px;
gap: $fp-space-2;
&.o_fp_drop_target {
background-color: rgba(13, 110, 253, 0.08);
background-color: color-mix(in srgb, #{$fp-accent} 8%, transparent);
}
}
// -------------------------------------------------------------------------
// Cards
// Cards (brightest layer)
// -------------------------------------------------------------------------
.o_fp_jpo_card {
background-color: var(--bs-body-bg, #ffffff);
border: 1px solid #d8dadd;
border-radius: 6px;
padding: 8px 10px;
background-color: $fp-card;
border: 1px solid #{$fp-border};
border-radius: $fp-radius-sm;
padding: $fp-space-2 10px;
display: flex;
flex-direction: column;
gap: 4px;
cursor: grab;
transition: transform 0.1s ease, box-shadow 0.15s ease;
color: $fp-ink;
box-shadow: $fp-elev-1;
transition: transform $fp-dur-fast $fp-ease,
box-shadow $fp-dur $fp-ease,
border-color $fp-dur $fp-ease;
&:hover {
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
border-color: #c5c8cc;
@include fp-hover-only {
&:hover {
transform: translateY(-1px);
box-shadow: $fp-elev-2;
border-color: $fp-border-strong;
}
}
&:active { cursor: grabbing; }
// ---- State accents (left border) --------------------------------
&.o_fp_jpo_card_progress { border-left: 3px solid #0d6efd; }
&.o_fp_jpo_card_ready { border-left: 3px solid #ffc107; }
&.o_fp_jpo_card_paused { border-left: 3px solid #fd7e14; }
&.o_fp_jpo_card_done { border-left: 3px solid #198754; opacity: 0.75; }
&.o_fp_jpo_card_progress { border-left: 3px solid $fp-state-progress; }
&.o_fp_jpo_card_ready { border-left: 3px solid $fp-state-ready; }
&.o_fp_jpo_card_paused { border-left: 3px solid $fp-state-paused; }
&.o_fp_jpo_card_done { border-left: 3px solid $fp-state-done; opacity: 0.75; }
// ---- Priority overlay -------------------------------------------
&.o_fp_jpo_card_rush {
@@ -185,33 +207,35 @@
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 6px;
gap: $fp-space-2;
}
.o_fp_jpo_card_title {
flex: 1 1 auto;
font-size: 0.9rem;
line-height: 1.25;
word-break: break-word;
color: $fp-ink;
}
.o_fp_jpo_card_refs {
font-size: 0.8rem;
color: $fp-ink-soft;
}
.o_fp_jpo_job_link {
color: var(--bs-link-color, #0d6efd);
color: $fp-accent;
cursor: pointer;
text-decoration: none;
&:hover { text-decoration: underline; }
}
.o_fp_jpo_card_meta {
font-size: 0.72rem;
opacity: 0.85;
color: $fp-ink-mute;
display: flex;
flex-wrap: wrap;
gap: 2px 4px;
}
.o_fp_jpo_card_footer {
display: flex;
gap: 6px;
gap: $fp-space-2;
margin-top: 2px;
}
@@ -223,21 +247,21 @@
display: inline-flex;
align-items: center;
padding: 1px 7px;
border-radius: 999px;
border-radius: $fp-radius-pill;
font-size: 0.65rem;
font-weight: 700;
font-weight: $fp-weight-bold;
line-height: 1.4;
white-space: nowrap;
text-transform: uppercase;
letter-spacing: 0.02em;
&.o_fp_jpo_state_badge_pending { background-color: #e9ecef; color: #6c757d; }
&.o_fp_jpo_state_badge_ready { background-color: rgba(255, 193, 7, 0.18); color: #b58105; }
&.o_fp_jpo_state_badge_in_progress { background-color: rgba(13, 110, 253, 0.18); color: #084298; }
&.o_fp_jpo_state_badge_paused { background-color: rgba(253, 126, 20, 0.20); color: #97480d; }
&.o_fp_jpo_state_badge_done { background-color: rgba(25, 135, 84, 0.20); color: #0f5132; }
&.o_fp_jpo_state_badge_skipped { background-color: #e9ecef; color: #6c757d; }
&.o_fp_jpo_state_badge_cancelled { background-color: rgba(220, 53, 69, 0.18); color: #842029; }
&.o_fp_jpo_state_badge_pending { background-color: $fp-state-pending-bg; color: $fp-state-pending-text; }
&.o_fp_jpo_state_badge_ready { background-color: color-mix(in srgb, #{$fp-state-ready} 18%, transparent); color: $fp-state-ready-text; }
&.o_fp_jpo_state_badge_in_progress { background-color: color-mix(in srgb, #{$fp-state-progress} 18%, transparent); color: $fp-state-progress-text; }
&.o_fp_jpo_state_badge_paused { background-color: color-mix(in srgb, #{$fp-state-paused} 20%, transparent); color: $fp-state-paused-text; }
&.o_fp_jpo_state_badge_done { background-color: color-mix(in srgb, #{$fp-state-done} 20%, transparent); color: $fp-state-done-text; }
&.o_fp_jpo_state_badge_skipped { background-color: $fp-state-pending-bg; color: $fp-state-pending-text; }
&.o_fp_jpo_state_badge_cancelled { background-color: color-mix(in srgb, #{$fp-state-cancel} 18%, transparent); color: $fp-state-cancel-text; }
}
@@ -248,16 +272,17 @@
display: inline-flex;
align-items: center;
padding: 1px 8px;
border-radius: 999px;
border-radius: $fp-radius-pill;
font-size: 0.65rem;
font-weight: 700;
font-weight: $fp-weight-bold;
line-height: 1.5;
text-transform: uppercase;
letter-spacing: 0.02em;
color: #ffffff;
&.o_fp_jpo_chip_rush { background-color: #dc3545; color: #fff; }
&.o_fp_jpo_chip_high { background-color: #fd7e14; color: #fff; }
&.o_fp_jpo_chip_low { background-color: #6c757d; color: #fff; }
&.o_fp_jpo_chip_rush { background-color: $fp-state-rush; }
&.o_fp_jpo_chip_high { background-color: $fp-state-high; }
&.o_fp_jpo_chip_low { background-color: $fp-state-low; }
}
@@ -269,9 +294,9 @@
}
.o_fp_jpo_drop_placeholder {
height: 56px;
border: 2px dashed #0d6efd;
border-radius: 6px;
background-color: rgba(13, 110, 253, 0.08);
border: 2px dashed $fp-accent;
border-radius: $fp-radius-sm;
background-color: color-mix(in srgb, #{$fp-accent} 8%, transparent);
margin: 0;
}
@@ -280,7 +305,7 @@
// No-cards filler
// -------------------------------------------------------------------------
.o_fp_jpo_no_cards {
opacity: 0.6;
color: $fp-ink-mute;
font-size: 0.8rem;
}
}