overview-components 1.1.145 → 1.1.146

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