feat(configurator): Express form CSS-Grid rebuild — match mockup pixel layout
Previous rebuild used Odoo's <group col='4'> which renders as an HTML table — colspan+nesting broke into a vertical stack. Replaced entirely with raw <div> + CSS Grid (display: grid; grid-template-columns: repeat(4, 1fr)) so the header layout matches the mockup exactly: - Row 1: Customer (span 2) + Shipping Address (span 2) - Row 2: PO block (span 2, accent-bordered card with PO#/PDF/Pending toggle/Expected date stacked + chase warning) + Customer Job # + Job Sorting - Row 3: Material/Process Tag + Lead Time (inline X to Y) + Payment Terms + Delivery Method - Row 4: Blanket SO + Currency/Pricelist + Quote Validity + Invoice Strategy Footer also rebuilt as CSS Grid (1fr 320px) — Notes/Terms cards stacked in the left column, Totals card with Grand Total + currency pill in the right column. Each card has a title + subtitle + body matching the mockup's card chrome. SCSS overrides Odoo's default field chrome inside .o_fp_xpr_cell so inputs render with the mockup's underline style (no Bootstrap form- control border, just a 1px bottom-border that thickens on focus).
This commit is contained in:
@@ -1,91 +1,199 @@
|
||||
// Express Orders — main stylesheet (v1.5 / 2026-05-26)
|
||||
//
|
||||
// Tokens load FIRST via the manifest's web.assets_backend ordering;
|
||||
// $xpr-* are CSS custom-property wrappers from _express_tokens.scss.
|
||||
//
|
||||
// Goals (matching the .claude/mockups/express_orders.html mockup):
|
||||
// - Header reads as a single 4-col grid (not stacked groups)
|
||||
// - PO block reads as a single consolidated card-within-card
|
||||
// - Lines feel like a spreadsheet (tight borders, bordered cells)
|
||||
// - Bake column reads as a coloured pill, "no bake" reads italic muted
|
||||
// - Customer Line Job # reads bold + uppercase
|
||||
// - Footer is side-by-side Notes/Terms left + Totals right
|
||||
// - Grand Total shows a prominent currency pill
|
||||
// Express Orders — styles for the CSS Grid rebuild
|
||||
// (matches .claude/mockups/express_orders.html line-for-line where Odoo allows)
|
||||
|
||||
.o_fp_express_order {
|
||||
.o_fp_xpr {
|
||||
|
||||
// ============================================================
|
||||
// Header — 4-col grid (.o_fp_express_header)
|
||||
// Title pill
|
||||
// ============================================================
|
||||
.o_fp_express_header {
|
||||
// Group renders as a table by default. Tighten spacing and let
|
||||
// the colspan-2 spans on customer + shipping breathe.
|
||||
td.o_td_label > label { font-weight: 600; font-size: 12px; }
|
||||
.o_fp_xpr_pill {
|
||||
background: $xpr-accent-bg;
|
||||
color: $xpr-accent;
|
||||
padding: 2px 10px;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PO Block — consolidated inside the header (.o_fp_po_block)
|
||||
// HEADER GRID — 4-column CSS Grid (mirrors mockup .header-grid)
|
||||
// ============================================================
|
||||
.o_fp_po_block {
|
||||
.o_fp_xpr_grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 14px 24px;
|
||||
align-items: start;
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
// Each grid cell — label on top, field below
|
||||
.o_fp_xpr_cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
min-width: 0; // allow Many2One to shrink within grid track
|
||||
|
||||
> label {
|
||||
font-size: 11px;
|
||||
color: $xpr-text-muted;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
// The actual field input — kill Odoo's default block-level chrome
|
||||
> .o_field_widget,
|
||||
> .o_field_widget > div,
|
||||
> .o_field_widget input,
|
||||
> .o_field_widget select,
|
||||
> .o_field_many2one,
|
||||
> .o_field_char,
|
||||
> .o_field_date {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// Field input visual — underline style like the mockup
|
||||
.o_input, .o_field_widget input {
|
||||
border: none;
|
||||
border-bottom: 1px solid $xpr-border-strong;
|
||||
background: transparent;
|
||||
padding: 4px 0;
|
||||
border-radius: 0;
|
||||
font-size: 14px;
|
||||
color: $xpr-text;
|
||||
|
||||
&:focus {
|
||||
border-bottom-color: $xpr-accent;
|
||||
border-bottom-width: 2px;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_fp_xpr_cell.span-2 { grid-column: span 2; }
|
||||
.o_fp_xpr_cell.required > label::after {
|
||||
content: " *";
|
||||
color: $xpr-bad;
|
||||
}
|
||||
|
||||
// Lead Time range — inline X to Y
|
||||
.o_fp_xpr_range {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
}
|
||||
.o_fp_xpr_range .o_field_widget,
|
||||
.o_fp_xpr_range input {
|
||||
width: 3.5em !important;
|
||||
text-align: center;
|
||||
}
|
||||
.o_fp_xpr_range_sep {
|
||||
color: $xpr-text-dim;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PO BLOCK — consolidated card-within-card
|
||||
// ============================================================
|
||||
.o_fp_xpr_po_block {
|
||||
background: $xpr-accent-bg;
|
||||
border: 1px solid $xpr-accent;
|
||||
border: 1px solid lighten(#d8b4d4, 5%);
|
||||
border-left: 4px solid $xpr-accent;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
margin: 4px 0;
|
||||
padding: 10px 14px;
|
||||
gap: 6px;
|
||||
|
||||
.o_horizontal_separator {
|
||||
.o_fp_xpr_po_head {
|
||||
color: $xpr-accent;
|
||||
font-weight: 700;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
border-top: none;
|
||||
padding-top: 2px;
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
}
|
||||
.o_fp_xpr_po_row {
|
||||
display: grid;
|
||||
grid-template-columns: 130px 1fr;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 2px 0;
|
||||
|
||||
// ============================================================
|
||||
// Lines list — spreadsheet feel (.o_fp_express_lines)
|
||||
// ============================================================
|
||||
.o_fp_express_lines .o_list_view {
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid $xpr-border-table;
|
||||
> label {
|
||||
font-size: 12px;
|
||||
color: $xpr-text;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
text-transform: none;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
input, .o_field_widget input {
|
||||
border: none;
|
||||
border-bottom: 1px solid $xpr-border-strong;
|
||||
background: transparent;
|
||||
padding: 2px 0;
|
||||
font-size: 13px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
thead th {
|
||||
background: $xpr-table-head;
|
||||
color: $xpr-text-muted;
|
||||
.o_fp_xpr_po_chase {
|
||||
color: $xpr-bake-text;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
font-weight: 600;
|
||||
border-bottom: 2px solid $xpr-border-table;
|
||||
border-right: 1px solid $xpr-border-table;
|
||||
padding: 6px 8px;
|
||||
|
||||
&:last-child { border-right: none; }
|
||||
}
|
||||
tbody td {
|
||||
border-bottom: 1px solid $xpr-border-table;
|
||||
border-right: 1px solid $xpr-border-table;
|
||||
padding: 4px 8px;
|
||||
|
||||
&:last-child { border-right: none; }
|
||||
}
|
||||
tbody tr:hover {
|
||||
background: $xpr-row-hover;
|
||||
}
|
||||
tbody tr.o_data_row:focus-within {
|
||||
background: $xpr-cell-focus;
|
||||
font-style: italic;
|
||||
margin-top: 4px;
|
||||
padding: 4px 0 0;
|
||||
border-top: 1px dashed $xpr-bake-border;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Bake column — coloured pill (text-input visual)
|
||||
// SECTION TITLE — between header and lines
|
||||
// ============================================================
|
||||
.o_fp_express_lines .o_list_view td[name="bake_instructions"] input[type="text"] {
|
||||
.o_fp_xpr_section_title {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: $xpr-accent;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
margin: 16px 0 8px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid $xpr-border;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// LINES TABLE — tight bordered spreadsheet
|
||||
// ============================================================
|
||||
.o_fp_xpr_lines .o_list_view table {
|
||||
border-collapse: collapse;
|
||||
border: 1px solid $xpr-border-table;
|
||||
}
|
||||
.o_fp_xpr_lines .o_list_view thead th {
|
||||
background: $xpr-table-head;
|
||||
color: $xpr-text-muted;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
font-weight: 600;
|
||||
border-bottom: 2px solid $xpr-border-table;
|
||||
border-right: 1px solid $xpr-border-table;
|
||||
padding: 6px 8px;
|
||||
|
||||
&:last-child { border-right: none; }
|
||||
}
|
||||
.o_fp_xpr_lines .o_list_view tbody td {
|
||||
border-bottom: 1px solid $xpr-border-table;
|
||||
border-right: 1px solid $xpr-border-table;
|
||||
padding: 4px 8px;
|
||||
|
||||
&:last-child { border-right: none; }
|
||||
}
|
||||
.o_fp_xpr_lines .o_list_view tbody tr:hover { background: $xpr-row-hover; }
|
||||
.o_fp_xpr_lines .o_list_view tbody tr.o_data_row:focus-within { background: $xpr-cell-focus; }
|
||||
|
||||
// Bake column — coloured pill input
|
||||
.o_fp_xpr_lines td[name="bake_instructions"] input[type="text"] {
|
||||
background: $xpr-bake-bg;
|
||||
color: $xpr-bake-text;
|
||||
border: 1px solid $xpr-bake-border;
|
||||
@@ -93,20 +201,12 @@
|
||||
padding: 2px 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
min-width: 100px;
|
||||
|
||||
&::placeholder {
|
||||
color: $xpr-text-dim;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
background: transparent;
|
||||
}
|
||||
&:focus {
|
||||
background: $xpr-cell-focus;
|
||||
border-color: $xpr-accent;
|
||||
outline: none;
|
||||
}
|
||||
// Empty cell → render as muted italic "no bake"
|
||||
&:not(:focus):placeholder-shown {
|
||||
background: $xpr-card-soft;
|
||||
border-color: $xpr-border-strong;
|
||||
@@ -114,29 +214,17 @@
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Customer Line Job # — bold, uppercase, narrow
|
||||
// ============================================================
|
||||
.o_fp_express_lines .o_list_view td[name="customer_line_ref"] input {
|
||||
// Line Job # — bold uppercase narrow
|
||||
.o_fp_xpr_lines td[name="customer_line_ref"] input {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
max-width: 70px;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Masking toggle — bigger so it reads at a glance
|
||||
// ============================================================
|
||||
.o_fp_express_lines .o_list_view td[name="masking_enabled"] {
|
||||
.form-check-input,
|
||||
.o_field_boolean_toggle .form-check-input { transform: scale(1.15); }
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Inline action buttons (DWG / OPEN / + bulk)
|
||||
// ============================================================
|
||||
.o_fp_express_lines .o_fp_inline_btn {
|
||||
// Masking toggle — bigger
|
||||
.o_fp_xpr_lines td[name="masking_enabled"] .form-check-input { transform: scale(1.15); }
|
||||
// Inline buttons
|
||||
.o_fp_xpr_lines .o_fp_xpr_inline_btn {
|
||||
font-size: 10px !important;
|
||||
font-weight: 600 !important;
|
||||
letter-spacing: 0.5px;
|
||||
@@ -157,90 +245,108 @@
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Footer — Notes / Terms (left card) + Totals (right card)
|
||||
// FOOTER — Notes/Terms left + Totals right (CSS Grid)
|
||||
// ============================================================
|
||||
.o_fp_express_footer {
|
||||
.o_fp_xpr_footer {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 320px;
|
||||
gap: 24px;
|
||||
align-items: start;
|
||||
margin-top: 24px;
|
||||
}
|
||||
.o_fp_xpr_footer_left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
> .o_group { gap: 16px; }
|
||||
// Cards
|
||||
.o_fp_xpr_card {
|
||||
background: $xpr-card;
|
||||
border: 1px solid $xpr-border;
|
||||
border-radius: 4px;
|
||||
padding: 14px 16px;
|
||||
|
||||
.o_fp_footer_left,
|
||||
.o_fp_footer_totals {
|
||||
background: $xpr-card;
|
||||
.o_fp_xpr_card_title {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: $xpr-text;
|
||||
margin-bottom: 2px;
|
||||
|
||||
.o_fp_xpr_chip {
|
||||
background: $xpr-accent-bg;
|
||||
color: $xpr-accent;
|
||||
padding: 1px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
margin-left: 6px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
.o_fp_xpr_card_sub {
|
||||
font-size: 11px;
|
||||
color: $xpr-text-muted;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
// Textareas inside cards
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: 90px;
|
||||
border: 1px solid $xpr-border;
|
||||
border-radius: 4px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
border-radius: 3px;
|
||||
background: $xpr-card-soft;
|
||||
color: $xpr-text;
|
||||
padding: 8px;
|
||||
font-family: inherit;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
resize: vertical;
|
||||
|
||||
.o_fp_footer_left {
|
||||
.o_horizontal_separator {
|
||||
color: $xpr-text-muted;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
font-weight: 700;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.o_horizontal_separator:first-child { margin-top: 0; }
|
||||
textarea { min-height: 80px; }
|
||||
}
|
||||
|
||||
.o_fp_footer_totals {
|
||||
.o_horizontal_separator {
|
||||
color: $xpr-text-muted;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.4px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
&:focus {
|
||||
border-color: $xpr-accent;
|
||||
background: $xpr-card;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Grand Total — bigger + currency pill
|
||||
// ============================================================
|
||||
.o_fp_grand_total_label {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: $xpr-text;
|
||||
padding-top: 6px;
|
||||
border-top: 2px solid $xpr-text;
|
||||
margin-top: 4px;
|
||||
// Totals card
|
||||
.o_fp_xpr_totals {
|
||||
.o_fp_xpr_total_row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
font-size: 13px;
|
||||
color: $xpr-text;
|
||||
|
||||
.o_fp_xpr_total_label { color: $xpr-text-muted; }
|
||||
}
|
||||
.o_fp_xpr_total_row.o_fp_xpr_grand {
|
||||
border-top: 2px solid $xpr-text;
|
||||
margin-top: 8px;
|
||||
padding-top: 12px;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
|
||||
.o_fp_xpr_total_label { color: $xpr-text; }
|
||||
}
|
||||
}
|
||||
.o_fp_grand_total {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: $xpr-text;
|
||||
padding-top: 6px;
|
||||
border-top: 2px solid $xpr-text;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.o_fp_currency_pill {
|
||||
background: $xpr-accent-bg;
|
||||
color: $xpr-accent;
|
||||
.o_fp_xpr_currency_pill {
|
||||
background: $xpr-accent-bg !important;
|
||||
color: $xpr-accent !important;
|
||||
padding: 2px 10px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Section separators inside the sheet
|
||||
// ============================================================
|
||||
> .o_form_sheet .o_horizontal_separator {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: $xpr-accent;
|
||||
letter-spacing: 0.3px;
|
||||
border-top: 1px solid $xpr-border;
|
||||
padding-top: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// view_source badge column — Express vs Legacy in drafts list
|
||||
// ============================================================
|
||||
// view_source badge column on drafts list — accent-coloured for Express
|
||||
.o_list_view .badge.text-bg-info {
|
||||
background-color: $xpr-accent !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user