overview-components 1.1.145 → 1.1.147

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,6 +23,17 @@ import '../assets/illustration/not-found.js';
23
23
  import { tooltip } from '../shared/simple-tooltip.js';
24
24
  import '../shared/lit-tooltip.js';
25
25
  import '../shared/lit-progress-bar.js';
26
+ import '../shared/lit-modal.js';
27
+ import '../shared/lit-modal-header.js';
28
+ import '../shared/lit-modal-body.js';
29
+ import '../shared/lit-modal-footer.js';
30
+ import '../shared/lit-select-field.js';
31
+ import '../shared/lit-select.js';
32
+ import '../shared/lit-label.js';
33
+ import '../shared/lit-icon-button.js';
34
+ import '../shared/lit-toggle.js';
35
+ import '../shared/lit-text-field.js';
36
+ import './lit-tabs-overview.js';
26
37
  // utils
27
38
  import { formatDate } from '../utils/date.js';
28
39
  import { formatCurrency, formatDecimal } from '../utils/formatNumber.js';
@@ -37,6 +48,7 @@ export class LitCaseVariablesTab extends LitElement {
37
48
  this.variables = [];
38
49
  this.hideTabWhen = false;
39
50
  this.userLang = 'cs';
51
+ this.allowedLang = ['cs'];
40
52
  this.dateFormat = null;
41
53
  this.isLoading = false;
42
54
  this.enableSettings = false;
@@ -47,6 +59,32 @@ export class LitCaseVariablesTab extends LitElement {
47
59
  this.isOpen = false;
48
60
  this.filterText = '';
49
61
  this.isMobile = window.innerWidth <= 600;
62
+ this.expertModeModalOpen = false;
63
+ this.expertModeCell = null;
64
+ this.expertModeConfig = {
65
+ enabled: false,
66
+ leftType: 'column',
67
+ leftSource: '',
68
+ leftValue: '',
69
+ leftAggregation: 'sum',
70
+ operation: 'none',
71
+ rightType: 'variable',
72
+ rightSource: '',
73
+ rightValue: '',
74
+ rightAggregation: 'sum',
75
+ headerName: '',
76
+ headerNameMutations: {},
77
+ pmEnabled: false,
78
+ pmLeftType: 'column',
79
+ pmLeftSource: '',
80
+ pmLeftValue: '',
81
+ pmLeftAggregation: 'sum',
82
+ pmOperation: 'none',
83
+ pmRightType: 'variable',
84
+ pmRightSource: '',
85
+ pmRightValue: '',
86
+ pmRightAggregation: 'sum',
87
+ };
50
88
  this._resizeListener = this.handleResize.bind(this);
51
89
  this.sortableInstances = [];
52
90
  this.settingsPanes = new Map();
@@ -235,8 +273,9 @@ export class LitCaseVariablesTab extends LitElement {
235
273
  </div> `;
236
274
  };
237
275
  this.getCellProgress = (cell) => {
238
- // Convert string to number if needed, then convert decimal value (0.0-1.0) to percentage (0-100)
239
276
  const numericValue = this.parseProgressValue(cell?.value);
277
+ const progressMax = this.resolveProgressMax(cell);
278
+ const percentage = progressMax > 0 ? (numericValue / progressMax) * 100 : 0;
240
279
  const progressColor = this.computeProgressColor(cell);
241
280
  return html `<div>${this.getHeader(cell)}</div>
242
281
  <div
@@ -244,7 +283,7 @@ export class LitCaseVariablesTab extends LitElement {
244
283
  class="process-data-value"
245
284
  >
246
285
  <lit-progress-bar
247
- .progress="${numericValue}"
286
+ .progress="${percentage}"
248
287
  .color="${progressColor}"
249
288
  label=""
250
289
  style="width: 100%;"
@@ -291,9 +330,11 @@ export class LitCaseVariablesTab extends LitElement {
291
330
  : '';
292
331
  break;
293
332
  case 'progress':
294
- // For progress, show percentage value - convert string to number if needed
333
+ // For progress, show percentage value based on progressMax variable
295
334
  const progressNumeric = this.parseProgressValue(cell?.value);
296
- value = `${Math.round(progressNumeric * 100)}%`;
335
+ const progressMaxVal = this.resolveProgressMax(cell);
336
+ const progressPercent = progressMaxVal > 0 ? (progressNumeric / progressMaxVal) * 100 : 0;
337
+ value = `${Math.round(progressPercent)}%`;
297
338
  break;
298
339
  case 'checkbox':
299
340
  value = this.parseBooleanValue(cell?.value) ? msg('Ano') : msg('Ne');
@@ -543,6 +584,7 @@ export class LitCaseVariablesTab extends LitElement {
543
584
  dynamicLink: cell.dynamicLink || '',
544
585
  numberOfDecimal: cell.numberOfDecimal ?? 2,
545
586
  progressColor: cell.progressColor || 'primary',
587
+ progressMax: cell.progressMax ?? 100,
546
588
  buttonVariant: cell.buttonVariant || 'contained',
547
589
  buttonColor: cell.buttonColor || 'primary',
548
590
  buttonFullWidth: cell.buttonFullWidth ?? false,
@@ -1458,6 +1500,11 @@ export class LitCaseVariablesTab extends LitElement {
1458
1500
  this.hideOperatorInputs.set(hideOperatorKey, hideOperatorInput);
1459
1501
  // Hide condition value - dynamic input based on variable type
1460
1502
  this.addHideConditionValueInput(hideFolder, config, cell, config.hideConditionVariable);
1503
+ // Expert mode button
1504
+ const expertBtn = pane.addButton({ title: msg('Expertní mód') });
1505
+ expertBtn.on('click', () => {
1506
+ this.openExpertModeModal(cell);
1507
+ });
1461
1508
  // Add delete button
1462
1509
  const deleteBtn = pane.addButton({ title: msg('Smazat buňku') });
1463
1510
  deleteBtn.on('click', () => {
@@ -1773,6 +1820,15 @@ export class LitCaseVariablesTab extends LitElement {
1773
1820
  this.colorizeVariantSelect(valueVariantInput, valueVariantOptions, config, 'valueVariant');
1774
1821
  }
1775
1822
  addProgressControls(pane, config, cell) {
1823
+ // Progress Max — numeric value for 100%
1824
+ const progressMaxInput = pane.addBinding(config, 'progressMax', {
1825
+ label: msg('Hodnota 100%'),
1826
+ min: 1,
1827
+ step: 1,
1828
+ });
1829
+ progressMaxInput.on('change', (ev) => {
1830
+ this.setProgressMax(cell, ev.value);
1831
+ });
1776
1832
  // Progress Color
1777
1833
  const progressColorOptions = [
1778
1834
  { text: msg('Primární'), value: 'primary' },
@@ -2343,6 +2399,15 @@ export class LitCaseVariablesTab extends LitElement {
2343
2399
  });
2344
2400
  this.handleSettingsChanged(this.rows);
2345
2401
  }
2402
+ setProgressMax(cell, progressMax) {
2403
+ this.rows = this.rows.map((c) => {
2404
+ if (c.field === cell.field) {
2405
+ return { ...c, progressMax };
2406
+ }
2407
+ return c;
2408
+ });
2409
+ this.handleSettingsChanged(this.rows);
2410
+ }
2346
2411
  setProgressColor(cell, progressColor) {
2347
2412
  this.rows = this.rows.map((c) => {
2348
2413
  if (c.field === cell.field) {
@@ -2461,6 +2526,600 @@ export class LitCaseVariablesTab extends LitElement {
2461
2526
  }
2462
2527
  return String(rawValue);
2463
2528
  }
2529
+ getAllDataGridRefs() {
2530
+ if (this.dataGridRefs?.length)
2531
+ return this.dataGridRefs;
2532
+ if (this.dataGridRef)
2533
+ return [{ id: '_default', label: 'DataGrid', ref: this.dataGridRef }];
2534
+ return [];
2535
+ }
2536
+ hasAnyDataGridRef() {
2537
+ return this.getAllDataGridRefs().length > 0;
2538
+ }
2539
+ getDataGridBySource(source) {
2540
+ const refs = this.getAllDataGridRefs();
2541
+ if (!source) {
2542
+ return refs.length === 1 ? refs[0].ref : null;
2543
+ }
2544
+ const entry = refs.find((r) => r.id === source);
2545
+ return entry?.ref || null;
2546
+ }
2547
+ resolveExpertOperand(type, value, source, aggregation = 'sum') {
2548
+ if (!value)
2549
+ return null;
2550
+ if (type === 'column') {
2551
+ const grid = this.getDataGridBySource(source || '');
2552
+ if (!grid)
2553
+ return null;
2554
+ const rows = grid.row || [];
2555
+ const values = rows.map((row) => parseFloat(row[value])).filter((v) => !isNaN(v));
2556
+ if (values.length === 0)
2557
+ return 0;
2558
+ switch (aggregation) {
2559
+ case 'sum':
2560
+ return values.reduce((acc, v) => acc + v, 0);
2561
+ case 'avg':
2562
+ return values.reduce((acc, v) => acc + v, 0) / values.length;
2563
+ case 'min':
2564
+ return Math.min(...values);
2565
+ case 'max':
2566
+ return Math.max(...values);
2567
+ case 'count':
2568
+ return values.length;
2569
+ default:
2570
+ return values.reduce((acc, v) => acc + v, 0);
2571
+ }
2572
+ }
2573
+ // type === 'variable'
2574
+ const variable = [...this.rows, ...this.variables].find((v) => v.field === value);
2575
+ if (!variable)
2576
+ return null;
2577
+ const parsed = parseFloat(String(variable.value || 0));
2578
+ return isNaN(parsed) ? 0 : parsed;
2579
+ }
2580
+ computeExpertValue(cell) {
2581
+ const config = cell.expertMode;
2582
+ if (!config?.enabled)
2583
+ return null;
2584
+ const leftType = config.leftType || 'column';
2585
+ const rightType = config.rightType || 'variable';
2586
+ const left = this.resolveExpertOperand(leftType, config.leftValue || config.sourceColumn || '', config.leftSource, config.leftAggregation || 'sum');
2587
+ if (left === null)
2588
+ return null;
2589
+ if (config.operation === 'none')
2590
+ return left;
2591
+ const rightValue = config.rightValue || config.variableField || '';
2592
+ if (!rightValue)
2593
+ return left;
2594
+ const right = this.resolveExpertOperand(rightType, rightValue, config.rightSource, config.rightAggregation || 'sum');
2595
+ if (right === null)
2596
+ return left;
2597
+ switch (config.operation) {
2598
+ case '+':
2599
+ return left + right;
2600
+ case '-':
2601
+ return left - right;
2602
+ case '*':
2603
+ return left * right;
2604
+ case '/':
2605
+ return right !== 0 ? left / right : null;
2606
+ default:
2607
+ return left;
2608
+ }
2609
+ }
2610
+ getCellWithExpertValue(cell) {
2611
+ const expertValue = this.computeExpertValue(cell);
2612
+ if (expertValue !== null) {
2613
+ return { ...cell, value: expertValue };
2614
+ }
2615
+ return cell;
2616
+ }
2617
+ openExpertModeModal(cell) {
2618
+ this.closeSettingsPopper();
2619
+ this.expertModeCell = cell;
2620
+ const existing = cell.expertMode;
2621
+ const defaultSource = this.getAllDataGridRefs().length === 1 ? this.getAllDataGridRefs()[0].id : '';
2622
+ // Load headerName mutations from cell
2623
+ const mutations = {};
2624
+ const supportedLangs = ['cs', 'en', 'de', 'sk', 'pl', 'hu', 'fr', 'it', 'es', 'sr'];
2625
+ supportedLangs.forEach((lang) => {
2626
+ const val = cell[`headerName_${lang}`];
2627
+ if (val)
2628
+ mutations[lang] = val;
2629
+ });
2630
+ this.expertModeConfig = {
2631
+ enabled: existing?.enabled || false,
2632
+ leftType: existing?.leftType || 'column',
2633
+ leftSource: existing?.leftSource || defaultSource,
2634
+ leftValue: existing?.leftValue || existing?.sourceColumn || '',
2635
+ leftAggregation: existing?.leftAggregation || 'sum',
2636
+ operation: existing?.operation || 'none',
2637
+ rightType: existing?.rightType || 'variable',
2638
+ rightSource: existing?.rightSource || defaultSource,
2639
+ rightValue: existing?.rightValue || existing?.variableField || '',
2640
+ rightAggregation: existing?.rightAggregation || 'sum',
2641
+ headerName: cell.headerName || '',
2642
+ headerNameMutations: mutations,
2643
+ pmEnabled: existing?.pmEnabled || false,
2644
+ pmLeftType: existing?.pmLeftType || 'column',
2645
+ pmLeftSource: existing?.pmLeftSource || defaultSource,
2646
+ pmLeftValue: existing?.pmLeftValue || '',
2647
+ pmLeftAggregation: existing?.pmLeftAggregation || 'sum',
2648
+ pmOperation: existing?.pmOperation || 'none',
2649
+ pmRightType: existing?.pmRightType || 'variable',
2650
+ pmRightSource: existing?.pmRightSource || defaultSource,
2651
+ pmRightValue: existing?.pmRightValue || '',
2652
+ pmRightAggregation: existing?.pmRightAggregation || 'sum',
2653
+ };
2654
+ this.expertModeModalOpen = true;
2655
+ }
2656
+ saveExpertMode() {
2657
+ if (!this.expertModeCell)
2658
+ return;
2659
+ this.rows = this.rows.map((c) => {
2660
+ if (c.field === this.expertModeCell.field) {
2661
+ const { headerName, headerNameMutations, ...expertModeData } = this.expertModeConfig;
2662
+ const updated = {
2663
+ ...c,
2664
+ expertMode: expertModeData,
2665
+ headerName: headerName,
2666
+ };
2667
+ // Write language mutations
2668
+ Object.entries(headerNameMutations).forEach(([lang, val]) => {
2669
+ updated[`headerName_${lang}`] = val;
2670
+ });
2671
+ // Remove empty mutations
2672
+ const supportedLangs = ['cs', 'en', 'de', 'sk', 'pl', 'hu', 'fr', 'it', 'es', 'sr'];
2673
+ supportedLangs.forEach((lang) => {
2674
+ if (!headerNameMutations[lang]) {
2675
+ delete updated[`headerName_${lang}`];
2676
+ }
2677
+ });
2678
+ return updated;
2679
+ }
2680
+ return c;
2681
+ });
2682
+ this.handleSettingsChanged(this.rows);
2683
+ this.expertModeModalOpen = false;
2684
+ this.expertModeCell = null;
2685
+ }
2686
+ getGridColumnOptions(source) {
2687
+ const grid = this.getDataGridBySource(source || '');
2688
+ if (!grid?.columns)
2689
+ return [];
2690
+ return grid.columns
2691
+ .filter((col) => {
2692
+ const type = col.type;
2693
+ return type === 'number' || type === 'currency' || type === 'range' || type === 'numberRange';
2694
+ })
2695
+ .map((col) => ({
2696
+ label: col.headerName || col.field,
2697
+ value: col.field,
2698
+ }));
2699
+ }
2700
+ getGridSourceOptions() {
2701
+ return this.getAllDataGridRefs().map((entry) => ({
2702
+ label: entry.label,
2703
+ value: entry.id,
2704
+ }));
2705
+ }
2706
+ getExpertVariableOptions() {
2707
+ const numericTypes = ['currency', 'number'];
2708
+ const variables = [];
2709
+ this.rows.forEach((cell) => {
2710
+ if (cell.type && numericTypes.includes(cell.type)) {
2711
+ variables.push({
2712
+ label: cell.headerName || cell.field,
2713
+ value: cell.field,
2714
+ });
2715
+ }
2716
+ });
2717
+ const existingFields = this.rows.map((cell) => cell.field);
2718
+ this.variables.forEach((variable) => {
2719
+ if (!existingFields.includes(variable.field) && variable.type && numericTypes.includes(variable.type)) {
2720
+ variables.push({
2721
+ label: variable.headerName || variable.field,
2722
+ value: variable.field,
2723
+ });
2724
+ }
2725
+ });
2726
+ return variables.sort((a, b) => a.label.localeCompare(b.label));
2727
+ }
2728
+ getExpertModePreviewValue() {
2729
+ if (!this.expertModeConfig.enabled || !this.expertModeConfig.leftValue) {
2730
+ return '-';
2731
+ }
2732
+ const tempCell = { field: '__preview__', expertMode: { ...this.expertModeConfig } };
2733
+ const value = this.computeExpertValue(tempCell);
2734
+ if (value === null)
2735
+ return 'N/A';
2736
+ return formatDecimal(value, { locale: this.getLocaleLang(), numberOfDecimal: 2 }) || String(value);
2737
+ }
2738
+ computeProgressMaxExpertValue(config) {
2739
+ if (!config?.pmEnabled)
2740
+ return null;
2741
+ const leftType = config.pmLeftType || 'column';
2742
+ const rightType = config.pmRightType || 'variable';
2743
+ const left = this.resolveExpertOperand(leftType, config.pmLeftValue || '', config.pmLeftSource, config.pmLeftAggregation || 'sum');
2744
+ if (left === null)
2745
+ return null;
2746
+ if (config.pmOperation === 'none')
2747
+ return left;
2748
+ const rightValue = config.pmRightValue || '';
2749
+ if (!rightValue)
2750
+ return left;
2751
+ const right = this.resolveExpertOperand(rightType, rightValue, config.pmRightSource, config.pmRightAggregation || 'sum');
2752
+ if (right === null)
2753
+ return left;
2754
+ switch (config.pmOperation) {
2755
+ case '+': return left + right;
2756
+ case '-': return left - right;
2757
+ case '*': return left * right;
2758
+ case '/': return right !== 0 ? left / right : null;
2759
+ default: return left;
2760
+ }
2761
+ }
2762
+ getProgressMaxPreviewValue() {
2763
+ if (!this.expertModeConfig.pmEnabled || !this.expertModeConfig.pmLeftValue) {
2764
+ return '-';
2765
+ }
2766
+ const value = this.computeProgressMaxExpertValue(this.expertModeConfig);
2767
+ if (value === null)
2768
+ return 'N/A';
2769
+ return formatDecimal(value, { locale: this.getLocaleLang(), numberOfDecimal: 2 }) || String(value);
2770
+ }
2771
+ getExpertOperandLabel(type, value, source, aggregation = 'sum') {
2772
+ if (!value)
2773
+ return '?';
2774
+ if (type === 'column') {
2775
+ const refs = this.getAllDataGridRefs();
2776
+ const sourceLabel = refs.length > 1
2777
+ ? refs.find((r) => r.id === source)?.label || ''
2778
+ : '';
2779
+ const aggLabel = (aggregation || 'sum').toUpperCase();
2780
+ return sourceLabel ? `${aggLabel}(${sourceLabel}.${value})` : `${aggLabel}(${value})`;
2781
+ }
2782
+ const variable = [...this.rows, ...this.variables].find((v) => v.field === value);
2783
+ return variable?.headerName || value;
2784
+ }
2785
+ renderExpertOperandSelector(side, label, tooltipText, prefix = '') {
2786
+ const pre = prefix || '';
2787
+ const typeKey = (pre + (side === 'left' ? 'LeftType' : 'RightType'));
2788
+ const sourceKey = (pre + (side === 'left' ? 'LeftSource' : 'RightSource'));
2789
+ const valueKey = (pre + (side === 'left' ? 'LeftValue' : 'RightValue'));
2790
+ const aggregationKey = (pre + (side === 'left' ? 'LeftAggregation' : 'RightAggregation'));
2791
+ const enabledKey = (pre ? pre + 'Enabled' : 'enabled');
2792
+ // For non-prefixed keys (default), use original camelCase: leftType, rightType, etc.
2793
+ const typeKeyFinal = pre ? typeKey : (side === 'left' ? 'leftType' : 'rightType');
2794
+ const sourceKeyFinal = pre ? sourceKey : (side === 'left' ? 'leftSource' : 'rightSource');
2795
+ const valueKeyFinal = pre ? valueKey : (side === 'left' ? 'leftValue' : 'rightValue');
2796
+ const aggregationKeyFinal = pre ? aggregationKey : (side === 'left' ? 'leftAggregation' : 'rightAggregation');
2797
+ const currentType = this.expertModeConfig[typeKeyFinal];
2798
+ const currentSource = this.expertModeConfig[sourceKeyFinal];
2799
+ const currentValue = this.expertModeConfig[valueKeyFinal];
2800
+ const currentAggregation = this.expertModeConfig[aggregationKeyFinal];
2801
+ const isEnabled = !!this.expertModeConfig[enabledKey];
2802
+ const typeOptions = [
2803
+ { label: msg('Tabulka'), value: 'column' },
2804
+ { label: msg('Proměnná'), value: 'variable' },
2805
+ ];
2806
+ const aggregationOptions = [
2807
+ { label: 'SUM', value: 'sum' },
2808
+ { label: 'AVG', value: 'avg' },
2809
+ { label: 'MIN', value: 'min' },
2810
+ { label: 'MAX', value: 'max' },
2811
+ { label: 'COUNT', value: 'count' },
2812
+ ];
2813
+ const gridSourceOptions = this.getGridSourceOptions();
2814
+ const showSourceSelector = currentType === 'column' && gridSourceOptions.length > 1;
2815
+ const valueOptions = currentType === 'column'
2816
+ ? this.getGridColumnOptions(currentSource)
2817
+ : this.getExpertVariableOptions();
2818
+ return html `
2819
+ <div style="flex: 1; display: flex; flex-direction: column; gap: 4px;">
2820
+ <lit-label .label="${label}" .tooltip="${tooltipText}"></lit-label>
2821
+ <div style="display: flex; flex-direction: column; gap: 4px;">
2822
+ <lit-select
2823
+ .options=${typeOptions}
2824
+ .value=${currentType}
2825
+ .placeholder=${msg('Vyberte typ')}
2826
+ .disabled=${!isEnabled}
2827
+ .onChange=${(value) => {
2828
+ const val = (Array.isArray(value) ? value[0] : value);
2829
+ this.expertModeConfig = {
2830
+ ...this.expertModeConfig,
2831
+ [typeKeyFinal]: val || 'column',
2832
+ [valueKeyFinal]: '',
2833
+ };
2834
+ this.requestUpdate();
2835
+ }}
2836
+ ></lit-select>
2837
+ ${showSourceSelector
2838
+ ? html `
2839
+ <lit-select
2840
+ .options=${gridSourceOptions}
2841
+ .value=${currentSource}
2842
+ .placeholder=${msg('Vyberte tabulku')}
2843
+ .disabled=${!isEnabled}
2844
+ .onChange=${(value) => {
2845
+ const val = Array.isArray(value) ? value[0] : value;
2846
+ this.expertModeConfig = {
2847
+ ...this.expertModeConfig,
2848
+ [sourceKeyFinal]: val || '',
2849
+ [valueKeyFinal]: '',
2850
+ };
2851
+ this.requestUpdate();
2852
+ }}
2853
+ ></lit-select>
2854
+ `
2855
+ : ''}
2856
+ <lit-select
2857
+ .options=${valueOptions}
2858
+ .value=${currentValue}
2859
+ .placeholder=${currentType === 'column' ? msg('Vyberte sloupec') : msg('Vyberte proměnnou')}
2860
+ .disabled=${!isEnabled}
2861
+ .onChange=${(value) => {
2862
+ const val = Array.isArray(value) ? value[0] : value;
2863
+ this.expertModeConfig = {
2864
+ ...this.expertModeConfig,
2865
+ [valueKeyFinal]: val || '',
2866
+ };
2867
+ this.requestUpdate();
2868
+ }}
2869
+ ></lit-select>
2870
+ ${currentType === 'column'
2871
+ ? html `
2872
+ <lit-select
2873
+ .options=${aggregationOptions}
2874
+ .value=${currentAggregation}
2875
+ .placeholder=${msg('Vyberte agregaci')}
2876
+ .disabled=${!isEnabled}
2877
+ .onChange=${(value) => {
2878
+ const val = Array.isArray(value) ? value[0] : value;
2879
+ this.expertModeConfig = {
2880
+ ...this.expertModeConfig,
2881
+ [aggregationKeyFinal]: val || 'sum',
2882
+ };
2883
+ this.requestUpdate();
2884
+ }}
2885
+ ></lit-select>
2886
+ `
2887
+ : ''}
2888
+ </div>
2889
+ </div>
2890
+ `;
2891
+ }
2892
+ renderExpertModeModal() {
2893
+ const operationOptions = [
2894
+ { label: msg('Žádná'), value: 'none' },
2895
+ { label: '+ (' + msg('Sčítání') + ')', value: '+' },
2896
+ { label: '- (' + msg('Odčítání') + ')', value: '-' },
2897
+ { label: '× (' + msg('Násobení') + ')', value: '*' },
2898
+ { label: '÷ (' + msg('Dělení') + ')', value: '/' },
2899
+ ];
2900
+ const isNoOperation = this.expertModeConfig.operation === 'none';
2901
+ const closeModal = () => {
2902
+ this.expertModeModalOpen = false;
2903
+ this.expertModeCell = null;
2904
+ };
2905
+ return html `
2906
+ <lit-modal
2907
+ .open=${this.expertModeModalOpen}
2908
+ .onClose=${closeModal}
2909
+ .closeOnOutsideClick=${false}
2910
+ style="min-width: 60vw;"
2911
+ >
2912
+ <lit-modal-header
2913
+ style="display: flex; align-items: center; justify-content: space-between; color: var(--text-secondary, #777);"
2914
+ >
2915
+ ${msg('Expertní mód')} - ${this.expertModeCell?.headerName || this.expertModeCell?.field || ''}
2916
+ <lit-icon-button
2917
+ .icon="${'close'}"
2918
+ variant="text"
2919
+ color="secondary"
2920
+ size="${'small'}"
2921
+ @click=${closeModal}
2922
+ ></lit-icon-button>
2923
+ </lit-modal-header>
2924
+ <lit-modal-body>
2925
+ <div style="height: 400px;">
2926
+ <lit-tabs-overview
2927
+ .tabs=${[
2928
+ ...(this.expertModeCell?.type === 'progress'
2929
+ ? [{ id: 'progressMax', label: { default: msg('Hodnota 100%') }, icon: 'accomplish' }]
2930
+ : []),
2931
+ { id: 'calculation', label: { default: msg('Výpočet') }, icon: 'calculator' },
2932
+ { id: 'header', label: { default: msg('Záhlaví buňky') }, icon: 'lang' },
2933
+ ]}
2934
+ .initialSelectedTabId=${this.expertModeCell?.type === 'progress' ? 'progressMax' : 'calculation'}
2935
+ .userLang=${this.userLang}
2936
+ >
2937
+ <!-- Tab: Výpočet (Calculation) -->
2938
+ <div slot="calculation" style="display: flex; flex-direction: column; gap: 1rem;">
2939
+ <lit-toggle
2940
+ .label="${msg('Povolit expertní výpočet')}"
2941
+ .tooltip="${msg('Vypočítá hodnotu na základě vybraných sloupců/proměnných a zvolené matematické operace.')}"
2942
+ .checked=${this.expertModeConfig.enabled}
2943
+ @change=${(e) => {
2944
+ this.expertModeConfig = {
2945
+ ...this.expertModeConfig,
2946
+ enabled: e.detail,
2947
+ };
2948
+ }}
2949
+ ></lit-toggle>
2950
+
2951
+ <div style="display: flex; flex-direction: row; gap: 1rem; align-items: flex-start;">
2952
+ ${this.renderExpertOperandSelector('left', msg('Levý operand'), msg('Vyberte typ a hodnotu levého operandu. Sloupec vypočítá agregaci (SUM, AVG, MIN, MAX, COUNT) všech hodnot, proměnná použije hodnotu vybrané buňky.'))}
2953
+
2954
+ <div style="flex: 0 0 auto; display: flex; flex-direction: column; gap: 4px; min-width: 120px;">
2955
+ <lit-label .label="${msg('Operace')}" .tooltip="${msg('Matematická operace mezi levým a pravým operandem. Zvolte Žádná pro zobrazení pouze levého operandu.')}"></lit-label>
2956
+ <lit-select
2957
+ .options=${operationOptions}
2958
+ .value=${this.expertModeConfig.operation}
2959
+ .placeholder=${msg('Vyberte operaci')}
2960
+ .disabled=${!this.expertModeConfig.enabled}
2961
+ .onChange=${(value) => {
2962
+ const val = Array.isArray(value) ? value[0] : value;
2963
+ this.expertModeConfig = {
2964
+ ...this.expertModeConfig,
2965
+ operation: val || '+',
2966
+ };
2967
+ this.requestUpdate();
2968
+ }}
2969
+ ></lit-select>
2970
+ </div>
2971
+
2972
+ ${!isNoOperation ? this.renderExpertOperandSelector('right', msg('Pravý operand'), msg('Vyberte typ a hodnotu pravého operandu. Sloupec vypočítá agregaci (SUM, AVG, MIN, MAX, COUNT) všech hodnot, proměnná použije hodnotu vybrané buňky.')) : ''}
2973
+ </div>
2974
+
2975
+ ${this.expertModeConfig.enabled
2976
+ ? html `
2977
+ <div
2978
+ style="
2979
+ padding: 0.75rem;
2980
+ background: var(--background-default, #f5f5f5);
2981
+ border-radius: var(--border-radius-small, 8px);
2982
+ font-size: 0.8125rem;
2983
+ color: var(--text-secondary, #5d6371);
2984
+ "
2985
+ >
2986
+ <div style="font-weight: 600; margin-bottom: 0.25rem;">${msg('Náhled výpočtu')}</div>
2987
+ <div>
2988
+ ${this.getExpertOperandLabel(this.expertModeConfig.leftType, this.expertModeConfig.leftValue, this.expertModeConfig.leftSource, this.expertModeConfig.leftAggregation)}
2989
+ ${this.expertModeConfig.operation !== 'none'
2990
+ ? html `${this.expertModeConfig.operation} ${this.getExpertOperandLabel(this.expertModeConfig.rightType, this.expertModeConfig.rightValue, this.expertModeConfig.rightSource, this.expertModeConfig.rightAggregation)}`
2991
+ : ''}
2992
+ = <strong style="color: var(--text-primary, #111827);">${this.getExpertModePreviewValue()}</strong>
2993
+ </div>
2994
+ </div>
2995
+ `
2996
+ : ''}
2997
+ </div>
2998
+
2999
+ <!-- Tab: Hodnota 100% (Progress Max) -->
3000
+ ${this.expertModeCell?.type === 'progress' ? html `
3001
+ <div slot="progressMax" style="display: flex; flex-direction: column; gap: 1rem;">
3002
+ <lit-toggle
3003
+ .label="${msg('Vypočítat hodnotu 100%')}"
3004
+ .tooltip="${msg('Vypočítá hodnotu pro 100% na základě vybraných sloupců/proměnných a zvolené matematické operace.')}"
3005
+ .checked=${this.expertModeConfig.pmEnabled}
3006
+ @change=${(e) => {
3007
+ this.expertModeConfig = {
3008
+ ...this.expertModeConfig,
3009
+ pmEnabled: e.detail,
3010
+ };
3011
+ }}
3012
+ ></lit-toggle>
3013
+
3014
+ <div style="display: flex; flex-direction: row; gap: 1rem; align-items: flex-start;">
3015
+ ${this.renderExpertOperandSelector('left', msg('Levý operand'), msg('Vyberte typ a hodnotu levého operandu pro výpočet hodnoty 100%.'), 'pm')}
3016
+
3017
+ <div style="flex: 0 0 auto; display: flex; flex-direction: column; gap: 4px; min-width: 120px;">
3018
+ <lit-label .label="${msg('Operace')}" .tooltip="${msg('Matematická operace mezi levým a pravým operandem.')}"></lit-label>
3019
+ <lit-select
3020
+ .options=${operationOptions}
3021
+ .value=${this.expertModeConfig.pmOperation}
3022
+ .placeholder=${msg('Vyberte operaci')}
3023
+ .disabled=${!this.expertModeConfig.pmEnabled}
3024
+ .onChange=${(value) => {
3025
+ const val = Array.isArray(value) ? value[0] : value;
3026
+ this.expertModeConfig = {
3027
+ ...this.expertModeConfig,
3028
+ pmOperation: val || 'none',
3029
+ };
3030
+ this.requestUpdate();
3031
+ }}
3032
+ ></lit-select>
3033
+ </div>
3034
+
3035
+ ${this.expertModeConfig.pmOperation !== 'none' ? this.renderExpertOperandSelector('right', msg('Pravý operand'), msg('Vyberte typ a hodnotu pravého operandu pro výpočet hodnoty 100%.'), 'pm') : ''}
3036
+ </div>
3037
+
3038
+ ${this.expertModeConfig.pmEnabled
3039
+ ? html `
3040
+ <div
3041
+ style="
3042
+ padding: 0.75rem;
3043
+ background: var(--background-default, #f5f5f5);
3044
+ border-radius: var(--border-radius-small, 8px);
3045
+ font-size: 0.8125rem;
3046
+ color: var(--text-secondary, #5d6371);
3047
+ "
3048
+ >
3049
+ <div style="font-weight: 600; margin-bottom: 0.25rem;">${msg('Náhled hodnoty 100%')}</div>
3050
+ <div>
3051
+ ${this.getExpertOperandLabel(this.expertModeConfig.pmLeftType, this.expertModeConfig.pmLeftValue, this.expertModeConfig.pmLeftSource, this.expertModeConfig.pmLeftAggregation)}
3052
+ ${this.expertModeConfig.pmOperation !== 'none'
3053
+ ? html `${this.expertModeConfig.pmOperation} ${this.getExpertOperandLabel(this.expertModeConfig.pmRightType, this.expertModeConfig.pmRightValue, this.expertModeConfig.pmRightSource, this.expertModeConfig.pmRightAggregation)}`
3054
+ : ''}
3055
+ = <strong style="color: var(--text-primary, #111827);">${this.getProgressMaxPreviewValue()}</strong>
3056
+ </div>
3057
+ </div>
3058
+ `
3059
+ : ''}
3060
+ </div>
3061
+ ` : ''}
3062
+
3063
+ <!-- Tab: Záhlaví buňky (Header) -->
3064
+ <div slot="header" style="display: flex; flex-direction: column; gap: 1rem;">
3065
+ <lit-label
3066
+ .label="${msg('Záhlaví buňky')}"
3067
+ .tooltip="${msg('Název buňky zobrazený v záhlaví. Můžete nastavit překlad pro různé jazyky.')}"
3068
+ ></lit-label>
3069
+ <div style="display: flex; flex-direction: column; gap: 0.5rem;">
3070
+ <lit-text-field
3071
+ .label="${msg('Výchozí název')}"
3072
+ .value=${this.expertModeConfig.headerName}
3073
+ @onChange=${(e) => {
3074
+ this.expertModeConfig = {
3075
+ ...this.expertModeConfig,
3076
+ headerName: e.detail,
3077
+ };
3078
+ }}
3079
+ ></lit-text-field>
3080
+
3081
+ ${(this.allowedLang || []).map((lang) => html `
3082
+ <lit-text-field
3083
+ .label="${msg('Záhlaví')} ${lang.toUpperCase()}"
3084
+ .placeholder="${msg('Zadejte název')} ${lang.toUpperCase()}"
3085
+ .value=${this.expertModeConfig.headerNameMutations[lang] || ''}
3086
+ @onChange=${(e) => {
3087
+ this.expertModeConfig = {
3088
+ ...this.expertModeConfig,
3089
+ headerNameMutations: {
3090
+ ...this.expertModeConfig.headerNameMutations,
3091
+ [lang]: e.detail,
3092
+ },
3093
+ };
3094
+ }}
3095
+ ></lit-text-field>
3096
+ `)}
3097
+ </div>
3098
+ </div>
3099
+ </lit-tabs-overview>
3100
+ </div>
3101
+ </lit-modal-body>
3102
+ <lit-modal-footer
3103
+ style="display: flex; flex-direction: row; gap: 0.5rem; justify-content: center;"
3104
+ >
3105
+ <lit-button
3106
+ .label="${msg('Uložit')}"
3107
+ .icon="check"
3108
+ .size="medium"
3109
+ @click=${() => this.saveExpertMode()}
3110
+ ></lit-button>
3111
+ <lit-button
3112
+ color="secondary"
3113
+ .label="${msg('Zrušit')}"
3114
+ variant="text"
3115
+ .icon="close"
3116
+ .size="medium"
3117
+ @click=${closeModal}
3118
+ ></lit-button>
3119
+ </lit-modal-footer>
3120
+ </lit-modal>
3121
+ `;
3122
+ }
2464
3123
  resolveCurrencyType(cell) {
2465
3124
  const currencyType = cell.currencyType || 'CZK';
2466
3125
  if (currencyType.startsWith('var:')) {
@@ -2475,6 +3134,20 @@ export class LitCaseVariablesTab extends LitElement {
2475
3134
  }
2476
3135
  return currencyType;
2477
3136
  }
3137
+ resolveProgressMax(cell) {
3138
+ // 1. If expert mode progressMax computation is enabled, use it
3139
+ const config = cell.expertMode;
3140
+ if (config?.pmEnabled) {
3141
+ const computed = this.computeProgressMaxExpertValue(config);
3142
+ if (computed !== null && computed > 0)
3143
+ return computed;
3144
+ }
3145
+ // 2. Otherwise use static progressMax from tweakpane
3146
+ const staticMax = cell.progressMax;
3147
+ if (typeof staticMax === 'number' && staticMax > 0)
3148
+ return staticMax;
3149
+ return 100;
3150
+ }
2478
3151
  parseBooleanValue(value) {
2479
3152
  if (typeof value === 'boolean')
2480
3153
  return value;
@@ -2503,10 +3176,11 @@ export class LitCaseVariablesTab extends LitElement {
2503
3176
  : this.gridVariables
2504
3177
  ? html ` <div class="grid-container">
2505
3178
  ${repeat(this.rows, (cell, index) => cell.field, (cell) => {
3179
+ const resolvedCell = this.getCellWithExpertValue(cell);
2506
3180
  const classes = `cell--span4`;
2507
3181
  return html `
2508
3182
  <div class="${classes}" data-field="${cell.field}">
2509
- ${this.getInlineCellValue(cell)}
3183
+ ${this.getInlineCellValue(resolvedCell)}
2510
3184
  </div>
2511
3185
  `;
2512
3186
  })}
@@ -2514,6 +3188,7 @@ export class LitCaseVariablesTab extends LitElement {
2514
3188
  : html `
2515
3189
  <div class="grid-container">
2516
3190
  ${repeat(this.rows, (cell, index) => cell.field, (cell) => {
3191
+ const resolvedCell = this.getCellWithExpertValue(cell);
2517
3192
  const bpIndex = this.BREAKPOINTS.indexOf(this.currentBreakpoint);
2518
3193
  const spanSize = this.gridVariables
2519
3194
  ? 4
@@ -2528,21 +3203,21 @@ export class LitCaseVariablesTab extends LitElement {
2528
3203
  style="${styleMap(this.computeCellStyles(cell))}"
2529
3204
  data-field="${cell.field}"
2530
3205
  >
2531
- ${cell?.type === 'button'
2532
- ? this.getCellButton(cell)
2533
- : cell.type === 'link'
2534
- ? this.getCellLink(cell)
2535
- : cell.type === 'progress'
2536
- ? this.getCellProgress(cell)
2537
- : cell.type === 'currency'
2538
- ? this.getCellCurrency(cell)
2539
- : cell.type === 'date'
2540
- ? this.getCellDate(cell)
2541
- : cell.type === 'number'
2542
- ? this.getCellNumber(cell)
2543
- : cell.type === 'checkbox'
2544
- ? this.getCellCheckbox(cell)
2545
- : this.getCellValue(cell)}
3206
+ ${resolvedCell?.type === 'button'
3207
+ ? this.getCellButton(resolvedCell)
3208
+ : resolvedCell.type === 'link'
3209
+ ? this.getCellLink(resolvedCell)
3210
+ : resolvedCell.type === 'progress'
3211
+ ? this.getCellProgress(resolvedCell)
3212
+ : resolvedCell.type === 'currency'
3213
+ ? this.getCellCurrency(resolvedCell)
3214
+ : resolvedCell.type === 'date'
3215
+ ? this.getCellDate(resolvedCell)
3216
+ : resolvedCell.type === 'number'
3217
+ ? this.getCellNumber(resolvedCell)
3218
+ : resolvedCell.type === 'checkbox'
3219
+ ? this.getCellCheckbox(resolvedCell)
3220
+ : this.getCellValue(resolvedCell)}
2546
3221
  </div>
2547
3222
  `;
2548
3223
  })}
@@ -2662,6 +3337,7 @@ export class LitCaseVariablesTab extends LitElement {
2662
3337
  </div>
2663
3338
  `
2664
3339
  : ''}
3340
+ ${this.renderExpertModeModal()}
2665
3341
  `;
2666
3342
  }
2667
3343
  }
@@ -2911,6 +3587,9 @@ __decorate([
2911
3587
  __decorate([
2912
3588
  property({ type: String })
2913
3589
  ], LitCaseVariablesTab.prototype, "userLang", void 0);
3590
+ __decorate([
3591
+ property({ type: Array })
3592
+ ], LitCaseVariablesTab.prototype, "allowedLang", void 0);
2914
3593
  __decorate([
2915
3594
  property({ type: String })
2916
3595
  ], LitCaseVariablesTab.prototype, "dateFormat", void 0);
@@ -2932,6 +3611,12 @@ __decorate([
2932
3611
  __decorate([
2933
3612
  property({ type: String })
2934
3613
  ], LitCaseVariablesTab.prototype, "hostURL", void 0);
3614
+ __decorate([
3615
+ property({ attribute: false })
3616
+ ], LitCaseVariablesTab.prototype, "dataGridRef", void 0);
3617
+ __decorate([
3618
+ property({ attribute: false })
3619
+ ], LitCaseVariablesTab.prototype, "dataGridRefs", void 0);
2935
3620
  __decorate([
2936
3621
  state()
2937
3622
  ], LitCaseVariablesTab.prototype, "currentBreakpoint", void 0);
@@ -2944,6 +3629,15 @@ __decorate([
2944
3629
  __decorate([
2945
3630
  state()
2946
3631
  ], LitCaseVariablesTab.prototype, "isMobile", void 0);
3632
+ __decorate([
3633
+ state()
3634
+ ], LitCaseVariablesTab.prototype, "expertModeModalOpen", void 0);
3635
+ __decorate([
3636
+ state()
3637
+ ], LitCaseVariablesTab.prototype, "expertModeCell", void 0);
3638
+ __decorate([
3639
+ state()
3640
+ ], LitCaseVariablesTab.prototype, "expertModeConfig", void 0);
2947
3641
  __decorate([
2948
3642
  state()
2949
3643
  ], LitCaseVariablesTab.prototype, "activeSettingsCell", void 0);