diff --git a/dist/tryton-sao.css b/dist/tryton-sao.css index 533f046..94f5a96 100644 --- a/dist/tryton-sao.css +++ b/dist/tryton-sao.css @@ -10237,25 +10237,35 @@ img.icon { } .form fieldset.form-panel { background: #fff; - border: 1px solid #d7e3de; + border: 0; border-radius: 8px; - box-shadow: 0 3px 10px rgba(30, 55, 58, 0.075); - margin: 8px 0; - overflow: visible; - padding: 14px 16px 16px; + box-shadow: + inset 0 0 0 1px #d3e1dc, + 0 3px 10px rgba(30, 55, 58, 0.075); + margin: 4px 0; + overflow-x: hidden; + overflow-y: visible; + padding: 42px 16px 16px; + position: relative; } .form fieldset.form-panel > legend { align-items: center; + background: #fff; border-bottom: 1px solid #e6eee9; color: #145369; display: flex; font-size: 15px; font-weight: 700; gap: 8px; + height: 25px; + left: 16px; line-height: 1.35; - margin: 0 0 12px; - padding: 0 0 9px; - width: 100%; + margin: 0; + padding: 0 0 8px; + position: absolute; + right: 16px; + top: 12px; + width: auto; } .form fieldset.form-panel > legend .form-panel-icon { height: 18px; @@ -10270,6 +10280,11 @@ img.icon { min-width: 0; padding: 2px; } +.form fieldset.form-panel > .form-container > .form-item { + justify-self: stretch; + justify-content: flex-start; + align-self: center; +} .form fieldset.form-panel .form-label { color: #4b5f60; font-weight: 600; @@ -10285,16 +10300,59 @@ img.icon { align-items: center; gap: 10px 18px; } +.form fieldset.form-panel-summary > .form-container > .form-item { + align-items: center; + align-self: center; +} +.form fieldset.form-panel-summary .form-label { + align-self: center; +} .form fieldset.form-panel-sidebar { box-shadow: 0 4px 14px rgba(30, 55, 58, 0.08); + padding: 32px 10px 9px; } .form fieldset.form-panel-sidebar > .form-container, .form .form-panel-sidebar-grid { - gap: 8px 10px; + gap: 4px 8px; } .form fieldset.form-panel-sidebar .form-label { color: #145369; - font-size: 12px; + font-size: 11px; + line-height: 1.2; + white-space: nowrap; +} +.form fieldset.form-panel-sidebar .form-item { + padding: 1px; +} +.form fieldset.form-panel-sidebar input.form-control, +.form fieldset.form-panel-sidebar .input-group > .form-control, +.form fieldset.form-panel-sidebar .input-group-addon, +.form fieldset.form-panel-sidebar .selection { + background: transparent; + border-color: transparent; + box-shadow: none; + min-height: 20px; + padding-bottom: 1px; + padding-top: 1px; +} +.form fieldset.form-panel-sidebar input.form-control, +.form fieldset.form-panel-sidebar .input-group > .form-control { + font-size: 11px; + font-weight: 700; + height: 22px; + padding-left: 0; + padding-right: 0; +} +.form fieldset.form-panel-sidebar .input-group-addon { + font-size: 11px; + height: 22px; + padding: 1px 4px; +} +.form fieldset#purchase_recap.form-panel-sidebar, +.form fieldset[id="purchase_recap"].form-panel-sidebar, +.form fieldset#sale_recap.form-panel-sidebar, +.form fieldset[id="sale_recap"].form-panel-sidebar { + min-height: 172px; } .form fieldset.form-panel-plain { background: transparent; @@ -11048,13 +11106,24 @@ body { html[theme="default"] .screen-container > .filter-box, html[theme="default"] .screen-container .content-box > .form, html[theme="default"] .screen-container .content-box > form { - background: linear-gradient(180deg, #f4f8f6 0%, #edf4f1 100%); + background: linear-gradient(180deg, #fbfcfb 0%, #f6faf8 100%); border: 1px solid #dfe8e3; border-radius: 12px; padding: 14px 16px 10px; box-shadow: 0 3px 12px rgba(53, 81, 84, 0.06); } +html[theme="default"] .form .tab-pane:has(> .form-container > .form-item > fieldset.form-panel) { + background: #f7faf8; + border-radius: 10px; + padding: 8px; +} + +html[theme="default"] .form .tab-pane:has(> .form-container > .form-item > fieldset.form-panel) + > .form-container { + gap: 4px 14px; +} + html[theme="default"] .screen-container .form-group { margin-bottom: 12px; } @@ -11084,6 +11153,28 @@ html[theme="default"] .screen-container input[type="search"]:not(.ag-grid-tree-p -webkit-box-shadow: inset 0 1px 2px rgba(53, 81, 84, 0.03); } +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input.form-control:not([type="checkbox"]):not([type="radio"]), +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar select.form-control, +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar textarea.form-control, +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar .input-group > .form-control, +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar .input-group > .form-control[readonly], +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar .input-group-addon, +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input[type="text"]:not(.ag-grid-tree-panel-checkbox), +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input[type="date"]:not(.ag-grid-tree-panel-checkbox), +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input[type="datetime-local"]:not(.ag-grid-tree-panel-checkbox), +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input[type="number"]:not(.ag-grid-tree-panel-checkbox), +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input[type="search"]:not(.ag-grid-tree-panel-checkbox) { + background: transparent !important; + border-color: transparent !important; + box-shadow: none !important; + -webkit-box-shadow: none !important; + font-size: 11px !important; + font-weight: 700; + height: 22px !important; + min-height: 20px !important; + padding: 0 !important; +} + html[theme="default"] .screen-container .input-group-sm.input-icon-primary > .form-control, html[theme="default"] .screen-container .input-icon-primary > .form-control { padding-left: 33px; diff --git a/dist/tryton-sao.js b/dist/tryton-sao.js index 8c6878a..6e345d2 100644 --- a/dist/tryton-sao.js +++ b/dist/tryton-sao.js @@ -21313,6 +21313,167 @@ function eval_pyson(value){ const json = value.slice(3); const config = JSON.parse(json); // doit contenir { highlightedCountry: '250' } + if (config.type === 'execution-summary') { + let d3Div = container.find('.d3-execution-summary-container'); + if (d3Div.length === 0) { + d3Div = $('
', { + class: 'd3-execution-summary-container', + css: { + width: '100%', + height: height.toString() + 'px', + overflow: 'hidden', + background: 'transparent' + } + }).appendTo(container); + } else { + d3Div.empty(); + } + + const loadD3ExecutionSummary = () => { + const node = d3Div[0]; + const w = Math.max(300, node.clientWidth || width || 360); + const h = Math.max(125, height || 160); + const totals = config.totals || {}; + const rows = config.rows || []; + const totalQty = Math.max(1, Number(totals.quantity || 0)); + const matched = Number(totals.matched || 0); + const shipped = Number(totals.shipped || 0); + const matchedPct = Math.min(100, Math.round((matched / totalQty) * 100)); + const shippedPct = Math.min(100, Math.round((shipped / totalQty) * 100)); + const fmt = d3.format(',.0f'); + const hasTitle = Boolean(config.title); + const statsY = hasTitle ? 30 : 8; + const barY = hasTitle ? 58 : 36; + const rowY = hasTitle ? 84 : 62; + const palette = { + matched: '#2b8c87', + shipped: '#145369', + open: '#dbe8e3', + text: '#24484d', + muted: '#6b7d7b', + line: '#e3ede8' + }; + + const svg = d3.select(node).append('svg') + .attr('width', w) + .attr('height', h) + .attr('viewBox', `0 0 ${w} ${h}`); + + if (hasTitle) { + svg.append('text') + .attr('x', 4) + .attr('y', 15) + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 12) + .attr('font-weight', 700) + .attr('fill', palette.text) + .text(config.title); + } + + const stats = [ + {label: 'Lines', value: fmt(totals.lines || rows.length || 0)}, + {label: 'Matched', value: matchedPct + '%'}, + {label: 'Shipped', value: shippedPct + '%'} + ]; + const stat = svg.selectAll('g.stat') + .data(stats) + .join('g') + .attr('class', 'stat') + .attr('transform', (d, i) => `translate(${4 + i * Math.min(95, (w - 16) / 3)}, ${statsY})`); + stat.append('text') + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 10) + .attr('fill', palette.muted) + .text(d => d.label); + stat.append('text') + .attr('y', 16) + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 14) + .attr('font-weight', 700) + .attr('fill', palette.text) + .text(d => d.value); + + const barX = 4; + const barW = Math.max(160, w - 8); + const barH = 8; + svg.append('rect') + .attr('x', barX) + .attr('y', barY) + .attr('width', barW) + .attr('height', barH) + .attr('rx', 4) + .attr('fill', palette.open); + svg.append('rect') + .attr('x', barX) + .attr('y', barY) + .attr('width', barW * Math.min(1, matched / totalQty)) + .attr('height', barH) + .attr('rx', 4) + .attr('fill', palette.matched); + svg.append('rect') + .attr('x', barX) + .attr('y', barY) + .attr('width', barW * Math.min(1, shipped / totalQty)) + .attr('height', barH) + .attr('rx', 4) + .attr('fill', palette.shipped); + + const row = svg.selectAll('g.exec-row') + .data(rows.slice(0, 3)) + .join('g') + .attr('class', 'exec-row') + .attr('transform', (d, i) => `translate(4, ${rowY + i * 24})`); + row.append('line') + .attr('x1', 0) + .attr('x2', w - 8) + .attr('y1', -8) + .attr('y2', -8) + .attr('stroke', palette.line); + row.append('text') + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 10) + .attr('font-weight', 700) + .attr('fill', palette.text) + .text(d => (d.product || 'Line').slice(0, 18)); + row.append('text') + .attr('x', 170) + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 10) + .attr('fill', palette.muted) + .text(d => `${fmt(d.shipped || 0)}/${fmt(d.quantity || 0)} ${d.unit || ''}`); + row.append('text') + .attr('x', 250) + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 10) + .attr('fill', palette.muted) + .text(d => (d.route || '').slice(0, 32)); + + if (!rows.length) { + svg.append('text') + .attr('x', 4) + .attr('y', 90) + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 11) + .attr('fill', palette.muted) + .text('No purchase lines to summarize'); + } + }; + + const ensureD3 = (cb) => { + if (typeof d3 !== 'undefined') { + cb(); + return; + } + const script = document.createElement('script'); + script.src = 'https://d3js.org/d3.v7.min.js'; + script.onload = cb; + document.head.appendChild(script); + }; + + ensureD3(loadD3ExecutionSummary); + return; + } + // Réutilise le conteneur si déjà présent let d3Div = container.find('.d3-map-container'); if (d3Div.length === 0) { @@ -23181,9 +23342,24 @@ function eval_pyson(value){ var save_options = this._pending_state_save_options || {}; this._pending_state_save_options = null; this._state_save_timer = null; - this._persist_grid_state(save_options); + this._mark_grid_state_dirty(save_options); }, 80); }, + _mark_grid_state_dirty: function(options) { + options = options || {}; + var state = this._capture_grid_state(); + var active_preset = this._find_layout_preset(this._active_layout_id); + this._layout_dirty = active_preset ? + !Sao.common.compare(active_preset.state, state) : + !this._is_standard_layout_state(state); + this._render_column_menu(); + this._render_grouping_panel(); + this._render_layout_panel(); + this._update_layout_button(); + if (!options.skipLayoutRefresh) { + this._schedule_grid_layout_refresh(); + } + }, _on_grid_column_layout_changed: function(options) { options = options || {}; this._schedule_state_save(options); @@ -23672,7 +23848,7 @@ function eval_pyson(value){ this._persisted_state = jQuery.extend({}, this._persisted_state || {}); this._persisted_state.grouping = this._build_grouping_state( this._get_default_grouping_state(), state); - this._save_persisted_state(this._persisted_state); + this._mark_grid_state_dirty(); this._force_rowdata_reset = true; this.display(this.get_selected_paths()); this._render_column_menu(); @@ -24837,7 +25013,7 @@ function eval_pyson(value){ }], }); this._syncing_grid = false; - this._persist_grid_state(); + this._mark_grid_state_dirty(); this._schedule_grid_layout_refresh(); }, _sync_optional_columns_from_grid_state: function(column_states) { @@ -27439,7 +27615,7 @@ function eval_pyson(value){ } else { this.screen.order = [order[0]]; } - this._persist_grid_state(); + this._mark_grid_state_dirty(); if (filter_model && Object.keys(filter_model).length) { return; } @@ -28146,6 +28322,7 @@ function eval_pyson(value){ var rect = this.grid_host[0].getBoundingClientRect(); var content_box = this.grid_host.closest('.content-box.ag-grid-content-box'); var screen_container = this.grid_host.closest('.screen-container'); + var modal_body = this.grid_host.closest('.modal-body'); var toolbar_height = this.toolbar && this.toolbar.length ? Math.ceil(this.toolbar.outerHeight(true)) : 0; var summary_height = this.summary_bar && this.summary_bar.length ? @@ -28161,9 +28338,14 @@ function eval_pyson(value){ window.innerHeight - content_rect.top - toolbar_height - summary_height - external_scroll_height - bottom_margin); } - var compact_min_height = 260; - var readable_min_height = 420; - var maximum_height = 720; + var embedded_auto_height = this.is_embedded_one2many && + !this._embedded_height; + var compact_min_height = (modal_body.length || embedded_auto_height) ? + 170 : 260; + var readable_min_height = modal_body.length ? 240 : + (embedded_auto_height ? 220 : 420); + var maximum_height = modal_body.length ? 360 : + (embedded_auto_height ? 300 : 720); var height; if (this.is_embedded_one2many && this._embedded_height) { height = Math.max(80, this._embedded_height - toolbar_height - diff --git a/tryton-sao.css b/tryton-sao.css index bd6ecb3..5804f4e 100644 --- a/tryton-sao.css +++ b/tryton-sao.css @@ -10237,25 +10237,35 @@ img.icon { } .form fieldset.form-panel { background: #fff; - border: 1px solid #d7e3de; + border: 0; border-radius: 8px; - box-shadow: 0 3px 10px rgba(30, 55, 58, 0.075); - margin: 8px 0; - overflow: visible; - padding: 14px 16px 16px; + box-shadow: + inset 0 0 0 1px #d3e1dc, + 0 3px 10px rgba(30, 55, 58, 0.075); + margin: 4px 0; + overflow-x: hidden; + overflow-y: visible; + padding: 42px 16px 16px; + position: relative; } .form fieldset.form-panel > legend { align-items: center; + background: #fff; border-bottom: 1px solid #e6eee9; color: #145369; display: flex; font-size: 15px; font-weight: 700; gap: 8px; + height: 25px; + left: 16px; line-height: 1.35; - margin: 0 0 12px; - padding: 0 0 9px; - width: 100%; + margin: 0; + padding: 0 0 8px; + position: absolute; + right: 16px; + top: 12px; + width: auto; } .form fieldset.form-panel > legend .form-panel-icon { height: 18px; @@ -10270,6 +10280,11 @@ img.icon { min-width: 0; padding: 2px; } +.form fieldset.form-panel > .form-container > .form-item { + justify-self: stretch; + justify-content: flex-start; + align-self: center; +} .form fieldset.form-panel .form-label { color: #4b5f60; font-weight: 600; @@ -10285,16 +10300,59 @@ img.icon { align-items: center; gap: 10px 18px; } +.form fieldset.form-panel-summary > .form-container > .form-item { + align-items: center; + align-self: center; +} +.form fieldset.form-panel-summary .form-label { + align-self: center; +} .form fieldset.form-panel-sidebar { box-shadow: 0 4px 14px rgba(30, 55, 58, 0.08); + padding: 32px 10px 9px; } .form fieldset.form-panel-sidebar > .form-container, .form .form-panel-sidebar-grid { - gap: 8px 10px; + gap: 4px 8px; } .form fieldset.form-panel-sidebar .form-label { color: #145369; - font-size: 12px; + font-size: 11px; + line-height: 1.2; + white-space: nowrap; +} +.form fieldset.form-panel-sidebar .form-item { + padding: 1px; +} +.form fieldset.form-panel-sidebar input.form-control, +.form fieldset.form-panel-sidebar .input-group > .form-control, +.form fieldset.form-panel-sidebar .input-group-addon, +.form fieldset.form-panel-sidebar .selection { + background: transparent; + border-color: transparent; + box-shadow: none; + min-height: 20px; + padding-bottom: 1px; + padding-top: 1px; +} +.form fieldset.form-panel-sidebar input.form-control, +.form fieldset.form-panel-sidebar .input-group > .form-control { + font-size: 11px; + font-weight: 700; + height: 22px; + padding-left: 0; + padding-right: 0; +} +.form fieldset.form-panel-sidebar .input-group-addon { + font-size: 11px; + height: 22px; + padding: 1px 4px; +} +.form fieldset#purchase_recap.form-panel-sidebar, +.form fieldset[id="purchase_recap"].form-panel-sidebar, +.form fieldset#sale_recap.form-panel-sidebar, +.form fieldset[id="sale_recap"].form-panel-sidebar { + min-height: 172px; } .form fieldset.form-panel-plain { background: transparent; @@ -11048,13 +11106,24 @@ body { html[theme="default"] .screen-container > .filter-box, html[theme="default"] .screen-container .content-box > .form, html[theme="default"] .screen-container .content-box > form { - background: linear-gradient(180deg, #f4f8f6 0%, #edf4f1 100%); + background: linear-gradient(180deg, #fbfcfb 0%, #f6faf8 100%); border: 1px solid #dfe8e3; border-radius: 12px; padding: 14px 16px 10px; box-shadow: 0 3px 12px rgba(53, 81, 84, 0.06); } +html[theme="default"] .form .tab-pane:has(> .form-container > .form-item > fieldset.form-panel) { + background: #f7faf8; + border-radius: 10px; + padding: 8px; +} + +html[theme="default"] .form .tab-pane:has(> .form-container > .form-item > fieldset.form-panel) + > .form-container { + gap: 4px 14px; +} + html[theme="default"] .screen-container .form-group { margin-bottom: 12px; } @@ -11084,6 +11153,28 @@ html[theme="default"] .screen-container input[type="search"]:not(.ag-grid-tree-p -webkit-box-shadow: inset 0 1px 2px rgba(53, 81, 84, 0.03); } +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input.form-control:not([type="checkbox"]):not([type="radio"]), +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar select.form-control, +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar textarea.form-control, +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar .input-group > .form-control, +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar .input-group > .form-control[readonly], +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar .input-group-addon, +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input[type="text"]:not(.ag-grid-tree-panel-checkbox), +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input[type="date"]:not(.ag-grid-tree-panel-checkbox), +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input[type="datetime-local"]:not(.ag-grid-tree-panel-checkbox), +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input[type="number"]:not(.ag-grid-tree-panel-checkbox), +html[theme="default"] .screen-container .form fieldset.form-panel-sidebar input[type="search"]:not(.ag-grid-tree-panel-checkbox) { + background: transparent !important; + border-color: transparent !important; + box-shadow: none !important; + -webkit-box-shadow: none !important; + font-size: 11px !important; + font-weight: 700; + height: 22px !important; + min-height: 20px !important; + padding: 0 !important; +} + html[theme="default"] .screen-container .input-group-sm.input-icon-primary > .form-control, html[theme="default"] .screen-container .input-icon-primary > .form-control { padding-left: 33px; diff --git a/tryton-sao.js b/tryton-sao.js index 8c6878a..6e345d2 100644 --- a/tryton-sao.js +++ b/tryton-sao.js @@ -21313,6 +21313,167 @@ function eval_pyson(value){ const json = value.slice(3); const config = JSON.parse(json); // doit contenir { highlightedCountry: '250' } + if (config.type === 'execution-summary') { + let d3Div = container.find('.d3-execution-summary-container'); + if (d3Div.length === 0) { + d3Div = $('', { + class: 'd3-execution-summary-container', + css: { + width: '100%', + height: height.toString() + 'px', + overflow: 'hidden', + background: 'transparent' + } + }).appendTo(container); + } else { + d3Div.empty(); + } + + const loadD3ExecutionSummary = () => { + const node = d3Div[0]; + const w = Math.max(300, node.clientWidth || width || 360); + const h = Math.max(125, height || 160); + const totals = config.totals || {}; + const rows = config.rows || []; + const totalQty = Math.max(1, Number(totals.quantity || 0)); + const matched = Number(totals.matched || 0); + const shipped = Number(totals.shipped || 0); + const matchedPct = Math.min(100, Math.round((matched / totalQty) * 100)); + const shippedPct = Math.min(100, Math.round((shipped / totalQty) * 100)); + const fmt = d3.format(',.0f'); + const hasTitle = Boolean(config.title); + const statsY = hasTitle ? 30 : 8; + const barY = hasTitle ? 58 : 36; + const rowY = hasTitle ? 84 : 62; + const palette = { + matched: '#2b8c87', + shipped: '#145369', + open: '#dbe8e3', + text: '#24484d', + muted: '#6b7d7b', + line: '#e3ede8' + }; + + const svg = d3.select(node).append('svg') + .attr('width', w) + .attr('height', h) + .attr('viewBox', `0 0 ${w} ${h}`); + + if (hasTitle) { + svg.append('text') + .attr('x', 4) + .attr('y', 15) + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 12) + .attr('font-weight', 700) + .attr('fill', palette.text) + .text(config.title); + } + + const stats = [ + {label: 'Lines', value: fmt(totals.lines || rows.length || 0)}, + {label: 'Matched', value: matchedPct + '%'}, + {label: 'Shipped', value: shippedPct + '%'} + ]; + const stat = svg.selectAll('g.stat') + .data(stats) + .join('g') + .attr('class', 'stat') + .attr('transform', (d, i) => `translate(${4 + i * Math.min(95, (w - 16) / 3)}, ${statsY})`); + stat.append('text') + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 10) + .attr('fill', palette.muted) + .text(d => d.label); + stat.append('text') + .attr('y', 16) + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 14) + .attr('font-weight', 700) + .attr('fill', palette.text) + .text(d => d.value); + + const barX = 4; + const barW = Math.max(160, w - 8); + const barH = 8; + svg.append('rect') + .attr('x', barX) + .attr('y', barY) + .attr('width', barW) + .attr('height', barH) + .attr('rx', 4) + .attr('fill', palette.open); + svg.append('rect') + .attr('x', barX) + .attr('y', barY) + .attr('width', barW * Math.min(1, matched / totalQty)) + .attr('height', barH) + .attr('rx', 4) + .attr('fill', palette.matched); + svg.append('rect') + .attr('x', barX) + .attr('y', barY) + .attr('width', barW * Math.min(1, shipped / totalQty)) + .attr('height', barH) + .attr('rx', 4) + .attr('fill', palette.shipped); + + const row = svg.selectAll('g.exec-row') + .data(rows.slice(0, 3)) + .join('g') + .attr('class', 'exec-row') + .attr('transform', (d, i) => `translate(4, ${rowY + i * 24})`); + row.append('line') + .attr('x1', 0) + .attr('x2', w - 8) + .attr('y1', -8) + .attr('y2', -8) + .attr('stroke', palette.line); + row.append('text') + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 10) + .attr('font-weight', 700) + .attr('fill', palette.text) + .text(d => (d.product || 'Line').slice(0, 18)); + row.append('text') + .attr('x', 170) + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 10) + .attr('fill', palette.muted) + .text(d => `${fmt(d.shipped || 0)}/${fmt(d.quantity || 0)} ${d.unit || ''}`); + row.append('text') + .attr('x', 250) + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 10) + .attr('fill', palette.muted) + .text(d => (d.route || '').slice(0, 32)); + + if (!rows.length) { + svg.append('text') + .attr('x', 4) + .attr('y', 90) + .attr('font-family', 'Inter, sans-serif') + .attr('font-size', 11) + .attr('fill', palette.muted) + .text('No purchase lines to summarize'); + } + }; + + const ensureD3 = (cb) => { + if (typeof d3 !== 'undefined') { + cb(); + return; + } + const script = document.createElement('script'); + script.src = 'https://d3js.org/d3.v7.min.js'; + script.onload = cb; + document.head.appendChild(script); + }; + + ensureD3(loadD3ExecutionSummary); + return; + } + // Réutilise le conteneur si déjà présent let d3Div = container.find('.d3-map-container'); if (d3Div.length === 0) { @@ -23181,9 +23342,24 @@ function eval_pyson(value){ var save_options = this._pending_state_save_options || {}; this._pending_state_save_options = null; this._state_save_timer = null; - this._persist_grid_state(save_options); + this._mark_grid_state_dirty(save_options); }, 80); }, + _mark_grid_state_dirty: function(options) { + options = options || {}; + var state = this._capture_grid_state(); + var active_preset = this._find_layout_preset(this._active_layout_id); + this._layout_dirty = active_preset ? + !Sao.common.compare(active_preset.state, state) : + !this._is_standard_layout_state(state); + this._render_column_menu(); + this._render_grouping_panel(); + this._render_layout_panel(); + this._update_layout_button(); + if (!options.skipLayoutRefresh) { + this._schedule_grid_layout_refresh(); + } + }, _on_grid_column_layout_changed: function(options) { options = options || {}; this._schedule_state_save(options); @@ -23672,7 +23848,7 @@ function eval_pyson(value){ this._persisted_state = jQuery.extend({}, this._persisted_state || {}); this._persisted_state.grouping = this._build_grouping_state( this._get_default_grouping_state(), state); - this._save_persisted_state(this._persisted_state); + this._mark_grid_state_dirty(); this._force_rowdata_reset = true; this.display(this.get_selected_paths()); this._render_column_menu(); @@ -24837,7 +25013,7 @@ function eval_pyson(value){ }], }); this._syncing_grid = false; - this._persist_grid_state(); + this._mark_grid_state_dirty(); this._schedule_grid_layout_refresh(); }, _sync_optional_columns_from_grid_state: function(column_states) { @@ -27439,7 +27615,7 @@ function eval_pyson(value){ } else { this.screen.order = [order[0]]; } - this._persist_grid_state(); + this._mark_grid_state_dirty(); if (filter_model && Object.keys(filter_model).length) { return; } @@ -28146,6 +28322,7 @@ function eval_pyson(value){ var rect = this.grid_host[0].getBoundingClientRect(); var content_box = this.grid_host.closest('.content-box.ag-grid-content-box'); var screen_container = this.grid_host.closest('.screen-container'); + var modal_body = this.grid_host.closest('.modal-body'); var toolbar_height = this.toolbar && this.toolbar.length ? Math.ceil(this.toolbar.outerHeight(true)) : 0; var summary_height = this.summary_bar && this.summary_bar.length ? @@ -28161,9 +28338,14 @@ function eval_pyson(value){ window.innerHeight - content_rect.top - toolbar_height - summary_height - external_scroll_height - bottom_margin); } - var compact_min_height = 260; - var readable_min_height = 420; - var maximum_height = 720; + var embedded_auto_height = this.is_embedded_one2many && + !this._embedded_height; + var compact_min_height = (modal_body.length || embedded_auto_height) ? + 170 : 260; + var readable_min_height = modal_body.length ? 240 : + (embedded_auto_height ? 220 : 420); + var maximum_height = modal_body.length ? 360 : + (embedded_auto_height ? 300 : 720); var height; if (this.is_embedded_one2many && this._embedded_height) { height = Math.max(80, this._embedded_height - toolbar_height -