Badge notes

This commit is contained in:
2026-06-02 20:17:32 +02:00
parent 5ceebbf80c
commit 2835f49182
6 changed files with 825 additions and 40 deletions

View File

@@ -1027,3 +1027,101 @@ Point de deploiement:
- `GROUPING_LOAD_FIELDS_FLAT_JSON`
- `GROUPING_KEYS_FLAT_JSON`
- si une correction visuelle semble inactive, verifier d'abord quel fichier est charge par `index.html` et si `dist/` a ete synchronise
### Session du 2026-06-02 - KPI badges Lots Management non resolu
Demande UX:
- rapprocher `Lots management` de la maquette recue:
- barre KPI compacte entre les boutons AG Grid et la grille
- compteurs par valeur de badge (`Open`, `Physic`, `scheduled`, `shipped`, `unshipped`, etc.)
- badges en rendu pastel: fond clair + texte couleur forte
- couleurs proches de la maquette:
- `Open`: bleu
- `Physic`: violet
- `scheduled`: orange
- `shipped`: vert
- `unshipped`: rouge
Ce qui a ete implemente dans `tryton-sao.js` / `dist/tryton-sao.js`:
- ajout de `this.summary_bar` sous la toolbar AG Grid
- appel de `_update_summary_bar()` apres:
- changement de colonnes
- changement de filtre AG Grid
- refresh de `rowData`
- `display()`
- prise en compte de la hauteur de la barre KPI dans `_resize_grid_host()`
- helpers ajoutes/touches:
- `_get_visible_record_rows`
- `_get_summary_badge_columns`
- `_render_summary_item`
- `_update_summary_bar`
- `_normalize_badge_text_key`
- `_get_badge_color_order`
- `_get_badge_display_colors`
- `_append_badge_summary_items`
- `_get_summary_status_sources_from_grid`
- `_get_default_badge_summary_color`
- tentative de fallback:
- scanner les `columnDefs` AG Grid visibles qui ressemblent a des colonnes `status`, `state`, `type`, `shipping`, `shipment`, `open/physic`
- normaliser les valeurs type `emoji + Open` en `Open`
Ce qui a ete implemente dans `tryton-sao.css` / `dist/tryton-sao.css`:
- styles `.ag-grid-tree-summary`, `.ag-grid-summary-item`, `.ag-grid-summary-label`, `.ag-grid-summary-value`
- badges `.ag-grid-badge` passes en fond clair + texte plus fort
- couleurs dark mode pour la barre KPI
Etat observe par l'utilisateur:
- les badges de grille sont bien en rendu pastel pour `Shipping status`
- `Qt type` affiche encore `Open` / `Physic` en texte simple ou avec rendu partiel selon la config cote Tryton
- la barre KPI affiche toujours seulement:
- `Rows`
- `192`
- les compteurs par valeur de badge ne s'affichent pas, meme apres les fallbacks ajoutes
Conclusion importante:
- le probleme des totaux par valeur de badge n'est PAS resolu
- ne pas considerer les helpers KPI actuels comme valides
- prochaine reprise: instrumenter avant de retoucher encore
Hypotheses probables a verifier:
- `_get_summary_badge_columns()` retourne vide au runtime, ou retourne des colonnes sans valeurs exploitables
- `_get_summary_status_sources_from_grid()` ne voit pas les `columnDefs` attendus au moment ou `_update_summary_bar()` tourne
- `rows` contient bien 192 lignes, mais les proprietes lues (`row[name]`) ne correspondent pas aux `field/colId` visibles dans AG Grid
- les valeurs visibles dans les cellules peuvent venir du `valueGetter` / renderer, mais ne pas etre presentes dans le `rowData` sous le meme nom
- le `summary_bar` peut etre mis a jour avant que `_grid_columns` soit disponible ou avant que les `columnDefs` restaurees soient reappliquees
Trace a ajouter en premier a la prochaine session:
- une trace plate, appelee par exemple `[AGGRID][SUMMARY_BADGES_DEBUG_JSON]`, dans `_update_summary_bar()`
- champs minimum:
- `rowsLength`
- `rowKeysSample`: `Object.keys(rows[0] || {})`
- `rowSample`: valeurs des colonnes visibles sur 1 ou 2 lignes
- `summaryBadgeColumns`: noms/labels/attributs des colonnes retournees par `_get_summary_badge_columns()`
- `gridColumns`: `field`, `colId`, `headerName`, `cellClass`, `headerClass` de `this._grid_columns`
- `displayedColumns`: `getAllDisplayedColumns().map(col => col.getColId())`
- `statusSources`: retour de `_get_summary_status_sources_from_grid()`
- `summaryItemsBeforeRender`
Lignes Tryton/XML conseillees pour le rendu badges, mais a confirmer apres debug:
```xml
<field name="r_lot_status" widget="badge"
badge_colors="Open:#2563eb,Physic:#8b5cf6"/>
<field name="r_shipping_status" widget="badge"
badge_colors="scheduled:#f59e0b,shipped:#16a34a,unshipped:#ef4444"/>
```
Point de vigilance:
- si `Qt type` a un emoji/prefixe cote Python ou XML, SAO doit soit:
- rendre l'icone separement du texte de valeur
- soit normaliser la cle avant mapping couleur/KPI
- eviter de continuer a corriger "a l'oeil"; le prochain pas doit etre une trace runtime partageable.

View File

@@ -349,3 +349,12 @@ Traces a lire si le sujet revient:
- `_resize_grid_host()` peut poser des styles inline sur le `content_box`; quand `Sao.ScreenContainer.set()` repasse sur un widget non AG Grid, nettoyer `height`, `minHeight`, `flexBasis`, `flexShrink`, `width`, `maxWidth`
- regression typique si cet invariant casse: forme Purchase classique coupee en bas, onglets/lignes mal visibles, grande zone blanche ou largeur incorrecte apres toggle du menu gauche
- test obligatoire: Purchase form sans AG Grid principale avec menu gauche ouvert/cache, puis Lots Management/report AG Grid avec filtres hauts
### Point de reprise 2026-06-02 - KPI badges non resolu
- tentative en cours: barre KPI au-dessus de la grille avec compteurs par valeur de badge
- resultat actuel: la barre affiche `Rows 192`, mais PAS les compteurs `Open`, `Physic`, `scheduled`, `shipped`, `unshipped`
- le rendu pastel des badges `Shipping status` est visible, mais la logique de totaux par valeur ne marche pas
- fichiers touches: `tryton-sao.js`, `tryton-sao.css`, `dist/tryton-sao.js`, `dist/tryton-sao.css`
- avant toute nouvelle correction, ajouter une trace `[AGGRID][SUMMARY_BADGES_DEBUG_JSON]` dans `_update_summary_bar()`
- lire le bloc detaille dans `AG_GRID_PROGRESS.md > Session du 2026-06-02 - KPI badges Lots Management non resolu`

72
dist/tryton-sao.css vendored
View File

@@ -11613,6 +11613,54 @@ html[theme="default"] .ag-grid-tree-grouping-panel input[type="checkbox"]:after
margin-left: auto;
}
.ag-grid-tree-summary {
display: flex;
align-items: stretch;
flex-wrap: wrap;
gap: 0;
min-height: 48px;
margin: 0 0 8px;
border: 1px solid #dfe8e3;
border-radius: 6px;
background: #ffffff;
box-shadow: 0 1px 4px rgba(53, 81, 84, 0.06);
overflow: hidden;
}
.ag-grid-summary-item {
display: flex;
flex: 0 1 150px;
min-width: 120px;
max-width: 190px;
flex-direction: column;
justify-content: center;
gap: 3px;
padding: 8px 18px;
border-right: 1px solid #e7efeb;
}
.ag-grid-summary-item:last-child {
border-right: none;
}
.ag-grid-summary-label {
min-width: 0;
color: #64748b;
font-size: 11px;
font-weight: 600;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ag-grid-summary-value {
color: #145369;
font-size: 18px;
font-weight: 700;
line-height: 1.15;
}
.ag-grid-tree-host {
flex: 0 0 auto;
min-height: 520px;
@@ -11794,10 +11842,10 @@ html[theme="default"] .ag-grid-tree-grouping-panel input[type="checkbox"]:after
min-height: 22px;
padding: 2px 10px;
border-radius: 999px;
background: #6c757d;
color: #f7fbf9;
background: #eef3f2;
color: #355154;
font-size: 12px;
font-weight: 500;
font-weight: 700;
line-height: 1.35;
white-space: nowrap;
overflow: hidden;
@@ -12769,6 +12817,24 @@ html[data-theme-mode="dark"] .ag-grid-filter-logic-operator {
color: #8fe1d8;
}
html[data-theme-mode="dark"] .ag-grid-tree-summary {
background: #151f1e;
border-color: #2a403d;
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.24);
}
html[data-theme-mode="dark"] .ag-grid-summary-item {
border-right-color: #243735;
}
html[data-theme-mode="dark"] .ag-grid-summary-label {
color: #aebfba;
}
html[data-theme-mode="dark"] .ag-grid-summary-value {
color: #8fe1d8;
}
html[data-theme-mode="dark"] .form-tolerance-gauge {
color: #dce7e2;
}

307
dist/tryton-sao.js vendored
View File

@@ -22624,6 +22624,9 @@ function eval_pyson(value){
this.status = jQuery('<div/>', {
'class': 'ag-grid-tree-status text-muted',
}).appendTo(this.toolbar);
this.summary_bar = jQuery('<div/>', {
'class': 'ag-grid-tree-summary',
}).appendTo(this.el);
this.grid_host = jQuery('<div/>', {
'class': 'ag-grid-theme-alpine ag-grid-tree-host',
}).appendTo(this.el);
@@ -22946,6 +22949,7 @@ function eval_pyson(value){
if (!options.skipLayoutRefresh) {
this._schedule_grid_layout_refresh();
}
this._update_summary_bar();
},
_on_grid_column_resized: function(params) {
this._trace_aggrid_column_resize('event', params);
@@ -24629,6 +24633,7 @@ function eval_pyson(value){
onFilterChanged: () => {
this._schedule_state_save();
this._sync_selection_header_checkbox();
this._update_summary_bar();
},
onColumnMoved: this._on_grid_column_layout_changed.bind(this),
onColumnPinned: this._on_grid_column_layout_changed.bind(this),
@@ -25220,6 +25225,7 @@ function eval_pyson(value){
this._log_sao_value_filter_empty_result(this.group.slice());
}
this._resize_grid_host();
this._update_summary_bar();
this._schedule_grid_layout_refresh([0, 80]);
});
},
@@ -25258,6 +25264,7 @@ function eval_pyson(value){
this._refresh_grid_layout_after_row_data(row_data);
this._sync_selection(selected);
this._update_copy_state();
this._update_summary_bar();
},
_toggle_sao_value_filter_mode: function() {
this._set_sao_value_filter_mode(
@@ -26061,6 +26068,17 @@ function eval_pyson(value){
});
return parsed;
},
_normalize_badge_text_key: function(text) {
text = text === null || text === undefined ? '' : String(text);
return text.replace(/^[^A-Za-z0-9]+/, '').trim();
},
_get_badge_color_order: function(colors) {
return (colors || '').split(',').map(item => {
var pair = item.split(':');
return pair.length < 2 ? '' :
this._normalize_badge_text_key(pair.shift());
}).filter(Boolean);
},
_get_record_field_value: function(record, name) {
if (!record || !name) {
return '';
@@ -26084,34 +26102,73 @@ function eval_pyson(value){
color = color === null || color === undefined ? '' :
String(color).trim();
var palette = {
red: '#e06a6a',
danger: '#e06a6a',
coral: '#e06a6a',
orange: '#d99a2b',
warning: '#d99a2b',
amber: '#d99a2b',
yellow: '#f1c40f',
green: '#2b8c87',
success: '#2b8c87',
teal: '#2b8c87',
blue: '#57c1b9',
info: '#57c1b9',
red: '#ef4444',
danger: '#ef4444',
coral: '#ef4444',
orange: '#f59e0b',
warning: '#f59e0b',
amber: '#f59e0b',
yellow: '#eab308',
green: '#16a34a',
success: '#16a34a',
teal: '#0f766e',
blue: '#2563eb',
info: '#2563eb',
purple: '#8b5cf6',
violet: '#8b5cf6',
gray: '#6c757d',
grey: '#6c757d',
muted: '#6c757d',
};
return palette[color.toLowerCase()] || color;
},
_hex_to_rgb_badge_color: function(color) {
color = this._normalize_badge_color(color);
var match = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color || '');
if (!match) {
return null;
}
var hex = match[1];
if (hex.length == 3) {
hex = hex.split('').map(part => part + part).join('');
}
return {
r: parseInt(hex.slice(0, 2), 16),
g: parseInt(hex.slice(2, 4), 16),
b: parseInt(hex.slice(4, 6), 16),
};
},
_rgb_to_badge_color: function(rgb) {
var part = value => Math.max(0, Math.min(255, Math.round(value)))
.toString(16).padStart(2, '0');
return '#' + part(rgb.r) + part(rgb.g) + part(rgb.b);
},
_mix_badge_color: function(color, mix_with, weight) {
var source = this._hex_to_rgb_badge_color(color);
var target = this._hex_to_rgb_badge_color(mix_with);
if (!source || !target) {
return '';
}
return this._rgb_to_badge_color({
r: source.r * (1 - weight) + target.r * weight,
g: source.g * (1 - weight) + target.g * weight,
b: source.b * (1 - weight) + target.b * weight,
});
},
_get_badge_colors: function(column, record, text) {
var attributes = column.attributes || {};
var color = '';
var text_color = '';
var mapped = this._parse_badge_colors(attributes.badge_colors);
var normalized_text = this._normalize_badge_text_key(text);
if (Object.prototype.hasOwnProperty.call(mapped, text)) {
color = mapped[text];
} else if (Object.prototype.hasOwnProperty.call(
mapped, String(text).toLowerCase())) {
color = mapped[String(text).toLowerCase()];
mapped, normalized_text)) {
color = mapped[normalized_text];
} else if (Object.prototype.hasOwnProperty.call(
mapped, String(normalized_text).toLowerCase())) {
color = mapped[String(normalized_text).toLowerCase()];
}
if (!color && attributes.badge_color_field) {
color = this._get_record_field_value(
@@ -26138,6 +26195,19 @@ function eval_pyson(value){
color: this._normalize_badge_color(text_color),
};
},
_get_badge_display_colors: function(column, record, text) {
var colors = this._get_badge_colors(column, record, text);
var accent = colors.color || colors.background;
if (!accent) {
return colors;
}
return {
background: this._mix_badge_color(accent, '#ffffff', 0.86) ||
colors.background,
color: colors.color || accent,
accent: accent,
};
},
_render_badge_cell: function(column, record) {
var text = this._get_column_text_value(column, record);
var cell = jQuery('<div/>', {
@@ -26153,7 +26223,7 @@ function eval_pyson(value){
if (parseInt(column.attributes.badge_bold || '0', 10)) {
badge.addClass('ag-grid-badge-bold');
}
var colors = this._get_badge_colors(column, record, text);
var colors = this._get_badge_display_colors(column, record, text);
if (colors.background) {
badge.css('background-color', colors.background);
}
@@ -26621,6 +26691,205 @@ function eval_pyson(value){
this.copy_button.prop('disabled', !this.selected_records.length);
this.status.text(String(this.group.length) + ' records');
},
_get_visible_record_rows: function() {
var rows = [];
if (!this.gridApi) {
return rows;
}
var collect = node => {
if (node && node.data && node.data.__kind == 'record') {
rows.push(node.data);
}
};
if (this.gridApi.forEachNodeAfterFilterAndSort) {
this.gridApi.forEachNodeAfterFilterAndSort(collect);
} else if (this.gridApi.forEachNodeAfterFilter) {
this.gridApi.forEachNodeAfterFilter(collect);
} else {
(this._last_row_data || []).forEach(row => {
if (row && row.__kind == 'record') {
rows.push(row);
}
});
}
return rows;
},
_get_summary_badge_columns: function() {
var visible_columns = {};
var column_api = this.gridColumnApi || this.gridApi;
if (column_api && column_api.getAllDisplayedColumns) {
column_api.getAllDisplayedColumns().forEach(column => {
if (column && column.getColId) {
visible_columns[column.getColId()] = true;
}
});
}
return this.columns.filter(column => {
var name = column.attributes && column.attributes.name;
var label = column.attributes &&
(column.attributes.string || column.attributes.name) || '';
var is_status_column = /(^|[_\s-])(status|state|type)([_\s-]|$)/i
.test(String(name || '') + ' ' + String(label || ''));
if (!name || (!this._is_badge_column(column) &&
!is_status_column)) {
return false;
}
return !Object.keys(visible_columns).length ||
visible_columns[name];
});
},
_render_summary_item: function(label, value, color) {
var item = jQuery('<div/>', {
'class': 'ag-grid-summary-item',
});
jQuery('<span/>', {
'class': 'ag-grid-summary-label',
'text': label,
}).appendTo(item);
var value_el = jQuery('<span/>', {
'class': 'ag-grid-summary-value',
'text': value,
}).appendTo(item);
if (color) {
value_el.css('color', color);
}
return item;
},
_get_default_badge_summary_color: function(value) {
var key = String(value || '').toLowerCase();
var palette = {
open: '#2563eb',
physic: '#8b5cf6',
physical: '#8b5cf6',
scheduled: '#f59e0b',
shipped: '#16a34a',
unshipped: '#ef4444',
done: '#16a34a',
cancelled: '#ef4444',
canceled: '#ef4444',
draft: '#64748b',
quote: '#2563eb',
confirmed: '#16a34a',
processing: '#f59e0b',
};
return palette[key] || '#0f766e';
},
_append_badge_summary_items: function(summary_items, rows, column, source) {
var name = source && source.name ||
column && column.attributes && column.attributes.name;
if (!name) {
return false;
}
var counts = {};
var samples = {};
var order = [];
var empty = Sao.i18n.gettext('(Empty)');
rows.forEach(row => {
var value = row[name] ?
this._normalize_badge_text_key(row[name]) : empty;
if (value == empty) {
return;
}
if (!Object.prototype.hasOwnProperty.call(counts, value)) {
counts[value] = 0;
samples[value] = row;
order.push(value);
}
counts[value] += 1;
});
var color_order = this._get_badge_color_order(
column && column.attributes && column.attributes.badge_colors);
var color_order_lower = color_order.map(value =>
String(value).toLowerCase());
order.sort((a, b) => {
var a_index = color_order.indexOf(a);
var b_index = color_order.indexOf(b);
if (a_index < 0) {
a_index = color_order_lower.indexOf(
String(a).toLowerCase());
}
if (b_index < 0) {
b_index = color_order_lower.indexOf(
String(b).toLowerCase());
}
if (a_index >= 0 || b_index >= 0) {
return (a_index < 0 ? 999 : a_index) -
(b_index < 0 ? 999 : b_index);
}
return counts[b] - counts[a] ||
String(a).localeCompare(String(b), undefined, {
numeric: true,
sensitivity: 'base',
});
});
order.forEach(value => {
var colors = column ? this._get_badge_display_colors(
column,
samples[value] && samples[value].__record,
value) : {};
summary_items.push({
label: value,
count: counts[value],
color: colors.color || colors.accent ||
this._get_default_badge_summary_color(value),
});
});
return Boolean(order.length);
},
_get_summary_status_sources_from_grid: function() {
var sources = [];
var seen = {};
(this._grid_columns || []).forEach(col_def => {
var name = col_def && col_def.field || col_def && col_def.colId;
if (!name || name == '__selection__' || seen[name]) {
return;
}
var label = col_def.headerName || name;
if (!/(status|state|type|shipping|shipment|open\/physic)/i
.test(String(name) + ' ' + String(label))) {
return;
}
seen[name] = true;
sources.push({
name: name,
label: label,
});
});
return sources;
},
_update_summary_bar: function() {
if (!this.summary_bar || !this.summary_bar.length) {
return;
}
var rows = this._get_visible_record_rows();
var locale = Sao.i18n.BC47(Sao.i18n.getlang());
this.summary_bar.empty();
this.summary_bar.append(this._render_summary_item(
Sao.i18n.gettext('Rows'),
rows.length.toLocaleString(locale),
'#145369'));
var summary_items = [];
var summarized_names = {};
this._get_summary_badge_columns().forEach(column => {
if (this._append_badge_summary_items(
summary_items, rows, column)) {
summarized_names[column.attributes.name] = true;
}
});
this._get_summary_status_sources_from_grid().forEach(source => {
if (summarized_names[source.name]) {
return;
}
this._append_badge_summary_items(
summary_items, rows, null, source);
});
summary_items.forEach(item => {
this.summary_bar.append(this._render_summary_item(
item.label,
item.count.toLocaleString(locale),
item.color));
});
},
on_copy: function() {
var data = [];
this.selected_records.forEach(record => {
@@ -26739,6 +27008,7 @@ function eval_pyson(value){
this._update_layout_button();
this._render_filter_logic_panel();
this._update_copy_state();
this._update_summary_bar();
if (this._has_active_sao_value_filters()) {
this._apply_sao_value_filters({refreshRowData: false});
}
@@ -26765,6 +27035,8 @@ function eval_pyson(value){
var screen_container = this.grid_host.closest('.screen-container');
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 ?
Math.ceil(this.summary_bar.outerHeight(true)) : 0;
var external_scroll_height = 20;
var bottom_margin = 96;
var available = window.innerHeight - rect.top -
@@ -26774,7 +27046,7 @@ function eval_pyson(value){
available = Math.min(
available,
window.innerHeight - content_rect.top - toolbar_height -
external_scroll_height - bottom_margin);
summary_height - external_scroll_height - bottom_margin);
}
var compact_min_height = 260;
var readable_min_height = 420;
@@ -26805,7 +27077,8 @@ function eval_pyson(value){
width: available_width + 'px',
maxWidth: available_width + 'px',
});
var container_height = height + toolbar_height + external_scroll_height;
var container_height = height + toolbar_height + summary_height +
external_scroll_height;
this.el.css({
height: container_height + 'px',
minHeight: container_height + 'px',

View File

@@ -11613,6 +11613,54 @@ html[theme="default"] .ag-grid-tree-grouping-panel input[type="checkbox"]:after
margin-left: auto;
}
.ag-grid-tree-summary {
display: flex;
align-items: stretch;
flex-wrap: wrap;
gap: 0;
min-height: 48px;
margin: 0 0 8px;
border: 1px solid #dfe8e3;
border-radius: 6px;
background: #ffffff;
box-shadow: 0 1px 4px rgba(53, 81, 84, 0.06);
overflow: hidden;
}
.ag-grid-summary-item {
display: flex;
flex: 0 1 150px;
min-width: 120px;
max-width: 190px;
flex-direction: column;
justify-content: center;
gap: 3px;
padding: 8px 18px;
border-right: 1px solid #e7efeb;
}
.ag-grid-summary-item:last-child {
border-right: none;
}
.ag-grid-summary-label {
min-width: 0;
color: #64748b;
font-size: 11px;
font-weight: 600;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ag-grid-summary-value {
color: #145369;
font-size: 18px;
font-weight: 700;
line-height: 1.15;
}
.ag-grid-tree-host {
flex: 0 0 auto;
min-height: 520px;
@@ -11794,10 +11842,10 @@ html[theme="default"] .ag-grid-tree-grouping-panel input[type="checkbox"]:after
min-height: 22px;
padding: 2px 10px;
border-radius: 999px;
background: #6c757d;
color: #f7fbf9;
background: #eef3f2;
color: #355154;
font-size: 12px;
font-weight: 500;
font-weight: 700;
line-height: 1.35;
white-space: nowrap;
overflow: hidden;
@@ -12769,6 +12817,24 @@ html[data-theme-mode="dark"] .ag-grid-filter-logic-operator {
color: #8fe1d8;
}
html[data-theme-mode="dark"] .ag-grid-tree-summary {
background: #151f1e;
border-color: #2a403d;
box-shadow: 0 8px 22px rgba(0, 0, 0, 0.24);
}
html[data-theme-mode="dark"] .ag-grid-summary-item {
border-right-color: #243735;
}
html[data-theme-mode="dark"] .ag-grid-summary-label {
color: #aebfba;
}
html[data-theme-mode="dark"] .ag-grid-summary-value {
color: #8fe1d8;
}
html[data-theme-mode="dark"] .form-tolerance-gauge {
color: #dce7e2;
}

View File

@@ -22624,6 +22624,9 @@ function eval_pyson(value){
this.status = jQuery('<div/>', {
'class': 'ag-grid-tree-status text-muted',
}).appendTo(this.toolbar);
this.summary_bar = jQuery('<div/>', {
'class': 'ag-grid-tree-summary',
}).appendTo(this.el);
this.grid_host = jQuery('<div/>', {
'class': 'ag-grid-theme-alpine ag-grid-tree-host',
}).appendTo(this.el);
@@ -22946,6 +22949,7 @@ function eval_pyson(value){
if (!options.skipLayoutRefresh) {
this._schedule_grid_layout_refresh();
}
this._update_summary_bar();
},
_on_grid_column_resized: function(params) {
this._trace_aggrid_column_resize('event', params);
@@ -24629,6 +24633,7 @@ function eval_pyson(value){
onFilterChanged: () => {
this._schedule_state_save();
this._sync_selection_header_checkbox();
this._update_summary_bar();
},
onColumnMoved: this._on_grid_column_layout_changed.bind(this),
onColumnPinned: this._on_grid_column_layout_changed.bind(this),
@@ -25220,6 +25225,7 @@ function eval_pyson(value){
this._log_sao_value_filter_empty_result(this.group.slice());
}
this._resize_grid_host();
this._update_summary_bar();
this._schedule_grid_layout_refresh([0, 80]);
});
},
@@ -25258,6 +25264,7 @@ function eval_pyson(value){
this._refresh_grid_layout_after_row_data(row_data);
this._sync_selection(selected);
this._update_copy_state();
this._update_summary_bar();
},
_toggle_sao_value_filter_mode: function() {
this._set_sao_value_filter_mode(
@@ -26061,6 +26068,17 @@ function eval_pyson(value){
});
return parsed;
},
_normalize_badge_text_key: function(text) {
text = text === null || text === undefined ? '' : String(text);
return text.replace(/^[^A-Za-z0-9]+/, '').trim();
},
_get_badge_color_order: function(colors) {
return (colors || '').split(',').map(item => {
var pair = item.split(':');
return pair.length < 2 ? '' :
this._normalize_badge_text_key(pair.shift());
}).filter(Boolean);
},
_get_record_field_value: function(record, name) {
if (!record || !name) {
return '';
@@ -26084,34 +26102,73 @@ function eval_pyson(value){
color = color === null || color === undefined ? '' :
String(color).trim();
var palette = {
red: '#e06a6a',
danger: '#e06a6a',
coral: '#e06a6a',
orange: '#d99a2b',
warning: '#d99a2b',
amber: '#d99a2b',
yellow: '#f1c40f',
green: '#2b8c87',
success: '#2b8c87',
teal: '#2b8c87',
blue: '#57c1b9',
info: '#57c1b9',
red: '#ef4444',
danger: '#ef4444',
coral: '#ef4444',
orange: '#f59e0b',
warning: '#f59e0b',
amber: '#f59e0b',
yellow: '#eab308',
green: '#16a34a',
success: '#16a34a',
teal: '#0f766e',
blue: '#2563eb',
info: '#2563eb',
purple: '#8b5cf6',
violet: '#8b5cf6',
gray: '#6c757d',
grey: '#6c757d',
muted: '#6c757d',
};
return palette[color.toLowerCase()] || color;
},
_hex_to_rgb_badge_color: function(color) {
color = this._normalize_badge_color(color);
var match = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(color || '');
if (!match) {
return null;
}
var hex = match[1];
if (hex.length == 3) {
hex = hex.split('').map(part => part + part).join('');
}
return {
r: parseInt(hex.slice(0, 2), 16),
g: parseInt(hex.slice(2, 4), 16),
b: parseInt(hex.slice(4, 6), 16),
};
},
_rgb_to_badge_color: function(rgb) {
var part = value => Math.max(0, Math.min(255, Math.round(value)))
.toString(16).padStart(2, '0');
return '#' + part(rgb.r) + part(rgb.g) + part(rgb.b);
},
_mix_badge_color: function(color, mix_with, weight) {
var source = this._hex_to_rgb_badge_color(color);
var target = this._hex_to_rgb_badge_color(mix_with);
if (!source || !target) {
return '';
}
return this._rgb_to_badge_color({
r: source.r * (1 - weight) + target.r * weight,
g: source.g * (1 - weight) + target.g * weight,
b: source.b * (1 - weight) + target.b * weight,
});
},
_get_badge_colors: function(column, record, text) {
var attributes = column.attributes || {};
var color = '';
var text_color = '';
var mapped = this._parse_badge_colors(attributes.badge_colors);
var normalized_text = this._normalize_badge_text_key(text);
if (Object.prototype.hasOwnProperty.call(mapped, text)) {
color = mapped[text];
} else if (Object.prototype.hasOwnProperty.call(
mapped, String(text).toLowerCase())) {
color = mapped[String(text).toLowerCase()];
mapped, normalized_text)) {
color = mapped[normalized_text];
} else if (Object.prototype.hasOwnProperty.call(
mapped, String(normalized_text).toLowerCase())) {
color = mapped[String(normalized_text).toLowerCase()];
}
if (!color && attributes.badge_color_field) {
color = this._get_record_field_value(
@@ -26138,6 +26195,19 @@ function eval_pyson(value){
color: this._normalize_badge_color(text_color),
};
},
_get_badge_display_colors: function(column, record, text) {
var colors = this._get_badge_colors(column, record, text);
var accent = colors.color || colors.background;
if (!accent) {
return colors;
}
return {
background: this._mix_badge_color(accent, '#ffffff', 0.86) ||
colors.background,
color: colors.color || accent,
accent: accent,
};
},
_render_badge_cell: function(column, record) {
var text = this._get_column_text_value(column, record);
var cell = jQuery('<div/>', {
@@ -26153,7 +26223,7 @@ function eval_pyson(value){
if (parseInt(column.attributes.badge_bold || '0', 10)) {
badge.addClass('ag-grid-badge-bold');
}
var colors = this._get_badge_colors(column, record, text);
var colors = this._get_badge_display_colors(column, record, text);
if (colors.background) {
badge.css('background-color', colors.background);
}
@@ -26621,6 +26691,205 @@ function eval_pyson(value){
this.copy_button.prop('disabled', !this.selected_records.length);
this.status.text(String(this.group.length) + ' records');
},
_get_visible_record_rows: function() {
var rows = [];
if (!this.gridApi) {
return rows;
}
var collect = node => {
if (node && node.data && node.data.__kind == 'record') {
rows.push(node.data);
}
};
if (this.gridApi.forEachNodeAfterFilterAndSort) {
this.gridApi.forEachNodeAfterFilterAndSort(collect);
} else if (this.gridApi.forEachNodeAfterFilter) {
this.gridApi.forEachNodeAfterFilter(collect);
} else {
(this._last_row_data || []).forEach(row => {
if (row && row.__kind == 'record') {
rows.push(row);
}
});
}
return rows;
},
_get_summary_badge_columns: function() {
var visible_columns = {};
var column_api = this.gridColumnApi || this.gridApi;
if (column_api && column_api.getAllDisplayedColumns) {
column_api.getAllDisplayedColumns().forEach(column => {
if (column && column.getColId) {
visible_columns[column.getColId()] = true;
}
});
}
return this.columns.filter(column => {
var name = column.attributes && column.attributes.name;
var label = column.attributes &&
(column.attributes.string || column.attributes.name) || '';
var is_status_column = /(^|[_\s-])(status|state|type)([_\s-]|$)/i
.test(String(name || '') + ' ' + String(label || ''));
if (!name || (!this._is_badge_column(column) &&
!is_status_column)) {
return false;
}
return !Object.keys(visible_columns).length ||
visible_columns[name];
});
},
_render_summary_item: function(label, value, color) {
var item = jQuery('<div/>', {
'class': 'ag-grid-summary-item',
});
jQuery('<span/>', {
'class': 'ag-grid-summary-label',
'text': label,
}).appendTo(item);
var value_el = jQuery('<span/>', {
'class': 'ag-grid-summary-value',
'text': value,
}).appendTo(item);
if (color) {
value_el.css('color', color);
}
return item;
},
_get_default_badge_summary_color: function(value) {
var key = String(value || '').toLowerCase();
var palette = {
open: '#2563eb',
physic: '#8b5cf6',
physical: '#8b5cf6',
scheduled: '#f59e0b',
shipped: '#16a34a',
unshipped: '#ef4444',
done: '#16a34a',
cancelled: '#ef4444',
canceled: '#ef4444',
draft: '#64748b',
quote: '#2563eb',
confirmed: '#16a34a',
processing: '#f59e0b',
};
return palette[key] || '#0f766e';
},
_append_badge_summary_items: function(summary_items, rows, column, source) {
var name = source && source.name ||
column && column.attributes && column.attributes.name;
if (!name) {
return false;
}
var counts = {};
var samples = {};
var order = [];
var empty = Sao.i18n.gettext('(Empty)');
rows.forEach(row => {
var value = row[name] ?
this._normalize_badge_text_key(row[name]) : empty;
if (value == empty) {
return;
}
if (!Object.prototype.hasOwnProperty.call(counts, value)) {
counts[value] = 0;
samples[value] = row;
order.push(value);
}
counts[value] += 1;
});
var color_order = this._get_badge_color_order(
column && column.attributes && column.attributes.badge_colors);
var color_order_lower = color_order.map(value =>
String(value).toLowerCase());
order.sort((a, b) => {
var a_index = color_order.indexOf(a);
var b_index = color_order.indexOf(b);
if (a_index < 0) {
a_index = color_order_lower.indexOf(
String(a).toLowerCase());
}
if (b_index < 0) {
b_index = color_order_lower.indexOf(
String(b).toLowerCase());
}
if (a_index >= 0 || b_index >= 0) {
return (a_index < 0 ? 999 : a_index) -
(b_index < 0 ? 999 : b_index);
}
return counts[b] - counts[a] ||
String(a).localeCompare(String(b), undefined, {
numeric: true,
sensitivity: 'base',
});
});
order.forEach(value => {
var colors = column ? this._get_badge_display_colors(
column,
samples[value] && samples[value].__record,
value) : {};
summary_items.push({
label: value,
count: counts[value],
color: colors.color || colors.accent ||
this._get_default_badge_summary_color(value),
});
});
return Boolean(order.length);
},
_get_summary_status_sources_from_grid: function() {
var sources = [];
var seen = {};
(this._grid_columns || []).forEach(col_def => {
var name = col_def && col_def.field || col_def && col_def.colId;
if (!name || name == '__selection__' || seen[name]) {
return;
}
var label = col_def.headerName || name;
if (!/(status|state|type|shipping|shipment|open\/physic)/i
.test(String(name) + ' ' + String(label))) {
return;
}
seen[name] = true;
sources.push({
name: name,
label: label,
});
});
return sources;
},
_update_summary_bar: function() {
if (!this.summary_bar || !this.summary_bar.length) {
return;
}
var rows = this._get_visible_record_rows();
var locale = Sao.i18n.BC47(Sao.i18n.getlang());
this.summary_bar.empty();
this.summary_bar.append(this._render_summary_item(
Sao.i18n.gettext('Rows'),
rows.length.toLocaleString(locale),
'#145369'));
var summary_items = [];
var summarized_names = {};
this._get_summary_badge_columns().forEach(column => {
if (this._append_badge_summary_items(
summary_items, rows, column)) {
summarized_names[column.attributes.name] = true;
}
});
this._get_summary_status_sources_from_grid().forEach(source => {
if (summarized_names[source.name]) {
return;
}
this._append_badge_summary_items(
summary_items, rows, null, source);
});
summary_items.forEach(item => {
this.summary_bar.append(this._render_summary_item(
item.label,
item.count.toLocaleString(locale),
item.color));
});
},
on_copy: function() {
var data = [];
this.selected_records.forEach(record => {
@@ -26739,6 +27008,7 @@ function eval_pyson(value){
this._update_layout_button();
this._render_filter_logic_panel();
this._update_copy_state();
this._update_summary_bar();
if (this._has_active_sao_value_filters()) {
this._apply_sao_value_filters({refreshRowData: false});
}
@@ -26765,6 +27035,8 @@ function eval_pyson(value){
var screen_container = this.grid_host.closest('.screen-container');
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 ?
Math.ceil(this.summary_bar.outerHeight(true)) : 0;
var external_scroll_height = 20;
var bottom_margin = 96;
var available = window.innerHeight - rect.top -
@@ -26774,7 +27046,7 @@ function eval_pyson(value){
available = Math.min(
available,
window.innerHeight - content_rect.top - toolbar_height -
external_scroll_height - bottom_margin);
summary_height - external_scroll_height - bottom_margin);
}
var compact_min_height = 260;
var readable_min_height = 420;
@@ -26805,7 +27077,8 @@ function eval_pyson(value){
width: available_width + 'px',
maxWidth: available_width + 'px',
});
var container_height = height + toolbar_height + external_scroll_height;
var container_height = height + toolbar_height + summary_height +
external_scroll_height;
this.el.css({
height: container_height + 'px',
minHeight: container_height + 'px',