Files
Odoo-Modules/fusion_repairs/static/src/components/flowchart_designer/flowchart_designer.scss
gsinghpal b22bb11b31 feat(fusion_repairs): flowchart designer node content uses Odoo Wysiwyg
Replace the plain <textarea> in the flowchart designer's node-editor
right-panel with Odoo 19's native rich text editor so admins write
formatted prose / lists / bold / links / inline images without typing
HTML tags. The raw <textarea> stays available behind a toggle for the
power-user case (pasting markup from elsewhere, debugging).

CHANGES

manifest:
  - depends += 'html_editor' (provides @html_editor/wysiwyg)
  - bumped to 19.0.2.2.1

components/flowchart_designer/flowchart_designer.js:
  - import { Wysiwyg } from '@html_editor/wysiwyg'
  - import { MAIN_PLUGINS } from '@html_editor/plugin_sets'
  - register Wysiwyg in static components
  - state.sourceMode boolean (default false = rich text mode)
  - wysiwygConfig getter builds the EditorConfig for the SELECTED node;
    onChange reads editor.getContent() and writes back into the same
    selectedMeta.content_html the rest of the designer already uses,
    so the save path is unchanged
  - onWysiwygLoad(editor) captures the editor instance per dfId so the
    onChange callback can resolve the right one when nodes switch
  - onToggleSource flushes the current editor's content before flipping
    modes so unsaved keystrokes don't get lost

components/flowchart_designer/flowchart_designer.xml:
  - replaced <textarea>...</textarea> with a conditional block:
      sourceMode == false -> <Wysiwyg t-key="'wysiwyg-' + selectedNodeId"
                                       config="wysiwygConfig"
                                       onLoad="onWysiwygLoad.bind(this)"/>
      sourceMode == true  -> <textarea class="font-monospace" rows="10"/>
  - t-key forces the editor to re-mount with the freshly-selected node's
    content; otherwise switching nodes would keep showing the first
    selected node's HTML
  - new toolbar row above the editor has a "HTML Source" / "Rich Text"
    toggle button (eye / code icons) so the user can flip at will
  - hint text updated to reflect what each mode supports

components/flowchart_designer/flowchart_designer.scss:
  - widened the right editor panel from 320px to 360px to give the
    Wysiwyg toolbar room to breathe
  - new .fr-wysiwyg-shell rule frames the embedded editor with the same
    border + background as the other form-controls in the panel, with
    a min-height of 180px and max-height 320px so it scrolls when the
    content grows. Pins .o-we-toolbar inside the shell so it stays in
    view as the user scrolls long content.

The save path, the runtime renderer, and the data model are unchanged -
content_html is still sanitised HTML stored on fusion.repair.flowchart.node.

Verified on local westin-v19:
  - upgrade clean (no errors, no warnings)
  - login serves 200 after restart
  - 4 stale asset bundles flushed; Drawflow JS still served 46KB at
    /fusion_repairs/static/src/lib/drawflow/drawflow.min.js
  - Wysiwyg export confirmed at
    /usr/lib/python3/dist-packages/odoo/addons/html_editor/static/src/wysiwyg.js:25
  - MAIN_PLUGINS export confirmed at plugin_sets.js:103

Bumped to 19.0.2.2.1.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-27 13:18:27 -04:00

271 lines
5.8 KiB
SCSS

// Drawflow designer + runner shared theming.
// Uses the Bundle 1 SCSS token pattern with dark-mode-safe explicit hex.
$o-webclient-color-scheme: bright !default;
$_fr_page-hex: #f3f4f6;
$_fr_card-hex: #ffffff;
$_fr_border-hex: #d8dadd;
$_fr_text-muted-hex: #6b7280;
$_fr_panel-hex: #ffffff;
@if $o-webclient-color-scheme == dark {
$_fr_page-hex: #1a1d21 !global;
$_fr_card-hex: #22262d !global;
$_fr_border-hex: #2d3138 !global;
$_fr_text-muted-hex: #9aa1aa !global;
$_fr_panel-hex: #1f2329 !global;
}
$fr-page: var(--fr-page-bg, #{$_fr_page-hex});
$fr-card: var(--fr-card-bg, #{$_fr_card-hex});
$fr-border: var(--fr-border-color, #{$_fr_border-hex});
$fr-muted: var(--fr-text-muted, #{$_fr_text-muted-hex});
$fr-panel: var(--fr-panel-bg, #{$_fr_panel-hex});
.fr-designer-wrap {
display: flex;
flex-direction: column;
height: calc(100vh - 46px);
background: $fr-page;
}
.fr-designer-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 16px;
background: $fr-panel;
border-bottom: 1px solid $fr-border;
.fr-designer-title { font-size: 14px; }
.fr-designer-toolbar-actions { display: flex; gap: 4px; }
}
.fr-designer-body {
display: flex;
flex: 1;
overflow: hidden;
}
.fr-designer-canvas-wrap {
flex: 1;
overflow: hidden;
position: relative;
background: $fr-page;
.drawflow {
width: 100%;
height: 100%;
background: $fr-page !important;
}
}
.fr-designer-editor {
width: 360px;
border-left: 1px solid $fr-border;
background: $fr-panel;
padding: 12px 14px;
overflow-y: auto;
h6 { margin-bottom: 8px; font-weight: 700; }
}
// Frame the embedded Odoo Wysiwyg so it visually matches the other
// form-control fields in the right panel (border, padding, scrollable).
.fr-wysiwyg-shell {
border: 1px solid $fr-border;
border-radius: 4px;
background: $fr-card;
min-height: 180px;
max-height: 320px;
overflow-y: auto;
.odoo-editor-editable, .fr-wysiwyg-content {
min-height: 160px;
padding: 8px 10px;
outline: none;
}
.o-we-toolbar, .o_we_toolbar {
// Pin the Wysiwyg toolbar inside the shell so it scrolls with content.
z-index: 2;
}
}
// ----- Node card styling (inside Drawflow's drawflow_content_node) -----
.drawflow .drawflow-node {
background: transparent !important;
padding: 0 !important;
border: 0 !important;
box-shadow: none !important;
min-width: 220px;
&.selected .fr-node-card {
outline: 2px solid #2563eb;
outline-offset: 2px;
}
}
.fr-node-card {
background: $fr-card;
color: inherit;
border: 1px solid $fr-border;
border-top: 4px solid #6b7280;
border-radius: 6px;
width: 220px;
overflow: hidden;
font-size: 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
}
.fr-node-head {
padding: 4px 8px;
color: #ffffff;
font-weight: 600;
text-transform: uppercase;
font-size: 10px;
letter-spacing: 0.05em;
display: flex;
justify-content: space-between;
align-items: center;
}
.fr-start-badge,
.fr-outcome-badge {
background: rgba(0,0,0,0.25);
color: #ffffff;
border-radius: 3px;
font-size: 9px;
padding: 1px 5px;
margin-left: 4px;
}
.fr-node-title {
padding: 8px 10px 4px;
font-weight: 600;
color: #222;
}
.fr-node-body {
padding: 0 10px 6px;
color: $fr-muted;
max-height: 60px;
overflow: hidden;
font-size: 11px;
line-height: 1.4;
}
.fr-node-foot {
padding: 4px 10px 8px;
font-size: 10px;
color: $fr-muted;
}
.fr-media-badge {
background: $fr-page;
border: 1px solid $fr-border;
padding: 1px 6px;
border-radius: 3px;
}
.fr-media-thumbs {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.fr-media-thumb {
width: 48px;
height: 48px;
object-fit: cover;
border-radius: 4px;
border: 1px solid $fr-border;
}
// ----- Runner (Phase 3) styling -----
.fr-runner-wrap {
display: flex;
flex-direction: column;
height: calc(100vh - 46px);
background: $fr-page;
}
.fr-runner-header {
background: $fr-panel;
border-bottom: 1px solid $fr-border;
padding: 10px 18px;
display: flex;
justify-content: space-between;
align-items: center;
.fr-runner-title { font-size: 15px; font-weight: 700; }
}
.fr-runner-body {
flex: 1;
overflow-y: auto;
padding: 30px 20px;
}
.fr-runner-card {
max-width: 760px;
margin: 0 auto;
background: $fr-card;
border: 1px solid $fr-border;
border-radius: 10px;
padding: 30px;
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
}
.fr-runner-card h2 {
font-size: 24px;
font-weight: 700;
margin-bottom: 16px;
}
.fr-runner-content {
font-size: 16px;
line-height: 1.5;
margin-bottom: 20px;
}
.fr-runner-media {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 10px;
margin: 16px 0;
}
.fr-runner-media img,
.fr-runner-media video {
width: 100%;
border-radius: 6px;
border: 1px solid $fr-border;
}
.fr-runner-options {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 18px;
}
.fr-runner-option-btn {
text-align: left;
padding: 14px 18px;
font-size: 15px;
border-radius: 6px;
}
.fr-runner-note {
margin-top: 16px;
}
.fr-runner-transcript {
margin-top: 24px;
padding: 12px 16px;
background: $fr-page;
border-radius: 6px;
font-size: 12px;
color: $fr-muted;
border: 1px solid $fr-border;
}
// Tree-view (canvas) read-only highlights for the runner toggle.
.fr-runner-tree .fr-node-card { opacity: 0.55; }
.fr-runner-tree .fr-visited .fr-node-card { opacity: 1; outline: 2px solid #16a34a; }
.fr-runner-tree .fr-current .fr-node-card { opacity: 1; outline: 3px solid #facc15; }