@praxisui/table 1.0.0-beta.60 → 1.0.0-beta.63
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.
- package/README.md +9 -4
- package/fesm2022/{praxisui-table-table-ai.adapter-DAmsfime.mjs → praxisui-table-table-ai.adapter-C5rjLb8E.mjs} +9 -3
- package/fesm2022/{praxisui-table-table-ai.adapter-DAmsfime.mjs.map → praxisui-table-table-ai.adapter-C5rjLb8E.mjs.map} +1 -1
- package/fesm2022/praxisui-table.mjs +816 -457
- package/fesm2022/praxisui-table.mjs.map +1 -1
- package/index.d.ts +121 -26
- package/package.json +10 -9
|
@@ -2594,33 +2594,389 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
2594
2594
|
type: Output
|
|
2595
2595
|
}] } });
|
|
2596
2596
|
|
|
2597
|
+
const DOCUMENT_KIND = 'praxis.table.editor';
|
|
2598
|
+
const DOCUMENT_VERSION = 1;
|
|
2599
|
+
const HORIZONTAL_SCROLL_VALUES = ['auto', 'wrap', 'none'];
|
|
2600
|
+
function createTableAuthoringDocument(source) {
|
|
2601
|
+
const doc = {
|
|
2602
|
+
kind: DOCUMENT_KIND,
|
|
2603
|
+
version: DOCUMENT_VERSION,
|
|
2604
|
+
config: asTableConfig(source?.config),
|
|
2605
|
+
};
|
|
2606
|
+
if (hasOwnProperty(source, 'bindings')) {
|
|
2607
|
+
doc.bindings = source?.bindings;
|
|
2608
|
+
}
|
|
2609
|
+
return normalizeTableAuthoringDocument(doc);
|
|
2610
|
+
}
|
|
2611
|
+
function parseLegacyOrTableDocument(raw) {
|
|
2612
|
+
const obj = asRecord(raw);
|
|
2613
|
+
if (obj.kind === DOCUMENT_KIND && obj.version === DOCUMENT_VERSION) {
|
|
2614
|
+
return normalizeTableAuthoringDocument({
|
|
2615
|
+
kind: DOCUMENT_KIND,
|
|
2616
|
+
version: DOCUMENT_VERSION,
|
|
2617
|
+
config: asTableConfig(obj.config),
|
|
2618
|
+
bindings: obj.bindings,
|
|
2619
|
+
});
|
|
2620
|
+
}
|
|
2621
|
+
if (looksLikeLegacyPayload(obj)) {
|
|
2622
|
+
return normalizeTableAuthoringDocument({
|
|
2623
|
+
kind: DOCUMENT_KIND,
|
|
2624
|
+
version: DOCUMENT_VERSION,
|
|
2625
|
+
config: extractLegacyTableConfig(obj),
|
|
2626
|
+
bindings: {
|
|
2627
|
+
resourcePath: normalizeResourcePath(obj.__resourcePath__),
|
|
2628
|
+
idField: normalizeIdField(obj.__idField__),
|
|
2629
|
+
horizontalScroll: normalizeHorizontalScroll(obj.__horizontalScroll__),
|
|
2630
|
+
},
|
|
2631
|
+
});
|
|
2632
|
+
}
|
|
2633
|
+
return createTableAuthoringDocument({ config: raw });
|
|
2634
|
+
}
|
|
2635
|
+
function normalizeTableAuthoringDocument(doc) {
|
|
2636
|
+
const normalized = {
|
|
2637
|
+
kind: DOCUMENT_KIND,
|
|
2638
|
+
version: DOCUMENT_VERSION,
|
|
2639
|
+
config: ensureAuthoringCompleteness(normalizeTableConfig(doc?.config)),
|
|
2640
|
+
};
|
|
2641
|
+
if (hasOwnProperty(doc, 'bindings')) {
|
|
2642
|
+
normalized.bindings = normalizeBindings(doc?.bindings);
|
|
2643
|
+
}
|
|
2644
|
+
return normalized;
|
|
2645
|
+
}
|
|
2646
|
+
function validateTableAuthoringDocument(doc, context) {
|
|
2647
|
+
const raw = asRecord(doc);
|
|
2648
|
+
const rawConfig = asRecord(raw.config);
|
|
2649
|
+
const rawBindings = asRecord(raw.bindings);
|
|
2650
|
+
const normalized = normalizeTableAuthoringDocument(doc);
|
|
2651
|
+
const diagnostics = [];
|
|
2652
|
+
if ('columns' in rawConfig && !Array.isArray(rawConfig.columns)) {
|
|
2653
|
+
diagnostics.push(errorDiagnostic('table.config.columns.invalid', 'config.columns must be an array', 'config.columns'));
|
|
2654
|
+
return diagnostics;
|
|
2655
|
+
}
|
|
2656
|
+
const rawColumns = Array.isArray(rawConfig.columns)
|
|
2657
|
+
? rawConfig.columns
|
|
2658
|
+
: normalized.config.columns;
|
|
2659
|
+
rawColumns.forEach((column, index) => {
|
|
2660
|
+
if (!trimString(column?.field)) {
|
|
2661
|
+
diagnostics.push(errorDiagnostic('table.config.columns.field.required', 'Column field is required', `config.columns[${index}].field`));
|
|
2662
|
+
}
|
|
2663
|
+
});
|
|
2664
|
+
const bindings = normalized.bindings || {};
|
|
2665
|
+
if ('horizontalScroll' in rawBindings &&
|
|
2666
|
+
rawBindings.horizontalScroll !== undefined &&
|
|
2667
|
+
normalizeHorizontalScroll(rawBindings.horizontalScroll) === undefined) {
|
|
2668
|
+
diagnostics.push(errorDiagnostic('table.bindings.horizontalScroll.invalid', 'horizontalScroll must be auto, wrap or none', 'bindings.horizontalScroll'));
|
|
2669
|
+
}
|
|
2670
|
+
if (bindings.horizontalScroll &&
|
|
2671
|
+
!HORIZONTAL_SCROLL_VALUES.includes(bindings.horizontalScroll)) {
|
|
2672
|
+
diagnostics.push(errorDiagnostic('table.bindings.horizontalScroll.invalid', 'horizontalScroll must be auto, wrap or none', 'bindings.horizontalScroll'));
|
|
2673
|
+
}
|
|
2674
|
+
const effectiveMode = resolveEffectiveDataMode(normalized, context);
|
|
2675
|
+
if (effectiveMode === 'local') {
|
|
2676
|
+
if (normalized.config.behavior?.pagination?.strategy === 'server') {
|
|
2677
|
+
diagnostics.push(warnDiagnostic('table.behavior.pagination.strategy.invalid-for-local', 'Server pagination will be projected to client in local mode', 'config.behavior.pagination.strategy'));
|
|
2678
|
+
}
|
|
2679
|
+
if (normalized.config.behavior?.sorting?.strategy === 'server') {
|
|
2680
|
+
diagnostics.push(warnDiagnostic('table.behavior.sorting.strategy.invalid-for-local', 'Server sorting will be projected to client in local mode', 'config.behavior.sorting.strategy'));
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
if (context?.server?.idField &&
|
|
2684
|
+
bindings.idField &&
|
|
2685
|
+
context.server.idField !== bindings.idField) {
|
|
2686
|
+
diagnostics.push(warnDiagnostic('table.bindings.idField.diverges-from-server', 'bindings.idField diverges from current server idField', 'bindings.idField'));
|
|
2687
|
+
}
|
|
2688
|
+
const configHash = trimString(normalized.config?.meta?.serverHash);
|
|
2689
|
+
if (context?.server?.schemaHash && !configHash) {
|
|
2690
|
+
diagnostics.push(infoDiagnostic('table.meta.serverHash.snapshot-missing', 'No persisted schema snapshot is available for reconciliation with the current server schema hash', 'config.meta.serverHash'));
|
|
2691
|
+
}
|
|
2692
|
+
if (context?.server?.schemaHash && configHash && context.server.schemaHash !== configHash) {
|
|
2693
|
+
diagnostics.push(warnDiagnostic('table.meta.serverHash.diverges-from-server', 'config.meta.serverHash snapshot diverges from current server schema hash', 'config.meta.serverHash'));
|
|
2694
|
+
}
|
|
2695
|
+
return diagnostics;
|
|
2696
|
+
}
|
|
2697
|
+
function toCanonicalTableConfig(doc, context) {
|
|
2698
|
+
const normalized = normalizeTableAuthoringDocument(doc);
|
|
2699
|
+
const cfg = cloneJson(normalized.config);
|
|
2700
|
+
const effectiveMode = resolveEffectiveDataMode(normalized, context);
|
|
2701
|
+
if (effectiveMode === 'local') {
|
|
2702
|
+
if (cfg.behavior?.pagination?.strategy === 'server') {
|
|
2703
|
+
cfg.behavior = {
|
|
2704
|
+
...(cfg.behavior || {}),
|
|
2705
|
+
pagination: {
|
|
2706
|
+
...(cfg.behavior?.pagination || {}),
|
|
2707
|
+
strategy: 'client',
|
|
2708
|
+
},
|
|
2709
|
+
};
|
|
2710
|
+
}
|
|
2711
|
+
if (cfg.behavior?.sorting?.strategy === 'server') {
|
|
2712
|
+
cfg.behavior = {
|
|
2713
|
+
...(cfg.behavior || {}),
|
|
2714
|
+
sorting: {
|
|
2715
|
+
...(cfg.behavior?.sorting || {}),
|
|
2716
|
+
strategy: 'client',
|
|
2717
|
+
},
|
|
2718
|
+
};
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
return stripLegacyRuntimeMarkers(cfg);
|
|
2722
|
+
}
|
|
2723
|
+
function buildTableApplyPlan(doc, runtime, options) {
|
|
2724
|
+
const normalized = normalizeTableAuthoringDocument(doc);
|
|
2725
|
+
const hasBindings = hasOwnProperty(doc, 'bindings');
|
|
2726
|
+
const diagnostics = validateTableAuthoringDocument(normalized, {
|
|
2727
|
+
hasLocalDataInput: runtime?.hasLocalDataInput,
|
|
2728
|
+
localDataModeDefaultEnabled: runtime?.localDataModeDefaultEnabled,
|
|
2729
|
+
server: {
|
|
2730
|
+
idField: runtime?.server?.idField,
|
|
2731
|
+
schemaHash: runtime?.server?.schemaHash,
|
|
2732
|
+
},
|
|
2733
|
+
});
|
|
2734
|
+
const bindingsPatch = hasBindings
|
|
2735
|
+
? normalizeBindings(normalized.bindings) || {}
|
|
2736
|
+
: undefined;
|
|
2737
|
+
const diff = deriveBindingsDiff(bindingsPatch, runtime?.currentBindings);
|
|
2738
|
+
return {
|
|
2739
|
+
canonicalConfig: toCanonicalTableConfig(normalized, {
|
|
2740
|
+
hasLocalDataInput: runtime?.hasLocalDataInput,
|
|
2741
|
+
localDataModeDefaultEnabled: runtime?.localDataModeDefaultEnabled,
|
|
2742
|
+
}),
|
|
2743
|
+
bindingsPatch,
|
|
2744
|
+
persistence: {
|
|
2745
|
+
saveConfig: options?.saveConfig === true,
|
|
2746
|
+
saveBindings: options?.saveBindings === true,
|
|
2747
|
+
},
|
|
2748
|
+
runtime: deriveRuntimePlan(bindingsPatch, diff),
|
|
2749
|
+
metadata: {
|
|
2750
|
+
attachServerMeta: options?.attachServerMeta === true,
|
|
2751
|
+
},
|
|
2752
|
+
diff,
|
|
2753
|
+
diagnostics,
|
|
2754
|
+
};
|
|
2755
|
+
}
|
|
2756
|
+
function serializeTableAuthoringDocument(doc) {
|
|
2757
|
+
const normalized = normalizeTableAuthoringDocument(doc);
|
|
2758
|
+
return stripUndefinedDeep({
|
|
2759
|
+
kind: DOCUMENT_KIND,
|
|
2760
|
+
version: DOCUMENT_VERSION,
|
|
2761
|
+
config: normalized.config,
|
|
2762
|
+
bindings: normalized.bindings,
|
|
2763
|
+
});
|
|
2764
|
+
}
|
|
2765
|
+
function normalizeTableConfig(config) {
|
|
2766
|
+
const normalized = cloneJson(config || { columns: [] });
|
|
2767
|
+
if (!Array.isArray(normalized.columns)) {
|
|
2768
|
+
normalized.columns = [];
|
|
2769
|
+
}
|
|
2770
|
+
normalized.columns.sort((left, right) => String(left?.field || '').localeCompare(String(right?.field || '')));
|
|
2771
|
+
if (normalized.behavior && typeof normalized.behavior === 'object') {
|
|
2772
|
+
Object.keys(normalized.behavior).forEach((key) => {
|
|
2773
|
+
const value = normalized.behavior[key];
|
|
2774
|
+
if (value &&
|
|
2775
|
+
typeof value === 'object' &&
|
|
2776
|
+
!Array.isArray(value) &&
|
|
2777
|
+
Object.keys(value).length === 0) {
|
|
2778
|
+
delete normalized.behavior[key];
|
|
2779
|
+
}
|
|
2780
|
+
});
|
|
2781
|
+
if (Object.keys(normalized.behavior).length === 0) {
|
|
2782
|
+
delete normalized.behavior;
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
const settings = normalized.behavior?.filtering?.advancedFilters?.settings;
|
|
2786
|
+
if (settings && typeof settings === 'object' && !Array.isArray(settings)) {
|
|
2787
|
+
Object.keys(settings).forEach((key) => {
|
|
2788
|
+
if (settings[key] === undefined || settings[key] === null || settings[key] === '') {
|
|
2789
|
+
delete settings[key];
|
|
2790
|
+
}
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
return normalized;
|
|
2794
|
+
}
|
|
2795
|
+
function ensureAuthoringCompleteness(config) {
|
|
2796
|
+
const next = cloneJson(config);
|
|
2797
|
+
const advanced = next.behavior?.filtering?.advancedFilters;
|
|
2798
|
+
if (advanced &&
|
|
2799
|
+
typeof advanced === 'object' &&
|
|
2800
|
+
!Object.prototype.hasOwnProperty.call(advanced, 'settings')) {
|
|
2801
|
+
advanced.settings = {};
|
|
2802
|
+
}
|
|
2803
|
+
return next;
|
|
2804
|
+
}
|
|
2805
|
+
function normalizeBindings(bindings) {
|
|
2806
|
+
if (!bindings || typeof bindings !== 'object')
|
|
2807
|
+
return undefined;
|
|
2808
|
+
const normalized = {
|
|
2809
|
+
resourcePath: normalizeResourcePath(bindings.resourcePath),
|
|
2810
|
+
idField: normalizeIdField(bindings.idField),
|
|
2811
|
+
horizontalScroll: normalizeHorizontalScroll(bindings.horizontalScroll),
|
|
2812
|
+
};
|
|
2813
|
+
return stripUndefinedShallow(normalized);
|
|
2814
|
+
}
|
|
2815
|
+
function resolveEffectiveDataMode(doc, context) {
|
|
2816
|
+
const resourcePath = trimString(doc.bindings?.resourcePath);
|
|
2817
|
+
if (resourcePath)
|
|
2818
|
+
return 'remote';
|
|
2819
|
+
const localModeFlag = readBoolean(doc.config?.behavior?.localDataMode?.enabled);
|
|
2820
|
+
const effectiveLocalFlag = localModeFlag ?? (context?.localDataModeDefaultEnabled === true);
|
|
2821
|
+
if (context?.hasLocalDataInput && effectiveLocalFlag) {
|
|
2822
|
+
return 'local';
|
|
2823
|
+
}
|
|
2824
|
+
return 'empty';
|
|
2825
|
+
}
|
|
2826
|
+
function deriveBindingsDiff(next, current) {
|
|
2827
|
+
if (next === undefined) {
|
|
2828
|
+
return {
|
|
2829
|
+
resourcePathChanged: false,
|
|
2830
|
+
idFieldChanged: false,
|
|
2831
|
+
horizontalScrollChanged: false,
|
|
2832
|
+
requiresDataRefresh: false,
|
|
2833
|
+
};
|
|
2834
|
+
}
|
|
2835
|
+
const normalizedNext = normalizeBindings(next) || {};
|
|
2836
|
+
const normalizedCurrent = normalizeBindings(current) || {};
|
|
2837
|
+
const resourcePathChanged = normalizeResourcePath(normalizedNext.resourcePath) !==
|
|
2838
|
+
normalizeResourcePath(normalizedCurrent.resourcePath);
|
|
2839
|
+
const idFieldChanged = normalizeIdField(normalizedNext.idField) !==
|
|
2840
|
+
normalizeIdField(normalizedCurrent.idField);
|
|
2841
|
+
const horizontalScrollChanged = normalizeHorizontalScroll(normalizedNext.horizontalScroll) !==
|
|
2842
|
+
normalizeHorizontalScroll(normalizedCurrent.horizontalScroll);
|
|
2843
|
+
return {
|
|
2844
|
+
resourcePathChanged,
|
|
2845
|
+
idFieldChanged,
|
|
2846
|
+
horizontalScrollChanged,
|
|
2847
|
+
requiresDataRefresh: resourcePathChanged,
|
|
2848
|
+
};
|
|
2849
|
+
}
|
|
2850
|
+
function deriveRuntimePlan(bindings, diff) {
|
|
2851
|
+
if (bindings === undefined) {
|
|
2852
|
+
return {
|
|
2853
|
+
reconnectDataSource: false,
|
|
2854
|
+
reloadSchema: false,
|
|
2855
|
+
fetchData: false,
|
|
2856
|
+
refreshLocalView: false,
|
|
2857
|
+
};
|
|
2858
|
+
}
|
|
2859
|
+
const resourcePath = trimString(bindings?.resourcePath);
|
|
2860
|
+
if (resourcePath) {
|
|
2861
|
+
return {
|
|
2862
|
+
reconnectDataSource: !!diff?.resourcePathChanged,
|
|
2863
|
+
reloadSchema: !!diff?.resourcePathChanged,
|
|
2864
|
+
fetchData: !!diff?.requiresDataRefresh,
|
|
2865
|
+
refreshLocalView: false,
|
|
2866
|
+
};
|
|
2867
|
+
}
|
|
2868
|
+
return {
|
|
2869
|
+
reconnectDataSource: !!diff?.resourcePathChanged,
|
|
2870
|
+
reloadSchema: false,
|
|
2871
|
+
fetchData: false,
|
|
2872
|
+
refreshLocalView: true,
|
|
2873
|
+
};
|
|
2874
|
+
}
|
|
2875
|
+
function extractLegacyTableConfig(obj) {
|
|
2876
|
+
const clone = { ...obj };
|
|
2877
|
+
delete clone.__resourcePath__;
|
|
2878
|
+
delete clone.__resourcePathIntent__;
|
|
2879
|
+
delete clone.__idField__;
|
|
2880
|
+
delete clone.__horizontalScroll__;
|
|
2881
|
+
return asTableConfig(clone);
|
|
2882
|
+
}
|
|
2883
|
+
function stripLegacyRuntimeMarkers(config) {
|
|
2884
|
+
const clone = cloneJson(config);
|
|
2885
|
+
delete clone.__resourcePath__;
|
|
2886
|
+
delete clone.__resourcePathIntent__;
|
|
2887
|
+
delete clone.__idField__;
|
|
2888
|
+
delete clone.__horizontalScroll__;
|
|
2889
|
+
return clone;
|
|
2890
|
+
}
|
|
2891
|
+
function looksLikeLegacyPayload(obj) {
|
|
2892
|
+
return ('__resourcePath__' in obj ||
|
|
2893
|
+
'__resourcePathIntent__' in obj ||
|
|
2894
|
+
'__idField__' in obj ||
|
|
2895
|
+
'__horizontalScroll__' in obj);
|
|
2896
|
+
}
|
|
2897
|
+
function asTableConfig(value) {
|
|
2898
|
+
if (value && typeof value === 'object') {
|
|
2899
|
+
return value;
|
|
2900
|
+
}
|
|
2901
|
+
return { columns: [] };
|
|
2902
|
+
}
|
|
2903
|
+
function asRecord(value) {
|
|
2904
|
+
return value && typeof value === 'object'
|
|
2905
|
+
? value
|
|
2906
|
+
: {};
|
|
2907
|
+
}
|
|
2908
|
+
function hasOwnProperty(value, key) {
|
|
2909
|
+
return !!value && typeof value === 'object' && Object.prototype.hasOwnProperty.call(value, key);
|
|
2910
|
+
}
|
|
2911
|
+
function normalizeResourcePath(value) {
|
|
2912
|
+
if (value === undefined)
|
|
2913
|
+
return undefined;
|
|
2914
|
+
const trimmed = trimString(value);
|
|
2915
|
+
return trimmed || null;
|
|
2916
|
+
}
|
|
2917
|
+
function normalizeIdField(value) {
|
|
2918
|
+
const trimmed = trimString(value);
|
|
2919
|
+
return trimmed || undefined;
|
|
2920
|
+
}
|
|
2921
|
+
function normalizeHorizontalScroll(value) {
|
|
2922
|
+
const candidate = trimString(value);
|
|
2923
|
+
if (!candidate)
|
|
2924
|
+
return undefined;
|
|
2925
|
+
return HORIZONTAL_SCROLL_VALUES.includes(candidate) ? candidate : undefined;
|
|
2926
|
+
}
|
|
2927
|
+
function trimString(value) {
|
|
2928
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
2929
|
+
}
|
|
2930
|
+
function readBoolean(value) {
|
|
2931
|
+
if (value === true || value === false)
|
|
2932
|
+
return value;
|
|
2933
|
+
return undefined;
|
|
2934
|
+
}
|
|
2935
|
+
function cloneJson(value) {
|
|
2936
|
+
return JSON.parse(JSON.stringify(value ?? null));
|
|
2937
|
+
}
|
|
2938
|
+
function stripUndefinedShallow(value) {
|
|
2939
|
+
const entries = Object.entries(value).filter(([, v]) => v !== undefined);
|
|
2940
|
+
if (!entries.length)
|
|
2941
|
+
return undefined;
|
|
2942
|
+
return Object.fromEntries(entries);
|
|
2943
|
+
}
|
|
2944
|
+
function stripUndefinedDeep(value) {
|
|
2945
|
+
return JSON.parse(JSON.stringify(value));
|
|
2946
|
+
}
|
|
2947
|
+
function errorDiagnostic(code, message, path) {
|
|
2948
|
+
return { level: 'error', code, message, path };
|
|
2949
|
+
}
|
|
2950
|
+
function warnDiagnostic(code, message, path) {
|
|
2951
|
+
return { level: 'warning', code, message, path };
|
|
2952
|
+
}
|
|
2953
|
+
function infoDiagnostic(code, message, path) {
|
|
2954
|
+
return { level: 'info', code, message, path };
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2597
2957
|
class JsonConfigEditorComponent {
|
|
2598
2958
|
cdr;
|
|
2599
|
-
|
|
2600
|
-
|
|
2959
|
+
document = null;
|
|
2960
|
+
documentChange = new EventEmitter();
|
|
2601
2961
|
validationChange = new EventEmitter();
|
|
2602
2962
|
editorEvent = new EventEmitter();
|
|
2603
|
-
// JSON editing state
|
|
2604
2963
|
jsonText = '';
|
|
2605
2964
|
isValidJson = true;
|
|
2606
2965
|
jsonError = '';
|
|
2607
2966
|
unknownTopKeys = [];
|
|
2608
|
-
// Subjects para cleanup e debouncing
|
|
2609
2967
|
destroy$ = new Subject();
|
|
2610
2968
|
jsonTextChanges$ = new Subject();
|
|
2611
2969
|
constructor(cdr) {
|
|
2612
2970
|
this.cdr = cdr;
|
|
2613
|
-
// Setup debounced JSON validation
|
|
2614
2971
|
this.jsonTextChanges$
|
|
2615
2972
|
.pipe(debounceTime(300), takeUntil(this.destroy$))
|
|
2616
|
-
.subscribe(text => {
|
|
2973
|
+
.subscribe((text) => {
|
|
2617
2974
|
this.validateJson(text);
|
|
2618
2975
|
});
|
|
2619
2976
|
}
|
|
2620
2977
|
ngOnInit() {
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
this.jsonText = JSON.stringify(this.config, null, 2);
|
|
2978
|
+
if (this.document) {
|
|
2979
|
+
this.jsonText = JSON.stringify(serializeTableAuthoringDocument(this.document), null, 2);
|
|
2624
2980
|
this.validateJson(this.jsonText);
|
|
2625
2981
|
}
|
|
2626
2982
|
}
|
|
@@ -2632,99 +2988,87 @@ class JsonConfigEditorComponent {
|
|
|
2632
2988
|
this.jsonTextChanges$.next(text);
|
|
2633
2989
|
}
|
|
2634
2990
|
applyJsonChanges() {
|
|
2635
|
-
if (!this.isValidJson)
|
|
2991
|
+
if (!this.isValidJson)
|
|
2636
2992
|
return;
|
|
2637
|
-
}
|
|
2638
2993
|
try {
|
|
2639
|
-
const
|
|
2640
|
-
|
|
2641
|
-
this.
|
|
2642
|
-
// Emitir evento de aplicação
|
|
2994
|
+
const newDocument = parseLegacyOrTableDocument(JSON.parse(this.jsonText));
|
|
2995
|
+
const diagnostics = validateTableAuthoringDocument(newDocument);
|
|
2996
|
+
this.documentChange.emit(newDocument);
|
|
2643
2997
|
this.editorEvent.emit({
|
|
2644
2998
|
type: 'apply',
|
|
2645
2999
|
payload: {
|
|
2646
|
-
isValid:
|
|
2647
|
-
|
|
2648
|
-
|
|
3000
|
+
isValid: !diagnostics.some((item) => item.level === 'error'),
|
|
3001
|
+
document: newDocument,
|
|
3002
|
+
diagnostics,
|
|
3003
|
+
},
|
|
2649
3004
|
});
|
|
2650
3005
|
}
|
|
2651
|
-
catch
|
|
2652
|
-
const errorResult = {
|
|
2653
|
-
isValid: false,
|
|
2654
|
-
error: 'Erro ao aplicar configuração JSON'
|
|
2655
|
-
};
|
|
3006
|
+
catch {
|
|
2656
3007
|
this.editorEvent.emit({
|
|
2657
3008
|
type: 'apply',
|
|
2658
|
-
payload:
|
|
3009
|
+
payload: {
|
|
3010
|
+
isValid: false,
|
|
3011
|
+
error: 'Erro ao aplicar documento JSON',
|
|
3012
|
+
},
|
|
2659
3013
|
});
|
|
2660
3014
|
}
|
|
2661
3015
|
}
|
|
2662
3016
|
formatJson() {
|
|
2663
|
-
if (!this.isValidJson)
|
|
3017
|
+
if (!this.isValidJson)
|
|
2664
3018
|
return;
|
|
2665
|
-
}
|
|
2666
3019
|
try {
|
|
2667
|
-
const
|
|
2668
|
-
|
|
2669
|
-
|
|
3020
|
+
const document = parseLegacyOrTableDocument(JSON.parse(this.jsonText));
|
|
3021
|
+
const diagnostics = validateTableAuthoringDocument(document);
|
|
3022
|
+
this.jsonText = JSON.stringify(serializeTableAuthoringDocument(document), null, 2);
|
|
2670
3023
|
this.editorEvent.emit({
|
|
2671
3024
|
type: 'format',
|
|
2672
3025
|
payload: {
|
|
2673
|
-
isValid:
|
|
2674
|
-
|
|
2675
|
-
|
|
3026
|
+
isValid: !diagnostics.some((item) => item.level === 'error'),
|
|
3027
|
+
document,
|
|
3028
|
+
diagnostics,
|
|
3029
|
+
},
|
|
2676
3030
|
});
|
|
2677
3031
|
this.cdr.markForCheck();
|
|
2678
3032
|
}
|
|
2679
|
-
catch
|
|
3033
|
+
catch {
|
|
2680
3034
|
this.editorEvent.emit({
|
|
2681
3035
|
type: 'format',
|
|
2682
3036
|
payload: {
|
|
2683
3037
|
isValid: false,
|
|
2684
|
-
error: 'Erro ao formatar JSON'
|
|
2685
|
-
}
|
|
3038
|
+
error: 'Erro ao formatar JSON',
|
|
3039
|
+
},
|
|
2686
3040
|
});
|
|
2687
3041
|
}
|
|
2688
3042
|
}
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
*/
|
|
2692
|
-
updateJsonFromConfig(config) {
|
|
2693
|
-
this.jsonText = JSON.stringify(config, null, 2);
|
|
3043
|
+
updateJsonFromDocument(document) {
|
|
3044
|
+
this.jsonText = JSON.stringify(serializeTableAuthoringDocument(document), null, 2);
|
|
2694
3045
|
this.validateJson(this.jsonText);
|
|
2695
3046
|
}
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
*/
|
|
2699
|
-
getCurrentConfig() {
|
|
2700
|
-
if (!this.isValidJson) {
|
|
3047
|
+
getCurrentDocument() {
|
|
3048
|
+
if (!this.isValidJson)
|
|
2701
3049
|
return null;
|
|
2702
|
-
}
|
|
2703
3050
|
try {
|
|
2704
|
-
return JSON.parse(this.jsonText);
|
|
3051
|
+
return parseLegacyOrTableDocument(JSON.parse(this.jsonText));
|
|
2705
3052
|
}
|
|
2706
3053
|
catch {
|
|
2707
3054
|
return null;
|
|
2708
3055
|
}
|
|
2709
3056
|
}
|
|
2710
|
-
/**
|
|
2711
|
-
* Método público para verificar se há alterações
|
|
2712
|
-
*/
|
|
2713
3057
|
hasChanges() {
|
|
2714
|
-
if (!this.
|
|
3058
|
+
if (!this.document)
|
|
2715
3059
|
return false;
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
if (!currentConfig) {
|
|
3060
|
+
const currentDocument = this.getCurrentDocument();
|
|
3061
|
+
if (!currentDocument)
|
|
2719
3062
|
return false;
|
|
2720
|
-
|
|
2721
|
-
|
|
3063
|
+
return JSON.stringify(serializeTableAuthoringDocument(this.document))
|
|
3064
|
+
!== JSON.stringify(serializeTableAuthoringDocument(currentDocument));
|
|
2722
3065
|
}
|
|
2723
3066
|
validateJson(text) {
|
|
2724
3067
|
const result = {
|
|
2725
3068
|
isValid: false,
|
|
2726
3069
|
error: undefined,
|
|
2727
|
-
|
|
3070
|
+
document: undefined,
|
|
3071
|
+
diagnostics: [],
|
|
2728
3072
|
};
|
|
2729
3073
|
if (!text.trim()) {
|
|
2730
3074
|
result.error = 'JSON não pode estar vazio';
|
|
@@ -2733,32 +3077,19 @@ class JsonConfigEditorComponent {
|
|
|
2733
3077
|
}
|
|
2734
3078
|
try {
|
|
2735
3079
|
const parsed = JSON.parse(text);
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
}
|
|
2750
|
-
result.isValid = true;
|
|
2751
|
-
result.config = parsed;
|
|
2752
|
-
// Detect unknown top-level keys (non-blocking)
|
|
2753
|
-
try {
|
|
2754
|
-
const allowed = [
|
|
2755
|
-
'meta', 'columns', 'behavior', 'appearance', 'toolbar', 'actions', 'export', 'messages', 'localization', 'data', 'theme', 'performance', 'plugins', 'accessibility', 'rowConditionalStyles'
|
|
2756
|
-
];
|
|
2757
|
-
const keys = Object.keys(parsed || {});
|
|
2758
|
-
this.unknownTopKeys = keys.filter((k) => !allowed.includes(k));
|
|
2759
|
-
}
|
|
2760
|
-
catch {
|
|
2761
|
-
this.unknownTopKeys = [];
|
|
3080
|
+
const document = parseLegacyOrTableDocument(parsed);
|
|
3081
|
+
const diagnostics = validateTableAuthoringDocument(document);
|
|
3082
|
+
const hasErrors = diagnostics.some((item) => item.level === 'error');
|
|
3083
|
+
result.isValid = !hasErrors;
|
|
3084
|
+
result.document = document;
|
|
3085
|
+
result.diagnostics = diagnostics;
|
|
3086
|
+
const allowed = ['kind', 'version', 'config', 'bindings'];
|
|
3087
|
+
const keys = Object.keys(parsed || {});
|
|
3088
|
+
this.unknownTopKeys = keys.filter((key) => !allowed.includes(key));
|
|
3089
|
+
if (hasErrors) {
|
|
3090
|
+
result.error =
|
|
3091
|
+
diagnostics.find((item) => item.level === 'error')?.message ||
|
|
3092
|
+
'Documento de autoria inválido';
|
|
2762
3093
|
}
|
|
2763
3094
|
this.updateValidationState(result);
|
|
2764
3095
|
}
|
|
@@ -2770,32 +3101,28 @@ class JsonConfigEditorComponent {
|
|
|
2770
3101
|
updateValidationState(result) {
|
|
2771
3102
|
this.isValidJson = result.isValid;
|
|
2772
3103
|
this.jsonError = result.error || '';
|
|
2773
|
-
// Emitir evento de validação
|
|
2774
3104
|
this.validationChange.emit(result);
|
|
2775
|
-
// Emitir evento genérico do editor
|
|
2776
3105
|
this.editorEvent.emit({
|
|
2777
3106
|
type: 'validation',
|
|
2778
|
-
payload: result
|
|
3107
|
+
payload: result,
|
|
2779
3108
|
});
|
|
2780
3109
|
this.cdr.markForCheck();
|
|
2781
3110
|
}
|
|
2782
3111
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: JsonConfigEditorComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
2783
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: JsonConfigEditorComponent, isStandalone: true, selector: "json-config-editor", inputs: {
|
|
3112
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: JsonConfigEditorComponent, isStandalone: true, selector: "json-config-editor", inputs: { document: "document" }, outputs: { documentChange: "documentChange", validationChange: "validationChange", editorEvent: "editorEvent" }, ngImport: i0, template: `
|
|
2784
3113
|
<div class="json-config-editor">
|
|
2785
|
-
<!-- Educational Card -->
|
|
2786
3114
|
<mat-card class="educational-card">
|
|
2787
3115
|
<mat-card-header>
|
|
2788
3116
|
<mat-icon mat-card-avatar class="card-icon">data_object</mat-icon>
|
|
2789
3117
|
<mat-card-title>Edição Avançada JSON</mat-card-title>
|
|
2790
3118
|
</mat-card-header>
|
|
2791
3119
|
<mat-card-content>
|
|
2792
|
-
<p><strong>Para usuários avançados:</strong> Esta seção permite ajustar
|
|
3120
|
+
<p><strong>Para usuários avançados:</strong> Esta seção permite ajustar o documento canônico de autoria da tabela
|
|
2793
3121
|
diretamente via JSON. <strong>Atenção:</strong> Alterações aqui podem sobrescrever configurações
|
|
2794
3122
|
visuais nas outras abas.</p>
|
|
2795
3123
|
</mat-card-content>
|
|
2796
3124
|
</mat-card>
|
|
2797
3125
|
|
|
2798
|
-
<!-- JSON Editor Section -->
|
|
2799
3126
|
<div class="json-editor-section">
|
|
2800
3127
|
<div class="json-editor-toolbar">
|
|
2801
3128
|
<button mat-button (click)="formatJson()" [disabled]="!isValidJson">
|
|
@@ -2809,12 +3136,12 @@ class JsonConfigEditorComponent {
|
|
|
2809
3136
|
</div>
|
|
2810
3137
|
|
|
2811
3138
|
<mat-form-field appearance="outline" class="json-textarea-field">
|
|
2812
|
-
<mat-label>
|
|
3139
|
+
<mat-label>Documento JSON</mat-label>
|
|
2813
3140
|
<textarea
|
|
2814
3141
|
matInput
|
|
2815
3142
|
[(ngModel)]="jsonText"
|
|
2816
3143
|
(ngModelChange)="onJsonTextChange($event)"
|
|
2817
|
-
placeholder="Edite
|
|
3144
|
+
placeholder="Edite o documento JSON aqui..."
|
|
2818
3145
|
rows="20"
|
|
2819
3146
|
spellcheck="false"
|
|
2820
3147
|
class="json-textarea">
|
|
@@ -2838,23 +3165,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
2838
3165
|
MatIconModule,
|
|
2839
3166
|
MatFormFieldModule,
|
|
2840
3167
|
MatInputModule,
|
|
2841
|
-
MatCardModule
|
|
3168
|
+
MatCardModule,
|
|
2842
3169
|
], template: `
|
|
2843
3170
|
<div class="json-config-editor">
|
|
2844
|
-
<!-- Educational Card -->
|
|
2845
3171
|
<mat-card class="educational-card">
|
|
2846
3172
|
<mat-card-header>
|
|
2847
3173
|
<mat-icon mat-card-avatar class="card-icon">data_object</mat-icon>
|
|
2848
3174
|
<mat-card-title>Edição Avançada JSON</mat-card-title>
|
|
2849
3175
|
</mat-card-header>
|
|
2850
3176
|
<mat-card-content>
|
|
2851
|
-
<p><strong>Para usuários avançados:</strong> Esta seção permite ajustar
|
|
3177
|
+
<p><strong>Para usuários avançados:</strong> Esta seção permite ajustar o documento canônico de autoria da tabela
|
|
2852
3178
|
diretamente via JSON. <strong>Atenção:</strong> Alterações aqui podem sobrescrever configurações
|
|
2853
3179
|
visuais nas outras abas.</p>
|
|
2854
3180
|
</mat-card-content>
|
|
2855
3181
|
</mat-card>
|
|
2856
3182
|
|
|
2857
|
-
<!-- JSON Editor Section -->
|
|
2858
3183
|
<div class="json-editor-section">
|
|
2859
3184
|
<div class="json-editor-toolbar">
|
|
2860
3185
|
<button mat-button (click)="formatJson()" [disabled]="!isValidJson">
|
|
@@ -2868,12 +3193,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
2868
3193
|
</div>
|
|
2869
3194
|
|
|
2870
3195
|
<mat-form-field appearance="outline" class="json-textarea-field">
|
|
2871
|
-
<mat-label>
|
|
3196
|
+
<mat-label>Documento JSON</mat-label>
|
|
2872
3197
|
<textarea
|
|
2873
3198
|
matInput
|
|
2874
3199
|
[(ngModel)]="jsonText"
|
|
2875
3200
|
(ngModelChange)="onJsonTextChange($event)"
|
|
2876
|
-
placeholder="Edite
|
|
3201
|
+
placeholder="Edite o documento JSON aqui..."
|
|
2877
3202
|
rows="20"
|
|
2878
3203
|
spellcheck="false"
|
|
2879
3204
|
class="json-textarea">
|
|
@@ -2887,9 +3212,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
2887
3212
|
</div>
|
|
2888
3213
|
</div>
|
|
2889
3214
|
`, styles: [".json-config-editor{display:flex;flex-direction:column;height:100%}.educational-card{margin-bottom:24px;background-color:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary)}.educational-card .mat-mdc-card-header{padding-bottom:8px}.card-icon{background-color:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);font-size:20px;width:40px;height:40px;display:flex;align-items:center;justify-content:center}.educational-card .mat-mdc-card-title{font-size:1.1rem;font-weight:500;color:var(--md-sys-color-on-surface)}.educational-card .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant);line-height:1.5}.json-editor-section{flex:1;display:flex;flex-direction:column}.json-editor-toolbar{display:flex;gap:12px;margin-bottom:16px;padding:12px;background-color:var(--md-sys-color-surface-container-low);border-radius:8px;border:1px solid var(--md-sys-color-outline-variant)}.json-textarea-field{width:100%;flex:1}.json-textarea{font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace!important;font-size:13px!important;line-height:1.4!important;height:100%!important;min-height:300px!important;white-space:pre!important;overflow-wrap:normal!important;overflow-x:auto!important;resize:none!important}.valid-hint{color:var(--md-sys-color-primary)!important}@media(max-width:768px){.json-editor-toolbar{flex-direction:column;gap:8px}.json-textarea{font-size:12px!important;min-height:300px!important}}\n"] }]
|
|
2890
|
-
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: {
|
|
3215
|
+
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { document: [{
|
|
2891
3216
|
type: Input
|
|
2892
|
-
}],
|
|
3217
|
+
}], documentChange: [{
|
|
2893
3218
|
type: Output
|
|
2894
3219
|
}], validationChange: [{
|
|
2895
3220
|
type: Output
|
|
@@ -28289,6 +28614,7 @@ class PraxisTableConfigEditor {
|
|
|
28289
28614
|
serverSchemaHash;
|
|
28290
28615
|
idFieldDiverges = false;
|
|
28291
28616
|
schemaHashDiverges = false;
|
|
28617
|
+
schemaSnapshotMissing = false;
|
|
28292
28618
|
// V2 specific configs for advanced editing
|
|
28293
28619
|
isV2Config = false;
|
|
28294
28620
|
// Estado do componente
|
|
@@ -28367,10 +28693,11 @@ class PraxisTableConfigEditor {
|
|
|
28367
28693
|
const hs = this.panelData?.horizontalScroll || 'auto';
|
|
28368
28694
|
this.horizontalScroll = hs;
|
|
28369
28695
|
this.initialHorizontalScroll = hs;
|
|
28370
|
-
// Divergência: idField e
|
|
28696
|
+
// Divergência: idField efetivo e snapshot persistido de schema
|
|
28371
28697
|
const cfgHash = config?.meta?.serverHash;
|
|
28372
28698
|
this.idFieldDiverges = !!(this.serverIdField && this.idField && this.serverIdField !== this.idField);
|
|
28373
28699
|
this.schemaHashDiverges = !!(this.serverSchemaHash && cfgHash && this.serverSchemaHash !== cfgHash);
|
|
28700
|
+
this.schemaSnapshotMissing = !!(this.serverSchemaHash && !cfgHash);
|
|
28374
28701
|
// Validar configuração recebida
|
|
28375
28702
|
if (!config || typeof config !== 'object') {
|
|
28376
28703
|
console.error('PTABLE:config:invalid', config);
|
|
@@ -28414,9 +28741,17 @@ class PraxisTableConfigEditor {
|
|
|
28414
28741
|
}
|
|
28415
28742
|
}
|
|
28416
28743
|
// Event handlers para o JsonConfigEditorComponent
|
|
28417
|
-
onJsonConfigChange(
|
|
28418
|
-
this.debugLog('[PraxisTableConfigEditor] onJsonConfigChange received',
|
|
28419
|
-
|
|
28744
|
+
onJsonConfigChange(newValue) {
|
|
28745
|
+
this.debugLog('[PraxisTableConfigEditor] onJsonConfigChange received', newValue);
|
|
28746
|
+
const newDocument = newValue?.kind === 'praxis.table.editor'
|
|
28747
|
+
? newValue
|
|
28748
|
+
: createTableAuthoringDocument({ config: newValue });
|
|
28749
|
+
this.editedConfig = newDocument.config;
|
|
28750
|
+
if (Object.prototype.hasOwnProperty.call(newDocument, 'bindings')) {
|
|
28751
|
+
this.resourcePath = newDocument.bindings?.resourcePath ?? '';
|
|
28752
|
+
this.idField = newDocument.bindings?.idField || 'id';
|
|
28753
|
+
this.horizontalScroll = newDocument.bindings?.horizontalScroll || 'auto';
|
|
28754
|
+
}
|
|
28420
28755
|
this.enforceEffectiveModeStrategyConstraints();
|
|
28421
28756
|
// Update state directly
|
|
28422
28757
|
this.updateConfigurationVersion();
|
|
@@ -28431,7 +28766,7 @@ class PraxisTableConfigEditor {
|
|
|
28431
28766
|
onJsonEditorEvent(event) {
|
|
28432
28767
|
switch (event.type) {
|
|
28433
28768
|
case 'apply':
|
|
28434
|
-
if (event.payload.isValid && event.payload.
|
|
28769
|
+
if (event.payload.isValid && event.payload.document) {
|
|
28435
28770
|
this.showSuccess('Configuração JSON aplicada com sucesso!');
|
|
28436
28771
|
}
|
|
28437
28772
|
else {
|
|
@@ -28448,6 +28783,16 @@ class PraxisTableConfigEditor {
|
|
|
28448
28783
|
break;
|
|
28449
28784
|
}
|
|
28450
28785
|
}
|
|
28786
|
+
buildJsonAuthoringDocument() {
|
|
28787
|
+
return createTableAuthoringDocument({
|
|
28788
|
+
config: this.editedConfig,
|
|
28789
|
+
bindings: {
|
|
28790
|
+
resourcePath: (this.resourcePath || '').trim() || null,
|
|
28791
|
+
idField: (this.idField || '').trim() || undefined,
|
|
28792
|
+
horizontalScroll: this.horizontalScroll || 'auto',
|
|
28793
|
+
},
|
|
28794
|
+
});
|
|
28795
|
+
}
|
|
28451
28796
|
// Event handlers para o ColumnsConfigEditorComponent
|
|
28452
28797
|
onColumnsConfigChange(newConfig) {
|
|
28453
28798
|
// Normalize and short-circuit when nothing effectively changed to avoid loops
|
|
@@ -28838,26 +29183,7 @@ class PraxisTableConfigEditor {
|
|
|
28838
29183
|
getSettingsValue() {
|
|
28839
29184
|
this.syncFilterSettingsFromEditor();
|
|
28840
29185
|
this.enforceEffectiveModeStrategyConstraints();
|
|
28841
|
-
|
|
28842
|
-
const idKey = (this.idField || '').trim();
|
|
28843
|
-
const hs = this.horizontalScroll || 'auto';
|
|
28844
|
-
const out = { ...this.editedConfig };
|
|
28845
|
-
out.__resourcePathIntent__ = this.getResourcePathIntent();
|
|
28846
|
-
if (trimmed)
|
|
28847
|
-
out.__resourcePath__ = trimmed;
|
|
28848
|
-
if (idKey)
|
|
28849
|
-
out.__idField__ = idKey;
|
|
28850
|
-
out.__horizontalScroll__ = hs;
|
|
28851
|
-
return out;
|
|
28852
|
-
}
|
|
28853
|
-
getResourcePathIntent() {
|
|
28854
|
-
const current = (this.resourcePath || '').trim();
|
|
28855
|
-
const initial = (this.initialResourcePath || '').trim();
|
|
28856
|
-
if (current === initial)
|
|
28857
|
-
return 'unchanged';
|
|
28858
|
-
if (!current)
|
|
28859
|
-
return 'clear';
|
|
28860
|
-
return 'set';
|
|
29186
|
+
return this.buildJsonAuthoringDocument();
|
|
28861
29187
|
}
|
|
28862
29188
|
syncFilterSettingsFromEditor() {
|
|
28863
29189
|
const childCfg = this.filterSettingsEditor?.getSettingsValue?.();
|
|
@@ -28970,14 +29296,6 @@ class PraxisTableConfigEditor {
|
|
|
28970
29296
|
if (!this.serverIdField)
|
|
28971
29297
|
return;
|
|
28972
29298
|
this.idField = this.serverIdField;
|
|
28973
|
-
const meta = { ...(this.editedConfig.meta || {}) };
|
|
28974
|
-
meta.idField = this.serverIdField;
|
|
28975
|
-
meta.version = meta.version || '2.0.0';
|
|
28976
|
-
const now = new Date().toISOString();
|
|
28977
|
-
meta.updatedAt = now;
|
|
28978
|
-
if (!meta.createdAt)
|
|
28979
|
-
meta.createdAt = now;
|
|
28980
|
-
this.editedConfig = { ...this.editedConfig, meta };
|
|
28981
29299
|
this.idFieldDiverges = false;
|
|
28982
29300
|
this.updateCanSaveState();
|
|
28983
29301
|
this.showSuccess('Chave primária reconciliada com o servidor');
|
|
@@ -28986,15 +29304,8 @@ class PraxisTableConfigEditor {
|
|
|
28986
29304
|
onAcceptServerHash() {
|
|
28987
29305
|
if (!this.serverSchemaHash)
|
|
28988
29306
|
return;
|
|
28989
|
-
const meta = { ...(this.editedConfig.meta || {}) };
|
|
28990
|
-
meta.serverHash = this.serverSchemaHash;
|
|
28991
|
-
meta.version = meta.version || '2.0.0';
|
|
28992
|
-
const now = new Date().toISOString();
|
|
28993
|
-
meta.updatedAt = now;
|
|
28994
|
-
if (!meta.createdAt)
|
|
28995
|
-
meta.createdAt = now;
|
|
28996
|
-
this.editedConfig = { ...this.editedConfig, meta };
|
|
28997
29307
|
this.schemaHashDiverges = false;
|
|
29308
|
+
this.schemaSnapshotMissing = false;
|
|
28998
29309
|
this.updateCanSaveState();
|
|
28999
29310
|
this.showSuccess('Metadados de schema atualizados');
|
|
29000
29311
|
}
|
|
@@ -29085,8 +29396,6 @@ class PraxisTableConfigEditor {
|
|
|
29085
29396
|
// Recalcular estado de save após salvar overrides e config
|
|
29086
29397
|
this.updateCanSaveState();
|
|
29087
29398
|
const value = this.getSettingsValue();
|
|
29088
|
-
// Importante: retornar o mesmo valor de getSettingsValue()
|
|
29089
|
-
// para garantir que __resourcePath__ seja propagado pelo painel
|
|
29090
29399
|
return value;
|
|
29091
29400
|
}
|
|
29092
29401
|
catch (error) {
|
|
@@ -29278,7 +29587,7 @@ class PraxisTableConfigEditor {
|
|
|
29278
29587
|
this.behaviorFeedbackBurstTypes.clear();
|
|
29279
29588
|
}
|
|
29280
29589
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTableConfigEditor, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.TableConfigService }], target: i0.ɵɵFactoryTarget.Component });
|
|
29281
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisTableConfigEditor, isStandalone: true, selector: "praxis-table-config-editor", providers: [TableConfigService], viewQueries: [{ propertyName: "filterSettingsEditor", first: true, predicate: ["tableFilterSettingsRef"], descendants: true }, { propertyName: "behaviorEditor", first: true, predicate: BehaviorConfigEditorComponent, descendants: true }, { propertyName: "crudEditorSetter", first: true, predicate: ["crudEditorRef"], descendants: true }], ngImport: i0, template: " <mat-tab-group class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n <mat-tab *ngFor=\"let section of sections\">\n <ng-template mat-tab-label>\n @if (section.icon) { <mat-icon [praxisIcon]=\"section.icon\"></mat-icon> }\n <span [attr.data-testid]=\"'config-tab-' + section.id\">{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n <ng-container [ngSwitch]=\"section.id\">\n <div *ngSwitchCase=\"'connect'\" class=\"connect-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Recurso</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" placeholder=\"ex.: employees\" />\n <mat-icon matSuffix>link</mat-icon>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Chave prim\u00E1ria</mat-label>\n <input matInput [(ngModel)]=\"idField\" (ngModelChange)=\"onIdFieldChange($event)\" placeholder=\"ex.: id, uuid, codigo\" />\n <mat-icon matSuffix>fingerprint</mat-icon>\n </mat-form-field>\n <p class=\"connect-note\">Defina o recurso da API e, se necess\u00E1rio, a chave prim\u00E1ria.</p>\n\n <!-- Diverg\u00EAncia: idField -->\n @if (idFieldDiverges) { <div class=\"alert alert--warning connect-alert\">\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n A chave prim\u00E1ria no servidor diverge da configura\u00E7\u00E3o atual.\n <span class=\"connect-alert__meta\">(config: {{ idField || 'id' }} | servidor: {{ serverIdField || 'id' }})</span>\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onReconcileIdField()\">Reconciliar</button>\n </div> }\n\n <!-- Diverg\u00EAncia: serverHash -->\n @if (schemaHashDiverges) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n O schema no servidor foi atualizado desde a \u00FAltima sincroniza\u00E7\u00E3o.\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">Atualizar metadados</button>\n </div> }\n </div>\n\n <div *ngSwitchCase=\"'overview'\" class=\"overview-grid\">\n <div class=\"overview-row\">\n <mat-form-field appearance=\"outline\" class=\"hs-field\">\n <mat-label>Scroll Horizontal</mat-label>\n <mat-select [(ngModel)]=\"horizontalScroll\" (ngModelChange)=\"onHorizontalScrollChange($event)\">\n <mat-option value=\"auto\">Auto (padr\u00E3o)</mat-option>\n <mat-option value=\"wrap\">Wrap (quebrar linhas)</mat-option>\n <mat-option value=\"none\">Host controla</mat-option>\n </mat-select>\n </mat-form-field>\n <div class=\"help small\">Controla como a tabela lida com largura horizontal e barra de rolagem.</div>\n </div>\n @if (isEffectiveLocalMode()) { <div class=\"alert alert--info overview-mode-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text\">\n Modo local efetivo ativo: estrat\u00E9gias de pagina\u00E7\u00E3o e ordena\u00E7\u00E3o em servidor ficam bloqueadas e s\u00E3o salvas como cliente.\n </div>\n </div> }\n <behavior-config-editor\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n [forceClientStrategies]=\"isEffectiveLocalMode()\"\n (configChange)=\"onBehaviorConfigChange($event)\"\n (behaviorChange)=\"onBehaviorChange($event)\"\n ></behavior-config-editor>\n </div>\n\n <columns-config-editor\n *ngSwitchCase=\"'columns'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n (columnChange)=\"onColumnChange($event)\"\n ></columns-config-editor>\n\n <table-rules-editor\n *ngSwitchCase=\"'rules'\"\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n (configChange)=\"onRulesConfigChange($event)\"\n ></table-rules-editor>\n\n <header-appearance-editor\n *ngSwitchCase=\"'header'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n ></header-appearance-editor>\n\n <toolbar-actions-editor\n *ngSwitchCase=\"'toolbar'\"\n [config]=\"editedConfig\"\n (configChange)=\"onToolbarActionsConfigChange($event)\"\n (toolbarActionsChange)=\"onToolbarActionsChange($event)\"\n ></toolbar-actions-editor>\n\n <!-- Aba extra para integra\u00E7\u00F5es CRUD (vis\u00EDvel quando em contexto CRUD) -->\n <crud-integration-editor\n #crudEditorRef\n *ngSwitchCase=\"'crud'\"\n [tableId]=\"crudContext?.tableId || 'default'\"\n [crudContext]=\"crudContext\"\n [componentKeyId]=\"crudContext?.componentKeyId\"\n ></crud-integration-editor>\n\n <filter-settings\n #tableFilterSettingsRef\n *ngSwitchCase=\"'filters'\"\n [metadata]=\"getFilterSettingsMetas()\"\n [metadataSource]=\"isUsingFilterDtoMetas() ? 'filter-dto' : 'columns'\"\n [metadataLoading]=\"filterDtoMetasLoading\"\n [metadataErrorMsg]=\"filterDtoMetasErrorMsg\"\n [settings]=\"\n editedConfig.behavior?.filtering?.advancedFilters?.settings\n \"\n (settingsChange)=\"onFilterSettingsChange($event)\"\n ></filter-settings>\n\n <messages-localization-editor\n *ngSwitchCase=\"'messages'\"\n [config]=\"editedConfig\"\n (configChange)=\"onMessagesLocalizationConfigChange($event)\"\n (messagesLocalizationChange)=\"\n onMessagesLocalizationChange($event)\n \"\n ></messages-localization-editor>\n\n <confirm-dialog-appearance-editor\n *ngSwitchCase=\"'dialogs'\"\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n ></confirm-dialog-appearance-editor>\n\n\n <json-config-editor\n *ngSwitchCase=\"'json'\"\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n ></json-config-editor>\n </ng-container>\n </div>\n </mat-tab>\n </mat-tab-group>\n@if (statusMessage) { <div class=\"config-editor-status\">\n <span\n class=\"status-text\"\n [class.error]=\"hasErrors\"\n [class.success]=\"hasSuccess\"\n >{{ statusMessage }}</span\n >\n </div> }\n", styles: ["@charset \"UTF-8\";.config-tabs{flex:1 1 auto;display:flex;flex-direction:column}.config-tabs .mat-mdc-tab{min-width:120px}.config-tabs .mat-mdc-tab-body-wrapper,.config-tabs .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.config-tabs .mat-mdc-tab-body-content{height:100%;min-height:0;overflow:visible}.tab-content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:8px 8px 56px;box-sizing:border-box;overflow:visible}.overview-grid{display:grid;grid-template-columns:1fr;gap:12px}.overview-grid .overview-row{display:grid;grid-template-columns:280px 1fr;align-items:end;gap:12px}.overview-grid .overview-row .help.small{opacity:.75;font-size:12px}.overview-mode-alert{align-items:center}.connect-grid{display:grid;gap:12px;padding:8px;grid-template-columns:1fr 240px;align-items:start}.connect-note{grid-column:1/-1;margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.connect-alert{grid-column:1/-1;align-items:center}.connect-alert__text{flex:1}.connect-alert__meta{opacity:.85}.educational-card{margin-bottom:24px;background:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary);flex-shrink:0}.config-editor-status{padding:12px 16px;margin-top:auto;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);flex-shrink:0;position:relative}.status-text{font-size:.875rem;line-height:1.2}.status-text.error{color:var(--md-sys-color-error)}.status-text.success{color:var(--md-sys-color-primary)}@media(max-width:768px){.tab-content{padding:8px}.config-editor-status{padding:12px 16px}.connect-grid{grid-template-columns:1fr}.overview-grid .overview-row{grid-template-columns:1fr;align-items:start}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i4.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i4.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i4.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i6.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i6.MatLabel, selector: "mat-label" }, { kind: "directive", type: i6.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: JsonConfigEditorComponent, selector: "json-config-editor", inputs: ["config"], outputs: ["configChange", "validationChange", "editorEvent"] }, { kind: "component", type: ColumnsConfigEditorComponent, selector: "columns-config-editor", inputs: ["config"], outputs: ["configChange", "columnChange"] }, { kind: "component", type: BehaviorConfigEditorComponent, selector: "behavior-config-editor", inputs: ["config", "resourcePath", "forceClientStrategies"], outputs: ["configChange", "behaviorChange"] }, { kind: "component", type: HeaderAppearanceEditorComponent, selector: "header-appearance-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: ToolbarActionsEditorComponent, selector: "toolbar-actions-editor", inputs: ["config"], outputs: ["configChange", "toolbarActionsChange"] }, { kind: "component", type: MessagesLocalizationEditorComponent, selector: "messages-localization-editor", inputs: ["config"], outputs: ["configChange", "messagesLocalizationChange"] }, { kind: "component", type: FilterSettingsComponent, selector: "filter-settings", inputs: ["metadata", "metadataSource", "metadataLoading", "metadataErrorMsg", "settings", "configKey"], outputs: ["settingsChange"] }, { kind: "component", type: CrudIntegrationEditorComponent, selector: "crud-integration-editor", inputs: ["tableId", "crudContext", "componentKeyId"] }, { kind: "component", type: ConfirmDialogAppearanceEditorComponent, selector: "confirm-dialog-appearance-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: TableRulesEditorComponent, selector: "table-rules-editor", inputs: ["config", "resourcePath", "fields", "i18nRules", "dslFunctionRegistry", "debugLogs", "debugLevel"], outputs: ["configChange"] }] });
|
|
29590
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: PraxisTableConfigEditor, isStandalone: true, selector: "praxis-table-config-editor", providers: [TableConfigService], viewQueries: [{ propertyName: "filterSettingsEditor", first: true, predicate: ["tableFilterSettingsRef"], descendants: true }, { propertyName: "behaviorEditor", first: true, predicate: BehaviorConfigEditorComponent, descendants: true }, { propertyName: "crudEditorSetter", first: true, predicate: ["crudEditorRef"], descendants: true }], ngImport: i0, template: " <mat-tab-group class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n <mat-tab *ngFor=\"let section of sections\">\n <ng-template mat-tab-label>\n @if (section.icon) { <mat-icon [praxisIcon]=\"section.icon\"></mat-icon> }\n <span [attr.data-testid]=\"'config-tab-' + section.id\">{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n <ng-container [ngSwitch]=\"section.id\">\n <div *ngSwitchCase=\"'connect'\" class=\"connect-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Recurso</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" placeholder=\"ex.: employees\" />\n <mat-icon matSuffix>link</mat-icon>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Chave prim\u00E1ria</mat-label>\n <input matInput [(ngModel)]=\"idField\" (ngModelChange)=\"onIdFieldChange($event)\" placeholder=\"ex.: id, uuid, codigo\" />\n <mat-icon matSuffix>fingerprint</mat-icon>\n </mat-form-field>\n <p class=\"connect-note\">Defina o recurso da API e, se necess\u00E1rio, a chave prim\u00E1ria.</p>\n\n <!-- Diverg\u00EAncia: idField -->\n @if (idFieldDiverges) { <div class=\"alert alert--warning connect-alert\">\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n A chave prim\u00E1ria no servidor diverge da configura\u00E7\u00E3o atual.\n <span class=\"connect-alert__meta\">(config: {{ idField || 'id' }} | servidor: {{ serverIdField || 'id' }})</span>\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onReconcileIdField()\">Reconciliar</button>\n </div> }\n\n <!-- Diverg\u00EAncia: serverHash -->\n @if (schemaHashDiverges) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n O snapshot de schema salvo diverge do schema atual do servidor.\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">Atualizar metadados</button>\n </div> }\n\n @if (!schemaHashDiverges && schemaSnapshotMissing) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n Existe schema atual no servidor, mas ainda n\u00E3o h\u00E1 snapshot persistido para reconcilia\u00E7\u00E3o.\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">Salvar snapshot</button>\n </div> }\n </div>\n\n <div *ngSwitchCase=\"'overview'\" class=\"overview-grid\">\n <div class=\"overview-row\">\n <mat-form-field appearance=\"outline\" class=\"hs-field\">\n <mat-label>Scroll Horizontal</mat-label>\n <mat-select [(ngModel)]=\"horizontalScroll\" (ngModelChange)=\"onHorizontalScrollChange($event)\">\n <mat-option value=\"auto\">Auto (padr\u00E3o)</mat-option>\n <mat-option value=\"wrap\">Wrap (quebrar linhas)</mat-option>\n <mat-option value=\"none\">Host controla</mat-option>\n </mat-select>\n </mat-form-field>\n <div class=\"help small\">Controla como a tabela lida com largura horizontal e barra de rolagem.</div>\n </div>\n @if (isEffectiveLocalMode()) { <div class=\"alert alert--info overview-mode-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text\">\n Modo local efetivo ativo: estrat\u00E9gias de pagina\u00E7\u00E3o e ordena\u00E7\u00E3o em servidor ficam bloqueadas e s\u00E3o salvas como cliente.\n </div>\n </div> }\n <behavior-config-editor\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n [forceClientStrategies]=\"isEffectiveLocalMode()\"\n (configChange)=\"onBehaviorConfigChange($event)\"\n (behaviorChange)=\"onBehaviorChange($event)\"\n ></behavior-config-editor>\n </div>\n\n <columns-config-editor\n *ngSwitchCase=\"'columns'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n (columnChange)=\"onColumnChange($event)\"\n ></columns-config-editor>\n\n <table-rules-editor\n *ngSwitchCase=\"'rules'\"\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n (configChange)=\"onRulesConfigChange($event)\"\n ></table-rules-editor>\n\n <header-appearance-editor\n *ngSwitchCase=\"'header'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n ></header-appearance-editor>\n\n <toolbar-actions-editor\n *ngSwitchCase=\"'toolbar'\"\n [config]=\"editedConfig\"\n (configChange)=\"onToolbarActionsConfigChange($event)\"\n (toolbarActionsChange)=\"onToolbarActionsChange($event)\"\n ></toolbar-actions-editor>\n\n <!-- Aba extra para integra\u00E7\u00F5es CRUD (vis\u00EDvel quando em contexto CRUD) -->\n <crud-integration-editor\n #crudEditorRef\n *ngSwitchCase=\"'crud'\"\n [tableId]=\"crudContext?.tableId || 'default'\"\n [crudContext]=\"crudContext\"\n [componentKeyId]=\"crudContext?.componentKeyId\"\n ></crud-integration-editor>\n\n <filter-settings\n #tableFilterSettingsRef\n *ngSwitchCase=\"'filters'\"\n [metadata]=\"getFilterSettingsMetas()\"\n [metadataSource]=\"isUsingFilterDtoMetas() ? 'filter-dto' : 'columns'\"\n [metadataLoading]=\"filterDtoMetasLoading\"\n [metadataErrorMsg]=\"filterDtoMetasErrorMsg\"\n [settings]=\"\n editedConfig.behavior?.filtering?.advancedFilters?.settings\n \"\n (settingsChange)=\"onFilterSettingsChange($event)\"\n ></filter-settings>\n\n <messages-localization-editor\n *ngSwitchCase=\"'messages'\"\n [config]=\"editedConfig\"\n (configChange)=\"onMessagesLocalizationConfigChange($event)\"\n (messagesLocalizationChange)=\"\n onMessagesLocalizationChange($event)\n \"\n ></messages-localization-editor>\n\n <confirm-dialog-appearance-editor\n *ngSwitchCase=\"'dialogs'\"\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n ></confirm-dialog-appearance-editor>\n\n\n <json-config-editor\n *ngSwitchCase=\"'json'\"\n [document]=\"buildJsonAuthoringDocument()\"\n (documentChange)=\"onJsonConfigChange($event)\"\n (validationChange)=\"onJsonValidationChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n ></json-config-editor>\n </ng-container>\n </div>\n </mat-tab>\n </mat-tab-group>\n@if (statusMessage) { <div class=\"config-editor-status\">\n <span\n class=\"status-text\"\n [class.error]=\"hasErrors\"\n [class.success]=\"hasSuccess\"\n >{{ statusMessage }}</span\n >\n </div> }\n", styles: ["@charset \"UTF-8\";.config-tabs{flex:1 1 auto;display:flex;flex-direction:column}.config-tabs .mat-mdc-tab{min-width:120px}.config-tabs .mat-mdc-tab-body-wrapper,.config-tabs .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.config-tabs .mat-mdc-tab-body-content{height:100%;min-height:0;overflow:visible}.tab-content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:8px 8px 56px;box-sizing:border-box;overflow:visible}.overview-grid{display:grid;grid-template-columns:1fr;gap:12px}.overview-grid .overview-row{display:grid;grid-template-columns:280px 1fr;align-items:end;gap:12px}.overview-grid .overview-row .help.small{opacity:.75;font-size:12px}.overview-mode-alert{align-items:center}.connect-grid{display:grid;gap:12px;padding:8px;grid-template-columns:1fr 240px;align-items:start}.connect-note{grid-column:1/-1;margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.connect-alert{grid-column:1/-1;align-items:center}.connect-alert__text{flex:1}.connect-alert__meta{opacity:.85}.educational-card{margin-bottom:24px;background:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary);flex-shrink:0}.config-editor-status{padding:12px 16px;margin-top:auto;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);flex-shrink:0;position:relative}.status-text{font-size:.875rem;line-height:1.2}.status-text.error{color:var(--md-sys-color-error)}.status-text.success{color:var(--md-sys-color-primary)}@media(max-width:768px){.tab-content{padding:8px}.config-editor-status{padding:12px 16px}.connect-grid{grid-template-columns:1fr}.overview-grid .overview-row{grid-template-columns:1fr;align-items:start}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1$2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i5.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "directive", type: i4.MatTabLabel, selector: "[mat-tab-label], [matTabLabel]" }, { kind: "component", type: i4.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i4.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i6.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i6.MatLabel, selector: "mat-label" }, { kind: "directive", type: i6.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "component", type: JsonConfigEditorComponent, selector: "json-config-editor", inputs: ["document"], outputs: ["documentChange", "validationChange", "editorEvent"] }, { kind: "component", type: ColumnsConfigEditorComponent, selector: "columns-config-editor", inputs: ["config"], outputs: ["configChange", "columnChange"] }, { kind: "component", type: BehaviorConfigEditorComponent, selector: "behavior-config-editor", inputs: ["config", "resourcePath", "forceClientStrategies"], outputs: ["configChange", "behaviorChange"] }, { kind: "component", type: HeaderAppearanceEditorComponent, selector: "header-appearance-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: ToolbarActionsEditorComponent, selector: "toolbar-actions-editor", inputs: ["config"], outputs: ["configChange", "toolbarActionsChange"] }, { kind: "component", type: MessagesLocalizationEditorComponent, selector: "messages-localization-editor", inputs: ["config"], outputs: ["configChange", "messagesLocalizationChange"] }, { kind: "component", type: FilterSettingsComponent, selector: "filter-settings", inputs: ["metadata", "metadataSource", "metadataLoading", "metadataErrorMsg", "settings", "configKey"], outputs: ["settingsChange"] }, { kind: "component", type: CrudIntegrationEditorComponent, selector: "crud-integration-editor", inputs: ["tableId", "crudContext", "componentKeyId"] }, { kind: "component", type: ConfirmDialogAppearanceEditorComponent, selector: "confirm-dialog-appearance-editor", inputs: ["config"], outputs: ["configChange"] }, { kind: "component", type: TableRulesEditorComponent, selector: "table-rules-editor", inputs: ["config", "resourcePath", "fields", "i18nRules", "dslFunctionRegistry", "debugLogs", "debugLevel"], outputs: ["configChange"] }] });
|
|
29282
29591
|
}
|
|
29283
29592
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisTableConfigEditor, decorators: [{
|
|
29284
29593
|
type: Component,
|
|
@@ -29301,7 +29610,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
29301
29610
|
CrudIntegrationEditorComponent,
|
|
29302
29611
|
ConfirmDialogAppearanceEditorComponent,
|
|
29303
29612
|
TableRulesEditorComponent,
|
|
29304
|
-
], providers: [TableConfigService], template: " <mat-tab-group class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n <mat-tab *ngFor=\"let section of sections\">\n <ng-template mat-tab-label>\n @if (section.icon) { <mat-icon [praxisIcon]=\"section.icon\"></mat-icon> }\n <span [attr.data-testid]=\"'config-tab-' + section.id\">{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n <ng-container [ngSwitch]=\"section.id\">\n <div *ngSwitchCase=\"'connect'\" class=\"connect-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Recurso</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" placeholder=\"ex.: employees\" />\n <mat-icon matSuffix>link</mat-icon>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Chave prim\u00E1ria</mat-label>\n <input matInput [(ngModel)]=\"idField\" (ngModelChange)=\"onIdFieldChange($event)\" placeholder=\"ex.: id, uuid, codigo\" />\n <mat-icon matSuffix>fingerprint</mat-icon>\n </mat-form-field>\n <p class=\"connect-note\">Defina o recurso da API e, se necess\u00E1rio, a chave prim\u00E1ria.</p>\n\n <!-- Diverg\u00EAncia: idField -->\n @if (idFieldDiverges) { <div class=\"alert alert--warning connect-alert\">\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n A chave prim\u00E1ria no servidor diverge da configura\u00E7\u00E3o atual.\n <span class=\"connect-alert__meta\">(config: {{ idField || 'id' }} | servidor: {{ serverIdField || 'id' }})</span>\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onReconcileIdField()\">Reconciliar</button>\n </div> }\n\n <!-- Diverg\u00EAncia: serverHash -->\n @if (schemaHashDiverges) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n O schema
|
|
29613
|
+
], providers: [TableConfigService], template: " <mat-tab-group class=\"config-tabs\" [(selectedIndex)]=\"activeSectionIndex\">\n <mat-tab *ngFor=\"let section of sections\">\n <ng-template mat-tab-label>\n @if (section.icon) { <mat-icon [praxisIcon]=\"section.icon\"></mat-icon> }\n <span [attr.data-testid]=\"'config-tab-' + section.id\">{{ section.label }}</span>\n </ng-template>\n <div class=\"tab-content\">\n <ng-container [ngSwitch]=\"section.id\">\n <div *ngSwitchCase=\"'connect'\" class=\"connect-grid\">\n <mat-form-field appearance=\"outline\">\n <mat-label>Recurso</mat-label>\n <input matInput [(ngModel)]=\"resourcePath\" (ngModelChange)=\"onResourcePathChange($event)\" placeholder=\"ex.: employees\" />\n <mat-icon matSuffix>link</mat-icon>\n </mat-form-field>\n <mat-form-field appearance=\"outline\">\n <mat-label>Chave prim\u00E1ria</mat-label>\n <input matInput [(ngModel)]=\"idField\" (ngModelChange)=\"onIdFieldChange($event)\" placeholder=\"ex.: id, uuid, codigo\" />\n <mat-icon matSuffix>fingerprint</mat-icon>\n </mat-form-field>\n <p class=\"connect-note\">Defina o recurso da API e, se necess\u00E1rio, a chave prim\u00E1ria.</p>\n\n <!-- Diverg\u00EAncia: idField -->\n @if (idFieldDiverges) { <div class=\"alert alert--warning connect-alert\">\n <mat-icon class=\"alert__icon\">warning</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n A chave prim\u00E1ria no servidor diverge da configura\u00E7\u00E3o atual.\n <span class=\"connect-alert__meta\">(config: {{ idField || 'id' }} | servidor: {{ serverIdField || 'id' }})</span>\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onReconcileIdField()\">Reconciliar</button>\n </div> }\n\n <!-- Diverg\u00EAncia: serverHash -->\n @if (schemaHashDiverges) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n O snapshot de schema salvo diverge do schema atual do servidor.\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">Atualizar metadados</button>\n </div> }\n\n @if (!schemaHashDiverges && schemaSnapshotMissing) { <div class=\"alert alert--info connect-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text connect-alert__text\">\n Existe schema atual no servidor, mas ainda n\u00E3o h\u00E1 snapshot persistido para reconcilia\u00E7\u00E3o.\n </div>\n <button mat-stroked-button color=\"primary\" type=\"button\" (click)=\"onAcceptServerHash()\">Salvar snapshot</button>\n </div> }\n </div>\n\n <div *ngSwitchCase=\"'overview'\" class=\"overview-grid\">\n <div class=\"overview-row\">\n <mat-form-field appearance=\"outline\" class=\"hs-field\">\n <mat-label>Scroll Horizontal</mat-label>\n <mat-select [(ngModel)]=\"horizontalScroll\" (ngModelChange)=\"onHorizontalScrollChange($event)\">\n <mat-option value=\"auto\">Auto (padr\u00E3o)</mat-option>\n <mat-option value=\"wrap\">Wrap (quebrar linhas)</mat-option>\n <mat-option value=\"none\">Host controla</mat-option>\n </mat-select>\n </mat-form-field>\n <div class=\"help small\">Controla como a tabela lida com largura horizontal e barra de rolagem.</div>\n </div>\n @if (isEffectiveLocalMode()) { <div class=\"alert alert--info overview-mode-alert\">\n <mat-icon class=\"alert__icon\">info</mat-icon>\n <div class=\"alert__text\">\n Modo local efetivo ativo: estrat\u00E9gias de pagina\u00E7\u00E3o e ordena\u00E7\u00E3o em servidor ficam bloqueadas e s\u00E3o salvas como cliente.\n </div>\n </div> }\n <behavior-config-editor\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n [forceClientStrategies]=\"isEffectiveLocalMode()\"\n (configChange)=\"onBehaviorConfigChange($event)\"\n (behaviorChange)=\"onBehaviorChange($event)\"\n ></behavior-config-editor>\n </div>\n\n <columns-config-editor\n *ngSwitchCase=\"'columns'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n (columnChange)=\"onColumnChange($event)\"\n ></columns-config-editor>\n\n <table-rules-editor\n *ngSwitchCase=\"'rules'\"\n [config]=\"editedConfig\"\n [resourcePath]=\"resourcePath\"\n (configChange)=\"onRulesConfigChange($event)\"\n ></table-rules-editor>\n\n <header-appearance-editor\n *ngSwitchCase=\"'header'\"\n [config]=\"editedConfig\"\n (configChange)=\"onColumnsConfigChange($event)\"\n ></header-appearance-editor>\n\n <toolbar-actions-editor\n *ngSwitchCase=\"'toolbar'\"\n [config]=\"editedConfig\"\n (configChange)=\"onToolbarActionsConfigChange($event)\"\n (toolbarActionsChange)=\"onToolbarActionsChange($event)\"\n ></toolbar-actions-editor>\n\n <!-- Aba extra para integra\u00E7\u00F5es CRUD (vis\u00EDvel quando em contexto CRUD) -->\n <crud-integration-editor\n #crudEditorRef\n *ngSwitchCase=\"'crud'\"\n [tableId]=\"crudContext?.tableId || 'default'\"\n [crudContext]=\"crudContext\"\n [componentKeyId]=\"crudContext?.componentKeyId\"\n ></crud-integration-editor>\n\n <filter-settings\n #tableFilterSettingsRef\n *ngSwitchCase=\"'filters'\"\n [metadata]=\"getFilterSettingsMetas()\"\n [metadataSource]=\"isUsingFilterDtoMetas() ? 'filter-dto' : 'columns'\"\n [metadataLoading]=\"filterDtoMetasLoading\"\n [metadataErrorMsg]=\"filterDtoMetasErrorMsg\"\n [settings]=\"\n editedConfig.behavior?.filtering?.advancedFilters?.settings\n \"\n (settingsChange)=\"onFilterSettingsChange($event)\"\n ></filter-settings>\n\n <messages-localization-editor\n *ngSwitchCase=\"'messages'\"\n [config]=\"editedConfig\"\n (configChange)=\"onMessagesLocalizationConfigChange($event)\"\n (messagesLocalizationChange)=\"\n onMessagesLocalizationChange($event)\n \"\n ></messages-localization-editor>\n\n <confirm-dialog-appearance-editor\n *ngSwitchCase=\"'dialogs'\"\n [config]=\"editedConfig\"\n (configChange)=\"onJsonConfigChange($event)\"\n ></confirm-dialog-appearance-editor>\n\n\n <json-config-editor\n *ngSwitchCase=\"'json'\"\n [document]=\"buildJsonAuthoringDocument()\"\n (documentChange)=\"onJsonConfigChange($event)\"\n (validationChange)=\"onJsonValidationChange($event)\"\n (editorEvent)=\"onJsonEditorEvent($event)\"\n ></json-config-editor>\n </ng-container>\n </div>\n </mat-tab>\n </mat-tab-group>\n@if (statusMessage) { <div class=\"config-editor-status\">\n <span\n class=\"status-text\"\n [class.error]=\"hasErrors\"\n [class.success]=\"hasSuccess\"\n >{{ statusMessage }}</span\n >\n </div> }\n", styles: ["@charset \"UTF-8\";.config-tabs{flex:1 1 auto;display:flex;flex-direction:column}.config-tabs .mat-mdc-tab{min-width:120px}.config-tabs .mat-mdc-tab-body-wrapper,.config-tabs .mat-mdc-tab-group-container{flex:1 1 auto;min-height:0}.config-tabs .mat-mdc-tab-body-content{height:100%;min-height:0;overflow:visible}.tab-content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:8px 8px 56px;box-sizing:border-box;overflow:visible}.overview-grid{display:grid;grid-template-columns:1fr;gap:12px}.overview-grid .overview-row{display:grid;grid-template-columns:280px 1fr;align-items:end;gap:12px}.overview-grid .overview-row .help.small{opacity:.75;font-size:12px}.overview-mode-alert{align-items:center}.connect-grid{display:grid;gap:12px;padding:8px;grid-template-columns:1fr 240px;align-items:start}.connect-note{grid-column:1/-1;margin:0;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.connect-alert{grid-column:1/-1;align-items:center}.connect-alert__text{flex:1}.connect-alert__meta{opacity:.85}.educational-card{margin-bottom:24px;background:var(--md-sys-color-surface-container-low);border-left:4px solid var(--md-sys-color-primary);flex-shrink:0}.config-editor-status{padding:12px 16px;margin-top:auto;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);flex-shrink:0;position:relative}.status-text{font-size:.875rem;line-height:1.2}.status-text.error{color:var(--md-sys-color-error)}.status-text.success{color:var(--md-sys-color-primary)}@media(max-width:768px){.tab-content{padding:8px}.config-editor-status{padding:12px 16px}.connect-grid{grid-template-columns:1fr}.overview-grid .overview-row{grid-template-columns:1fr;align-items:start}:host{display:flex;flex-direction:column;flex:1 1 auto;min-height:0}}\n"] }]
|
|
29305
29614
|
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.TableConfigService }], propDecorators: { filterSettingsEditor: [{
|
|
29306
29615
|
type: ViewChild,
|
|
29307
29616
|
args: ['tableFilterSettingsRef']
|
|
@@ -29650,6 +29959,11 @@ class PraxisFilter {
|
|
|
29650
29959
|
configKey = null;
|
|
29651
29960
|
lastSchemaMeta;
|
|
29652
29961
|
schemaLoadVersion = 0;
|
|
29962
|
+
storedSchemaMetaCache = new Map();
|
|
29963
|
+
schemaMetaIndexCache;
|
|
29964
|
+
schemaMetaIndexLoadPromise;
|
|
29965
|
+
verifySchemaInFlight = new Map();
|
|
29966
|
+
lastSchemaVerificationByContext = new Map();
|
|
29653
29967
|
// Outdated state and persistence helpers
|
|
29654
29968
|
schemaOutdated = false;
|
|
29655
29969
|
anchorRef;
|
|
@@ -30075,13 +30389,17 @@ class PraxisFilter {
|
|
|
30075
30389
|
this.applyProvidedFieldMetadata();
|
|
30076
30390
|
}
|
|
30077
30391
|
else {
|
|
30078
|
-
|
|
30079
|
-
|
|
30080
|
-
//
|
|
30081
|
-
|
|
30392
|
+
const requiresSchemaBodyAtInit = (this.alwaysVisibleFields?.length || 0) > 0 ||
|
|
30393
|
+
(this.selectedFieldIds?.length || 0) > 0;
|
|
30394
|
+
// Skip the lightweight verification when we already know a full schema load is
|
|
30395
|
+
// required for rendering. loadSchemaViaClient() will fetch and persist metadata.
|
|
30396
|
+
if (requiresSchemaBodyAtInit) {
|
|
30082
30397
|
this.logFilterDebug('[PFILTER] ngOnInit: alwaysVisible present -> loadSchema');
|
|
30083
30398
|
this.loadSchema();
|
|
30084
30399
|
}
|
|
30400
|
+
else {
|
|
30401
|
+
this.verifyServerSchemaVersion().catch(() => { });
|
|
30402
|
+
}
|
|
30085
30403
|
}
|
|
30086
30404
|
}
|
|
30087
30405
|
ngAfterViewInit() {
|
|
@@ -30209,13 +30527,17 @@ class PraxisFilter {
|
|
|
30209
30527
|
}
|
|
30210
30528
|
else {
|
|
30211
30529
|
this.schemaMetas = undefined;
|
|
30212
|
-
|
|
30213
|
-
|
|
30214
|
-
if (
|
|
30530
|
+
const requiresSchemaBodyForPathChange = (this.alwaysVisibleFields?.length || 0) > 0 ||
|
|
30531
|
+
(this.selectedFieldIds?.length || 0) > 0;
|
|
30532
|
+
if (requiresSchemaBodyForPathChange) {
|
|
30215
30533
|
this.logFilterDebug('[PFILTER] ngOnChanges: alwaysVisible present -> loadSchema');
|
|
30216
30534
|
if (!this.schemaLoading)
|
|
30217
30535
|
this.loadSchema();
|
|
30218
30536
|
}
|
|
30537
|
+
else {
|
|
30538
|
+
// Verify lightweight on path changes only when a full schema fetch is not already required.
|
|
30539
|
+
this.verifyServerSchemaVersion().catch(() => { });
|
|
30540
|
+
}
|
|
30219
30541
|
}
|
|
30220
30542
|
}
|
|
30221
30543
|
if (changes['fieldMetadata']) {
|
|
@@ -30774,33 +31096,31 @@ class PraxisFilter {
|
|
|
30774
31096
|
try {
|
|
30775
31097
|
const keys = new Set();
|
|
30776
31098
|
const legacyScopeKey = this.getSchemaMetaKey();
|
|
30777
|
-
if (legacyScopeKey)
|
|
31099
|
+
if (legacyScopeKey) {
|
|
30778
31100
|
keys.add(legacyScopeKey);
|
|
31101
|
+
this.storedSchemaMetaCache.delete(legacyScopeKey);
|
|
31102
|
+
}
|
|
30779
31103
|
if (options?.context) {
|
|
30780
31104
|
const contextualKey = this.getSchemaMetaKeyForContext(options.context);
|
|
30781
|
-
if (contextualKey)
|
|
31105
|
+
if (contextualKey) {
|
|
30782
31106
|
keys.add(contextualKey);
|
|
31107
|
+
this.storedSchemaMetaCache.delete(contextualKey);
|
|
31108
|
+
}
|
|
30783
31109
|
}
|
|
30784
31110
|
if (options?.includeAllScoped) {
|
|
30785
31111
|
const indexKey = this.filterSchemaMetaIndexKey();
|
|
30786
31112
|
if (indexKey) {
|
|
30787
|
-
this.
|
|
30788
|
-
|
|
30789
|
-
|
|
30790
|
-
|
|
30791
|
-
|
|
30792
|
-
|
|
30793
|
-
|
|
30794
|
-
|
|
30795
|
-
|
|
30796
|
-
this.clearConfigStorageKey(key);
|
|
30797
|
-
});
|
|
30798
|
-
this.clearConfigStorageKey(indexKey);
|
|
30799
|
-
},
|
|
30800
|
-
error: () => {
|
|
30801
|
-
this.clearConfigStorageKey(indexKey);
|
|
30802
|
-
},
|
|
31113
|
+
this.schemaMetaIndexCache = undefined;
|
|
31114
|
+
this.schemaMetaIndexLoadPromise = undefined;
|
|
31115
|
+
const ids = Array.from(this.schemaMetaIndexCache ?? []);
|
|
31116
|
+
ids.forEach((schemaId) => {
|
|
31117
|
+
const key = this.filterSchemaMetaKeyForSchemaId(schemaId);
|
|
31118
|
+
if (key) {
|
|
31119
|
+
this.storedSchemaMetaCache.delete(key);
|
|
31120
|
+
this.clearConfigStorageKey(key);
|
|
31121
|
+
}
|
|
30803
31122
|
});
|
|
31123
|
+
this.clearConfigStorageKey(indexKey);
|
|
30804
31124
|
}
|
|
30805
31125
|
}
|
|
30806
31126
|
keys.forEach((key) => this.clearConfigStorageKey(key));
|
|
@@ -30812,6 +31132,11 @@ class PraxisFilter {
|
|
|
30812
31132
|
clearConfigStorageKey(key) {
|
|
30813
31133
|
if (!key)
|
|
30814
31134
|
return;
|
|
31135
|
+
this.storedSchemaMetaCache.delete(key);
|
|
31136
|
+
if (key === this.filterSchemaMetaIndexKey()) {
|
|
31137
|
+
this.schemaMetaIndexCache = undefined;
|
|
31138
|
+
this.schemaMetaIndexLoadPromise = undefined;
|
|
31139
|
+
}
|
|
30815
31140
|
try {
|
|
30816
31141
|
this.configStorage.clearConfig(key).pipe(take(1)).subscribe();
|
|
30817
31142
|
}
|
|
@@ -30822,35 +31147,79 @@ class PraxisFilter {
|
|
|
30822
31147
|
loadStoredSchemaMeta(key) {
|
|
30823
31148
|
if (!key)
|
|
30824
31149
|
return Promise.resolve(undefined);
|
|
31150
|
+
if (this.storedSchemaMetaCache.has(key)) {
|
|
31151
|
+
const cached = this.storedSchemaMetaCache.get(key);
|
|
31152
|
+
return Promise.resolve(cached ?? undefined);
|
|
31153
|
+
}
|
|
30825
31154
|
return firstValueFrom(this.configStorage.loadConfig(key))
|
|
30826
|
-
.then((value) =>
|
|
31155
|
+
.then((value) => {
|
|
31156
|
+
const next = value ?? null;
|
|
31157
|
+
this.storedSchemaMetaCache.set(key, next);
|
|
31158
|
+
return next ?? undefined;
|
|
31159
|
+
})
|
|
30827
31160
|
.catch(() => undefined);
|
|
30828
31161
|
}
|
|
30829
31162
|
rememberSchemaMetaContext(schemaId) {
|
|
30830
31163
|
const id = String(schemaId || '').trim();
|
|
30831
31164
|
if (!id)
|
|
30832
31165
|
return;
|
|
30833
|
-
const
|
|
30834
|
-
|
|
31166
|
+
const index = this.schemaMetaIndexCache ?? new Set();
|
|
31167
|
+
index.add(id);
|
|
31168
|
+
this.schemaMetaIndexCache = index;
|
|
31169
|
+
}
|
|
31170
|
+
ensureSchemaMetaIndexLoaded(indexKey) {
|
|
31171
|
+
if (this.schemaMetaIndexCache) {
|
|
31172
|
+
return Promise.resolve(this.schemaMetaIndexCache);
|
|
31173
|
+
}
|
|
31174
|
+
if (this.schemaMetaIndexLoadPromise) {
|
|
31175
|
+
return this.schemaMetaIndexLoadPromise;
|
|
31176
|
+
}
|
|
31177
|
+
this.schemaMetaIndexLoadPromise = Promise.resolve(new Set()).then((next) => {
|
|
31178
|
+
this.schemaMetaIndexCache = next;
|
|
31179
|
+
this.schemaMetaIndexLoadPromise = undefined;
|
|
31180
|
+
return next;
|
|
31181
|
+
});
|
|
31182
|
+
return this.schemaMetaIndexLoadPromise;
|
|
31183
|
+
}
|
|
31184
|
+
saveStoredSchemaMeta(key, next, schemaId) {
|
|
31185
|
+
const current = this.storedSchemaMetaCache.get(key) ?? null;
|
|
31186
|
+
if (this.stableSerialize(this.normalizeStoredSchemaMetaForComparison(current)) === this.stableSerialize(this.normalizeStoredSchemaMetaForComparison(next))) {
|
|
31187
|
+
if (schemaId)
|
|
31188
|
+
this.rememberSchemaMetaContext(schemaId);
|
|
30835
31189
|
return;
|
|
31190
|
+
}
|
|
31191
|
+
this.storedSchemaMetaCache.set(key, next);
|
|
31192
|
+
this.configStorage.saveConfig(key, next).pipe(take(1)).subscribe();
|
|
31193
|
+
if (schemaId)
|
|
31194
|
+
this.rememberSchemaMetaContext(schemaId);
|
|
31195
|
+
}
|
|
31196
|
+
stableSerialize(value) {
|
|
30836
31197
|
try {
|
|
30837
|
-
|
|
30838
|
-
.
|
|
30839
|
-
|
|
30840
|
-
|
|
30841
|
-
const
|
|
30842
|
-
|
|
30843
|
-
|
|
30844
|
-
|
|
30845
|
-
|
|
30846
|
-
.saveConfig(indexKey, Array.from(unique))
|
|
30847
|
-
.pipe(take(1))
|
|
30848
|
-
.subscribe();
|
|
31198
|
+
return JSON.stringify(value, (_key, nested) => {
|
|
31199
|
+
if (!nested || typeof nested !== 'object' || Array.isArray(nested)) {
|
|
31200
|
+
return nested;
|
|
31201
|
+
}
|
|
31202
|
+
const ordered = {};
|
|
31203
|
+
for (const key of Object.keys(nested).sort()) {
|
|
31204
|
+
ordered[key] = nested[key];
|
|
31205
|
+
}
|
|
31206
|
+
return ordered;
|
|
30849
31207
|
});
|
|
30850
31208
|
}
|
|
30851
31209
|
catch {
|
|
30852
|
-
|
|
31210
|
+
return String(value);
|
|
31211
|
+
}
|
|
31212
|
+
}
|
|
31213
|
+
normalizeStoredSchemaMetaForComparison(value) {
|
|
31214
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
31215
|
+
return value;
|
|
30853
31216
|
}
|
|
31217
|
+
const record = { ...value };
|
|
31218
|
+
delete record['lastVerifiedAt'];
|
|
31219
|
+
return record;
|
|
31220
|
+
}
|
|
31221
|
+
schemaVerificationContextKey(schemaId, context) {
|
|
31222
|
+
return `${schemaId}|${this.stableSerialize(context)}`;
|
|
30854
31223
|
}
|
|
30855
31224
|
getSchemaMetaKeyForContext(context) {
|
|
30856
31225
|
try {
|
|
@@ -30899,21 +31268,16 @@ class PraxisFilter {
|
|
|
30899
31268
|
if (!key)
|
|
30900
31269
|
return;
|
|
30901
31270
|
const nowIso = new Date().toISOString();
|
|
30902
|
-
this.
|
|
30903
|
-
|
|
30904
|
-
|
|
30905
|
-
|
|
30906
|
-
|
|
30907
|
-
|
|
30908
|
-
|
|
30909
|
-
|
|
30910
|
-
|
|
30911
|
-
|
|
30912
|
-
if (serverHash)
|
|
30913
|
-
next.serverHash = serverHash;
|
|
30914
|
-
this.configStorage.saveConfig(key, next).pipe(take(1)).subscribe();
|
|
30915
|
-
});
|
|
30916
|
-
this.rememberSchemaMetaContext(schemaId);
|
|
31271
|
+
const prev = this.storedSchemaMetaCache.get(key) ?? null;
|
|
31272
|
+
const next = {
|
|
31273
|
+
...(prev || {}),
|
|
31274
|
+
schemaId,
|
|
31275
|
+
schemaContext: context,
|
|
31276
|
+
lastVerifiedAt: nowIso,
|
|
31277
|
+
};
|
|
31278
|
+
if (serverHash)
|
|
31279
|
+
next.serverHash = serverHash;
|
|
31280
|
+
this.saveStoredSchemaMeta(key, next, schemaId);
|
|
30917
31281
|
}
|
|
30918
31282
|
catch {
|
|
30919
31283
|
// no-op: schema metadata persistence is best effort
|
|
@@ -32212,6 +32576,31 @@ class PraxisFilter {
|
|
|
32212
32576
|
const currentCtx = this.buildFilteredSchemaContext();
|
|
32213
32577
|
const currentSchemaContext = this.toSchemaIdContext(currentCtx);
|
|
32214
32578
|
const currentSchemaId = buildSchemaId(currentSchemaContext);
|
|
32579
|
+
const verifyContextKey = this.schemaVerificationContextKey(currentSchemaId, currentSchemaContext);
|
|
32580
|
+
const inFlight = this.verifySchemaInFlight.get(verifyContextKey);
|
|
32581
|
+
if (inFlight) {
|
|
32582
|
+
await inFlight;
|
|
32583
|
+
return;
|
|
32584
|
+
}
|
|
32585
|
+
const verifiedAt = this.lastSchemaVerificationByContext.get(verifyContextKey) || 0;
|
|
32586
|
+
if (verifiedAt && Date.now() - verifiedAt < 5000) {
|
|
32587
|
+
return;
|
|
32588
|
+
}
|
|
32589
|
+
const task = this.runSchemaVerification(currentCtx, currentSchemaContext, currentSchemaId, verifyContextKey);
|
|
32590
|
+
this.verifySchemaInFlight.set(verifyContextKey, task);
|
|
32591
|
+
try {
|
|
32592
|
+
await task;
|
|
32593
|
+
}
|
|
32594
|
+
finally {
|
|
32595
|
+
this.verifySchemaInFlight.delete(verifyContextKey);
|
|
32596
|
+
}
|
|
32597
|
+
}
|
|
32598
|
+
catch {
|
|
32599
|
+
// ignore verification errors; filter remains functional
|
|
32600
|
+
}
|
|
32601
|
+
}
|
|
32602
|
+
async runSchemaVerification(currentCtx, currentSchemaContext, currentSchemaId, verifyContextKey) {
|
|
32603
|
+
try {
|
|
32215
32604
|
const scopedMetaKey = this.getSchemaMetaKeyForContext(currentSchemaContext) || this.getSchemaMetaKey();
|
|
32216
32605
|
if (!scopedMetaKey)
|
|
32217
32606
|
return;
|
|
@@ -32253,13 +32642,10 @@ class PraxisFilter {
|
|
|
32253
32642
|
}
|
|
32254
32643
|
if (res.status === 304) {
|
|
32255
32644
|
try {
|
|
32256
|
-
this.
|
|
32257
|
-
.saveConfig(scopedMetaKey, metaToSave)
|
|
32258
|
-
.pipe(take(1))
|
|
32259
|
-
.subscribe();
|
|
32260
|
-
this.rememberSchemaMetaContext(currentSchemaId);
|
|
32645
|
+
this.saveStoredSchemaMeta(scopedMetaKey, metaToSave, currentSchemaId);
|
|
32261
32646
|
}
|
|
32262
32647
|
catch { }
|
|
32648
|
+
this.lastSchemaVerificationByContext.set(verifyContextKey, Date.now());
|
|
32263
32649
|
if (this.schemaOutdated)
|
|
32264
32650
|
this.schemaOutdated = false;
|
|
32265
32651
|
this.schemaStatusChange.emit({ outdated: false, serverHash: metaToSave.serverHash, lastVerifiedAt: metaToSave.lastVerifiedAt, formId: this.formId });
|
|
@@ -32269,13 +32655,10 @@ class PraxisFilter {
|
|
|
32269
32655
|
const newHash = res.schemaHash;
|
|
32270
32656
|
metaToSave.serverHash = newHash;
|
|
32271
32657
|
try {
|
|
32272
|
-
this.
|
|
32273
|
-
.saveConfig(scopedMetaKey, metaToSave)
|
|
32274
|
-
.pipe(take(1))
|
|
32275
|
-
.subscribe();
|
|
32276
|
-
this.rememberSchemaMetaContext(currentSchemaId);
|
|
32658
|
+
this.saveStoredSchemaMeta(scopedMetaKey, metaToSave, currentSchemaId);
|
|
32277
32659
|
}
|
|
32278
32660
|
catch { }
|
|
32661
|
+
this.lastSchemaVerificationByContext.set(verifyContextKey, Date.now());
|
|
32279
32662
|
// Only mark outdated when there was a previous base (hash or schemaMetas) and we are in edit mode
|
|
32280
32663
|
const hadBase = !!previousHash || !!this.schemaMetas?.length;
|
|
32281
32664
|
this.schemaOutdated = !!(this.editModeEnabled && hadBase);
|
|
@@ -33521,6 +33904,7 @@ class PraxisTable {
|
|
|
33521
33904
|
expansionDetailAbortByRowKey = new Map();
|
|
33522
33905
|
expansionDetailActiveTabByRowKey = new Map();
|
|
33523
33906
|
schemaFieldsSnapshot = [];
|
|
33907
|
+
runtimeSchemaMeta = {};
|
|
33524
33908
|
schemaError = false;
|
|
33525
33909
|
dataError = false;
|
|
33526
33910
|
errorMessage = null;
|
|
@@ -34072,6 +34456,9 @@ class PraxisTable {
|
|
|
34072
34456
|
this.selection.clear();
|
|
34073
34457
|
}
|
|
34074
34458
|
catch { }
|
|
34459
|
+
if (!(this.uncontrolledExpandedRowKeys instanceof Set)) {
|
|
34460
|
+
this.uncontrolledExpandedRowKeys = new Set();
|
|
34461
|
+
}
|
|
34075
34462
|
this.uncontrolledExpandedRowKeys.clear();
|
|
34076
34463
|
this.clearExpansionDetailRuntime();
|
|
34077
34464
|
}
|
|
@@ -34408,7 +34795,7 @@ class PraxisTable {
|
|
|
34408
34795
|
if (this.aiAdapter || this.aiAdapterLoadStarted)
|
|
34409
34796
|
return;
|
|
34410
34797
|
this.aiAdapterLoadStarted = true;
|
|
34411
|
-
import('./praxisui-table-table-ai.adapter-
|
|
34798
|
+
import('./praxisui-table-table-ai.adapter-C5rjLb8E.mjs')
|
|
34412
34799
|
.then(({ TableAiAdapter }) => {
|
|
34413
34800
|
this.aiAdapter = new TableAiAdapter(this);
|
|
34414
34801
|
this.cdr.markForCheck();
|
|
@@ -34554,6 +34941,11 @@ class PraxisTable {
|
|
|
34554
34941
|
catch { }
|
|
34555
34942
|
// Apply global defaults as fallback only for missing properties
|
|
34556
34943
|
this.ensureConfigDefaults();
|
|
34944
|
+
// ngOnInit is async and Angular does not await it before ngAfterContentInit.
|
|
34945
|
+
// Rebuild column runtime state here so hydrated persisted config can replace
|
|
34946
|
+
// any columns already derived from the initial @Input() config.
|
|
34947
|
+
this.setupColumns();
|
|
34948
|
+
this.applyDefaultSortIfNone();
|
|
34557
34949
|
// Re-apply unsupported-feature guards after persisted config hydration.
|
|
34558
34950
|
this.enforceUnsupportedFeatureGuards();
|
|
34559
34951
|
this.syncHorizontalScrollFromAppearanceConfig();
|
|
@@ -34695,66 +35087,6 @@ class PraxisTable {
|
|
|
34695
35087
|
inputsKey() {
|
|
34696
35088
|
return this.storageKey('table-inputs:');
|
|
34697
35089
|
}
|
|
34698
|
-
normalizeResourcePathIntent(value) {
|
|
34699
|
-
if (value === 'set' || value === 'clear' || value === 'unchanged') {
|
|
34700
|
-
return value;
|
|
34701
|
-
}
|
|
34702
|
-
return null;
|
|
34703
|
-
}
|
|
34704
|
-
resolveResourcePathIntentFromEditor(cfg) {
|
|
34705
|
-
const explicitIntent = this.normalizeResourcePathIntent(cfg?.__resourcePathIntent__);
|
|
34706
|
-
if (explicitIntent)
|
|
34707
|
-
return explicitIntent;
|
|
34708
|
-
const hasLegacyPathMarker = Object.prototype.hasOwnProperty.call(cfg || {}, '__resourcePath__');
|
|
34709
|
-
if (!hasLegacyPathMarker)
|
|
34710
|
-
return 'unchanged';
|
|
34711
|
-
const legacyPath = cfg?.__resourcePath__ || '';
|
|
34712
|
-
return legacyPath.trim().length > 0 ? 'set' : 'unchanged';
|
|
34713
|
-
}
|
|
34714
|
-
applyResourcePathIntentFromEditor(cfg) {
|
|
34715
|
-
const intent = this.resolveResourcePathIntentFromEditor(cfg);
|
|
34716
|
-
const nextPath = (cfg?.__resourcePath__ || '').trim();
|
|
34717
|
-
if (intent === 'clear') {
|
|
34718
|
-
if (!this.hasExplicitResourcePath())
|
|
34719
|
-
return false;
|
|
34720
|
-
const previousPath = this.resourcePath;
|
|
34721
|
-
this.disconnect();
|
|
34722
|
-
this.debugLog('Editor resourcePath intent applied', {
|
|
34723
|
-
intent,
|
|
34724
|
-
from: (previousPath || '').trim(),
|
|
34725
|
-
to: '',
|
|
34726
|
-
});
|
|
34727
|
-
return true;
|
|
34728
|
-
}
|
|
34729
|
-
if (intent === 'set') {
|
|
34730
|
-
if (!nextPath)
|
|
34731
|
-
return false;
|
|
34732
|
-
if (nextPath === (this.resourcePath ?? '').trim())
|
|
34733
|
-
return false;
|
|
34734
|
-
this.resourcePath = nextPath;
|
|
34735
|
-
this.reconcileDataModeTransition('editor-set-resourcePath');
|
|
34736
|
-
this.clearSchemaFieldsSnapshot();
|
|
34737
|
-
this.saveConnection('editor');
|
|
34738
|
-
this.schemaError = false;
|
|
34739
|
-
this.dataError = false;
|
|
34740
|
-
this.errorMessage = null;
|
|
34741
|
-
this.clearLocalScaffolding();
|
|
34742
|
-
this.crudService.configure(this.resourcePath);
|
|
34743
|
-
if ((this.config?.columns?.length ?? 0) > 0) {
|
|
34744
|
-
this.verifyServerSchemaVersion().catch(() => { });
|
|
34745
|
-
}
|
|
34746
|
-
else {
|
|
34747
|
-
this.loadSchema();
|
|
34748
|
-
}
|
|
34749
|
-
this.fetchData();
|
|
34750
|
-
this.debugLog('Editor resourcePath intent applied', {
|
|
34751
|
-
intent,
|
|
34752
|
-
to: nextPath,
|
|
34753
|
-
});
|
|
34754
|
-
return true;
|
|
34755
|
-
}
|
|
34756
|
-
return false;
|
|
34757
|
-
}
|
|
34758
35090
|
disconnect() {
|
|
34759
35091
|
try {
|
|
34760
35092
|
const key = this.connectionKey();
|
|
@@ -36980,106 +37312,27 @@ class PraxisTable {
|
|
|
36980
37312
|
content: { component: PraxisTableConfigEditor, inputs: panelInputs },
|
|
36981
37313
|
});
|
|
36982
37314
|
this.subscriptions.push(ref.applied$.subscribe((val) => {
|
|
36983
|
-
|
|
36984
|
-
if (!cfg)
|
|
37315
|
+
if (!val)
|
|
36985
37316
|
return;
|
|
36986
|
-
|
|
36987
|
-
|
|
36988
|
-
const
|
|
36989
|
-
|
|
36990
|
-
|
|
36991
|
-
|
|
36992
|
-
}
|
|
36993
|
-
|
|
36994
|
-
this.horizontalScroll = newHs;
|
|
36995
|
-
this.horizontalScrollOverrideSource = 'editor';
|
|
36996
|
-
const inputsKey = this.inputsKey();
|
|
36997
|
-
if (inputsKey) {
|
|
36998
|
-
this.persistHorizontalScrollInput(inputsKey, newHs, 'settings-applied');
|
|
36999
|
-
}
|
|
37000
|
-
}
|
|
37001
|
-
// Persistir em memória no TableConfig.meta com idField resolvido (mesmo se usuário não alterou)
|
|
37002
|
-
const metaApply = { ...(cfg.meta || {}) };
|
|
37003
|
-
const resolvedIdApply = this.getIdField();
|
|
37004
|
-
if (resolvedIdApply)
|
|
37005
|
-
metaApply.idField = resolvedIdApply;
|
|
37006
|
-
if (!metaApply.version)
|
|
37007
|
-
metaApply.version = '2.0.0';
|
|
37008
|
-
const nowApply = new Date().toISOString();
|
|
37009
|
-
if (!metaApply.createdAt)
|
|
37010
|
-
metaApply.createdAt = nowApply;
|
|
37011
|
-
metaApply.updatedAt = nowApply;
|
|
37012
|
-
const infoApply = this.crudService.getLastSchemaInfo?.();
|
|
37013
|
-
if (infoApply?.schemaId)
|
|
37014
|
-
metaApply.schemaId = infoApply.schemaId;
|
|
37015
|
-
if (infoApply?.schemaHash)
|
|
37016
|
-
metaApply.serverHash = infoApply.schemaHash;
|
|
37017
|
-
cfg.meta = metaApply;
|
|
37018
|
-
// Remove internal marker before saving/applying
|
|
37019
|
-
if ('__resourcePath__' in cfg)
|
|
37020
|
-
delete cfg.__resourcePath__;
|
|
37021
|
-
if ('__resourcePathIntent__' in cfg)
|
|
37022
|
-
delete cfg.__resourcePathIntent__;
|
|
37023
|
-
if ('__idField__' in cfg)
|
|
37024
|
-
delete cfg.__idField__;
|
|
37025
|
-
if ('__horizontalScroll__' in cfg)
|
|
37026
|
-
delete cfg.__horizontalScroll__;
|
|
37027
|
-
this.applyTableConfig(cfg);
|
|
37317
|
+
const doc = parseLegacyOrTableDocument(val);
|
|
37318
|
+
this.debugLog('[PraxisTable] Applied config', doc);
|
|
37319
|
+
const plan = buildTableApplyPlan(doc, this.buildTableEditorRuntimeContext(), {
|
|
37320
|
+
saveConfig: false,
|
|
37321
|
+
saveBindings: true,
|
|
37322
|
+
attachServerMeta: true,
|
|
37323
|
+
});
|
|
37324
|
+
this.executeTableEditorApplyPlan(plan, doc, 'settings-applied');
|
|
37028
37325
|
}), ref.saved$.subscribe((val) => {
|
|
37029
|
-
|
|
37030
|
-
if (!cfg)
|
|
37326
|
+
if (!val)
|
|
37031
37327
|
return;
|
|
37032
|
-
|
|
37033
|
-
|
|
37034
|
-
const
|
|
37035
|
-
|
|
37036
|
-
|
|
37037
|
-
|
|
37038
|
-
}
|
|
37039
|
-
|
|
37040
|
-
this.horizontalScroll = newHs;
|
|
37041
|
-
this.horizontalScrollOverrideSource = 'editor';
|
|
37042
|
-
const inputsKey = this.inputsKey();
|
|
37043
|
-
if (inputsKey) {
|
|
37044
|
-
this.persistHorizontalScrollInput(inputsKey, newHs, 'settings-saved');
|
|
37045
|
-
}
|
|
37046
|
-
}
|
|
37047
|
-
// Persistir meta no JSON salvo (sempre definir idField resolvido)
|
|
37048
|
-
const metaSave = { ...(cfg.meta || {}) };
|
|
37049
|
-
if (!metaSave.version)
|
|
37050
|
-
metaSave.version = '2.0.0';
|
|
37051
|
-
const now = new Date().toISOString();
|
|
37052
|
-
if (!metaSave.createdAt)
|
|
37053
|
-
metaSave.createdAt = now;
|
|
37054
|
-
metaSave.updatedAt = now;
|
|
37055
|
-
const info = this.crudService.getLastSchemaInfo?.();
|
|
37056
|
-
if (info?.schemaId)
|
|
37057
|
-
metaSave.schemaId = info.schemaId;
|
|
37058
|
-
if (info?.schemaHash)
|
|
37059
|
-
metaSave.serverHash = info.schemaHash;
|
|
37060
|
-
const resolvedIdSave = this.getIdField();
|
|
37061
|
-
if (resolvedIdSave)
|
|
37062
|
-
metaSave.idField = resolvedIdSave;
|
|
37063
|
-
cfg.meta = metaSave;
|
|
37064
|
-
if ('__resourcePath__' in cfg)
|
|
37065
|
-
delete cfg.__resourcePath__;
|
|
37066
|
-
if ('__resourcePathIntent__' in cfg)
|
|
37067
|
-
delete cfg.__resourcePathIntent__;
|
|
37068
|
-
if ('__idField__' in cfg)
|
|
37069
|
-
delete cfg.__idField__;
|
|
37070
|
-
if ('__horizontalScroll__' in cfg)
|
|
37071
|
-
delete cfg.__horizontalScroll__;
|
|
37072
|
-
const cfgKey = this.tableConfigKey();
|
|
37073
|
-
if (cfgKey) {
|
|
37074
|
-
this.saveConfigWithAck(cfgKey, cfg, 'table-settings-save', {
|
|
37075
|
-
onSettled: (persisted) => {
|
|
37076
|
-
if (!persisted) {
|
|
37077
|
-
this.showPersistenceFailureFeedback('table-settings-save');
|
|
37078
|
-
}
|
|
37079
|
-
},
|
|
37080
|
-
});
|
|
37081
|
-
}
|
|
37082
|
-
this.applyTableConfig(cfg);
|
|
37328
|
+
const doc = parseLegacyOrTableDocument(val);
|
|
37329
|
+
this.debugLog('[PraxisTable] Saved config', doc);
|
|
37330
|
+
const plan = buildTableApplyPlan(doc, this.buildTableEditorRuntimeContext(), {
|
|
37331
|
+
saveConfig: true,
|
|
37332
|
+
saveBindings: true,
|
|
37333
|
+
attachServerMeta: true,
|
|
37334
|
+
});
|
|
37335
|
+
this.executeTableEditorApplyPlan(plan, doc, 'settings-saved');
|
|
37083
37336
|
}), ref.reset$.subscribe(() => {
|
|
37084
37337
|
this.debugLog('[PraxisTable] Resetting to default config');
|
|
37085
37338
|
this.resetPreferencesToDefaults({
|
|
@@ -37094,25 +37347,21 @@ class PraxisTable {
|
|
|
37094
37347
|
}
|
|
37095
37348
|
}
|
|
37096
37349
|
applyTableConfig(cfg) {
|
|
37097
|
-
|
|
37098
|
-
this.
|
|
37099
|
-
this.
|
|
37350
|
+
this.debugLog('[PraxisTable] Applying table config', cfg);
|
|
37351
|
+
this.config = { ...cfg };
|
|
37352
|
+
this.syncRuntimeSchemaMetaFromConfig();
|
|
37100
37353
|
this.ensureConfigDefaults();
|
|
37101
37354
|
this.enforceUnsupportedFeatureGuards();
|
|
37102
37355
|
this.syncHorizontalScrollFromAppearanceConfig();
|
|
37103
|
-
const advancedFilters =
|
|
37356
|
+
const advancedFilters = cfg.behavior?.filtering?.advancedFilters;
|
|
37104
37357
|
const hasExplicitFilterSettings = !!advancedFilters &&
|
|
37105
37358
|
Object.prototype.hasOwnProperty.call(advancedFilters, 'settings');
|
|
37106
37359
|
const filterSettings = hasExplicitFilterSettings
|
|
37107
37360
|
? advancedFilters?.settings
|
|
37108
37361
|
: undefined;
|
|
37109
37362
|
this.debugLog('[PraxisTable] Applying filter settings', filterSettings);
|
|
37110
|
-
|
|
37111
|
-
|
|
37112
|
-
this.filterConfig.save(filterKey, (filterSettings || {}));
|
|
37113
|
-
}
|
|
37114
|
-
this.setShowToolbar(!!(safeCfg.toolbar?.visible ||
|
|
37115
|
-
safeCfg.behavior?.filtering?.advancedFilters?.enabled), true);
|
|
37363
|
+
this.setShowToolbar(!!(cfg.toolbar?.visible ||
|
|
37364
|
+
cfg.behavior?.filtering?.advancedFilters?.enabled), true);
|
|
37116
37365
|
this.syncInternalFilterSettings(filterSettings);
|
|
37117
37366
|
this.debugLog('[PraxisTable] Toolbar visibility after apply', this.showToolbar);
|
|
37118
37367
|
this.setupColumns();
|
|
@@ -37128,27 +37377,125 @@ class PraxisTable {
|
|
|
37128
37377
|
}
|
|
37129
37378
|
this.cdr.detectChanges();
|
|
37130
37379
|
}
|
|
37131
|
-
|
|
37132
|
-
const
|
|
37133
|
-
|
|
37134
|
-
|
|
37135
|
-
|
|
37136
|
-
|
|
37380
|
+
buildTableEditorRuntimeContext() {
|
|
37381
|
+
const info = this.crudService.getLastSchemaInfo?.();
|
|
37382
|
+
return {
|
|
37383
|
+
hasLocalDataInput: this.hasLocalDataInput(),
|
|
37384
|
+
localDataModeDefaultEnabled: this.isLocalDataModeFeatureEnabled(),
|
|
37385
|
+
server: {
|
|
37386
|
+
idField: this.crudService.getResourceIdField?.(),
|
|
37387
|
+
schemaId: info?.schemaId,
|
|
37388
|
+
schemaHash: info?.schemaHash,
|
|
37389
|
+
},
|
|
37390
|
+
crud: {
|
|
37391
|
+
tableId: this.tableId,
|
|
37392
|
+
componentKeyId: this.componentKeyId() || undefined,
|
|
37393
|
+
},
|
|
37394
|
+
currentBindings: {
|
|
37395
|
+
resourcePath: (this.resourcePath || '').trim() || null,
|
|
37396
|
+
idField: this.getIdField(),
|
|
37397
|
+
horizontalScroll: this.horizontalScroll,
|
|
37398
|
+
},
|
|
37399
|
+
};
|
|
37400
|
+
}
|
|
37401
|
+
executeTableEditorApplyPlan(plan, doc, trigger) {
|
|
37402
|
+
if (plan.diagnostics.some((item) => item.level === 'error')) {
|
|
37403
|
+
this.warnLog('[PraxisTable] Editor document validation failed', {
|
|
37404
|
+
diagnostics: plan.diagnostics,
|
|
37405
|
+
});
|
|
37406
|
+
return;
|
|
37407
|
+
}
|
|
37408
|
+
const nextBindings = plan.bindingsPatch || {};
|
|
37409
|
+
const nextPath = (nextBindings.resourcePath || '').trim();
|
|
37410
|
+
const shouldReconnect = plan.runtime?.reconnectDataSource === true;
|
|
37411
|
+
const shouldReloadSchema = plan.runtime?.reloadSchema === true;
|
|
37412
|
+
const shouldFetchData = plan.runtime?.fetchData === true;
|
|
37413
|
+
const shouldRefreshLocalView = plan.runtime?.refreshLocalView === true;
|
|
37414
|
+
if (shouldReconnect) {
|
|
37415
|
+
if (nextPath) {
|
|
37416
|
+
this.resourcePath = nextPath;
|
|
37417
|
+
this.reconcileDataModeTransition('editor-set-resourcePath');
|
|
37418
|
+
this.clearSchemaFieldsSnapshot();
|
|
37419
|
+
this.saveConnection('editor');
|
|
37420
|
+
this.schemaError = false;
|
|
37421
|
+
this.dataError = false;
|
|
37422
|
+
this.errorMessage = null;
|
|
37423
|
+
this.clearLocalScaffolding();
|
|
37424
|
+
this.crudService.configure(this.resourcePath);
|
|
37425
|
+
}
|
|
37426
|
+
else {
|
|
37427
|
+
this.disconnect();
|
|
37428
|
+
}
|
|
37429
|
+
}
|
|
37430
|
+
if (nextBindings.idField && nextBindings.idField !== this.idField) {
|
|
37431
|
+
this.idField = nextBindings.idField;
|
|
37432
|
+
}
|
|
37433
|
+
const nextHs = nextBindings.horizontalScroll;
|
|
37434
|
+
if (nextHs && nextHs !== this.horizontalScroll) {
|
|
37435
|
+
this.horizontalScroll = nextHs;
|
|
37436
|
+
this.horizontalScrollOverrideSource = 'editor';
|
|
37437
|
+
const inputsKey = this.inputsKey();
|
|
37438
|
+
if (inputsKey && plan.persistence?.saveBindings) {
|
|
37439
|
+
this.persistHorizontalScrollInput(inputsKey, nextHs, trigger);
|
|
37440
|
+
}
|
|
37441
|
+
}
|
|
37442
|
+
const cfg = this.attachRuntimeMetadataToEditorConfig(plan.canonicalConfig, plan.metadata?.attachServerMeta === true, plan.persistence?.saveConfig === true);
|
|
37443
|
+
if (plan.persistence?.saveConfig) {
|
|
37444
|
+
const cfgKey = this.tableConfigKey();
|
|
37445
|
+
if (cfgKey) {
|
|
37446
|
+
this.saveConfigWithAck(cfgKey, cfg, 'table-settings-save', {
|
|
37447
|
+
onSettled: (persisted) => {
|
|
37448
|
+
if (!persisted) {
|
|
37449
|
+
this.showPersistenceFailureFeedback('table-settings-save');
|
|
37450
|
+
}
|
|
37451
|
+
},
|
|
37452
|
+
});
|
|
37453
|
+
}
|
|
37137
37454
|
}
|
|
37138
|
-
|
|
37139
|
-
if (
|
|
37455
|
+
this.applyTableConfig(cfg);
|
|
37456
|
+
if (shouldReconnect && nextPath) {
|
|
37457
|
+
if (shouldReloadSchema) {
|
|
37458
|
+
if ((doc.config?.columns?.length ?? 0) > 0) {
|
|
37459
|
+
this.verifyServerSchemaVersion().catch(() => { });
|
|
37460
|
+
}
|
|
37461
|
+
else {
|
|
37462
|
+
this.loadSchema();
|
|
37463
|
+
}
|
|
37464
|
+
}
|
|
37465
|
+
else if (shouldFetchData) {
|
|
37466
|
+
this.fetchData();
|
|
37467
|
+
}
|
|
37468
|
+
}
|
|
37469
|
+
else if (shouldRefreshLocalView) {
|
|
37470
|
+
this.refreshLocalScaffolding('editor-apply-plan', {
|
|
37471
|
+
resetPageSizeOverride: true,
|
|
37472
|
+
});
|
|
37473
|
+
}
|
|
37474
|
+
}
|
|
37475
|
+
attachRuntimeMetadataToEditorConfig(cfg, attachServerMeta, touchPersistenceMeta) {
|
|
37476
|
+
if (!attachServerMeta && !touchPersistenceMeta) {
|
|
37140
37477
|
return cfg;
|
|
37141
37478
|
}
|
|
37142
|
-
const
|
|
37143
|
-
const
|
|
37144
|
-
|
|
37145
|
-
|
|
37146
|
-
|
|
37147
|
-
|
|
37148
|
-
|
|
37149
|
-
|
|
37150
|
-
|
|
37151
|
-
|
|
37479
|
+
const next = { ...cfg };
|
|
37480
|
+
const meta = { ...(next.meta || {}) };
|
|
37481
|
+
if (touchPersistenceMeta) {
|
|
37482
|
+
const now = new Date().toISOString();
|
|
37483
|
+
if (!meta.version)
|
|
37484
|
+
meta.version = '2.0.0';
|
|
37485
|
+
if (!meta.createdAt)
|
|
37486
|
+
meta.createdAt = now;
|
|
37487
|
+
meta.updatedAt = now;
|
|
37488
|
+
}
|
|
37489
|
+
if (attachServerMeta) {
|
|
37490
|
+
// Persist only a reconciliation snapshot. Runtime drift detection uses runtimeSchemaMeta.
|
|
37491
|
+
const info = this.crudService.getLastSchemaInfo?.();
|
|
37492
|
+
if (info?.schemaId)
|
|
37493
|
+
meta.schemaId = info.schemaId;
|
|
37494
|
+
if (info?.schemaHash)
|
|
37495
|
+
meta.serverHash = info.schemaHash;
|
|
37496
|
+
}
|
|
37497
|
+
next.meta = meta;
|
|
37498
|
+
return next;
|
|
37152
37499
|
}
|
|
37153
37500
|
syncInternalFilterSettings(settings) {
|
|
37154
37501
|
const filter = this.internalFilter;
|
|
@@ -37333,6 +37680,9 @@ class PraxisTable {
|
|
|
37333
37680
|
return safe;
|
|
37334
37681
|
}
|
|
37335
37682
|
setupColumns() {
|
|
37683
|
+
if (!(this.uncontrolledExpandedRowKeys instanceof Set)) {
|
|
37684
|
+
this.uncontrolledExpandedRowKeys = new Set();
|
|
37685
|
+
}
|
|
37336
37686
|
const columns = this.sanitizeColumns(this.config.columns);
|
|
37337
37687
|
this.clearEffectiveCellRendererCache();
|
|
37338
37688
|
this.visibleColumns = columns
|
|
@@ -38157,7 +38507,7 @@ class PraxisTable {
|
|
|
38157
38507
|
u.searchParams.set('operation', 'get');
|
|
38158
38508
|
u.searchParams.set('schemaType', 'response');
|
|
38159
38509
|
u.searchParams.set('includeInternalSchemas', 'false');
|
|
38160
|
-
const serverHash = this.
|
|
38510
|
+
const serverHash = this.runtimeSchemaMeta.serverHash;
|
|
38161
38511
|
const hadServerHash = !!serverHash;
|
|
38162
38512
|
const tenant = (() => { try {
|
|
38163
38513
|
return this.global.getTenant();
|
|
@@ -38173,39 +38523,30 @@ class PraxisTable {
|
|
|
38173
38523
|
} })();
|
|
38174
38524
|
const res = await fetchWithETag({ url: u.toString(), schemaHash: serverHash, tenant, locale });
|
|
38175
38525
|
const nowIso = new Date().toISOString();
|
|
38176
|
-
const meta = { ...(this.config?.meta || {}) };
|
|
38177
|
-
if (!meta.version)
|
|
38178
|
-
meta.version = '2.0.0';
|
|
38179
|
-
if (!meta.createdAt)
|
|
38180
|
-
meta.createdAt = nowIso;
|
|
38181
|
-
meta.lastVerifiedAt = nowIso;
|
|
38182
38526
|
if (res.status === 304) {
|
|
38183
|
-
|
|
38184
|
-
|
|
38185
|
-
|
|
38186
|
-
|
|
38187
|
-
this.saveConfigWithAck(cfgKey, this.config, 'schema-verification-304');
|
|
38188
|
-
}
|
|
38527
|
+
this.runtimeSchemaMeta = {
|
|
38528
|
+
...this.runtimeSchemaMeta,
|
|
38529
|
+
lastVerifiedAt: nowIso,
|
|
38530
|
+
};
|
|
38189
38531
|
// Keep outdated false and emit status
|
|
38190
38532
|
if (this.schemaOutdated) {
|
|
38191
38533
|
this.schemaOutdated = false;
|
|
38192
38534
|
}
|
|
38193
38535
|
this.emitMetadataChange('verification');
|
|
38194
|
-
this.emitSchemaStatus({ outdated: false, serverHash: this.
|
|
38536
|
+
this.emitSchemaStatus({ outdated: false, serverHash: this.runtimeSchemaMeta.serverHash, lastVerifiedAt: this.runtimeSchemaMeta.lastVerifiedAt, resourcePath: this.resourcePath }, 'verification-304');
|
|
38195
38537
|
return;
|
|
38196
38538
|
}
|
|
38197
38539
|
// status === 200: server hash changed or first verification without hash. Do not apply columns here.
|
|
38198
38540
|
const newHash = res.schemaHash;
|
|
38199
|
-
|
|
38200
|
-
|
|
38541
|
+
this.runtimeSchemaMeta = {
|
|
38542
|
+
...this.runtimeSchemaMeta,
|
|
38543
|
+
serverHash: newHash,
|
|
38544
|
+
lastVerifiedAt: nowIso,
|
|
38545
|
+
};
|
|
38201
38546
|
const changed = !!(hadServerHash && newHash && newHash !== serverHash);
|
|
38202
38547
|
this.schemaOutdated = this.editModeEnabled && changed; // only notify in edit mode when we can compare
|
|
38203
|
-
const cfgKey = this.tableConfigKey();
|
|
38204
|
-
if (cfgKey) {
|
|
38205
|
-
this.saveConfigWithAck(cfgKey, this.config, 'schema-verification-200');
|
|
38206
|
-
}
|
|
38207
38548
|
this.emitMetadataChange('verification');
|
|
38208
|
-
this.emitSchemaStatus({ outdated: this.schemaOutdated, serverHash:
|
|
38549
|
+
this.emitSchemaStatus({ outdated: this.schemaOutdated, serverHash: this.runtimeSchemaMeta.serverHash, lastVerifiedAt: this.runtimeSchemaMeta.lastVerifiedAt, resourcePath: this.resourcePath }, 'verification-200');
|
|
38209
38550
|
// Notifications only if edit mode
|
|
38210
38551
|
if (this.schemaOutdated) {
|
|
38211
38552
|
if (this._resolvedPrefs.autoOpenSettingsOnOutdated) {
|
|
@@ -38231,28 +38572,28 @@ class PraxisTable {
|
|
|
38231
38572
|
next: (fields) => {
|
|
38232
38573
|
this.schemaError = false;
|
|
38233
38574
|
this.setSchemaFieldsSnapshot(fields);
|
|
38234
|
-
// Se idField não foi definido via inputs/contexto, tentar derivar do schema
|
|
38575
|
+
// Se idField não foi definido via inputs/contexto, tentar derivar do schema/serviço.
|
|
38235
38576
|
try {
|
|
38236
|
-
const derived = this.idField || this.crudContext?.idField || this.
|
|
38577
|
+
const derived = this.idField || this.crudContext?.idField || this.crudService.getResourceIdField();
|
|
38237
38578
|
if (derived && (!this.idField || this.idField === 'id')) {
|
|
38238
38579
|
this.idField = derived;
|
|
38239
38580
|
}
|
|
38240
|
-
// Alertar divergência entre
|
|
38241
|
-
const cfgId = this.
|
|
38581
|
+
// Alertar divergência entre o idField efetivo em uso e o idField vindo do serviço.
|
|
38582
|
+
const cfgId = this.idField;
|
|
38242
38583
|
const svcId = this.crudService.getResourceIdField();
|
|
38243
|
-
if (cfgId && svcId && cfgId !== svcId) {
|
|
38584
|
+
if (cfgId && cfgId !== 'id' && svcId && cfgId !== svcId) {
|
|
38244
38585
|
try {
|
|
38245
|
-
this.snackBar.open('Aviso: idField
|
|
38586
|
+
this.snackBar.open('Aviso: idField efetivo diverge do servidor', undefined, { duration: 4000 });
|
|
38246
38587
|
}
|
|
38247
38588
|
catch { }
|
|
38248
38589
|
this.warnLog('[PraxisTable] Divergência idField', { config: cfgId, server: svcId });
|
|
38249
38590
|
}
|
|
38250
38591
|
}
|
|
38251
38592
|
catch { }
|
|
38252
|
-
// Atualizar metadados de schema
|
|
38593
|
+
// Atualizar metadados transitórios de schema em memória.
|
|
38253
38594
|
try {
|
|
38254
38595
|
const info = this.crudService.getLastSchemaInfo?.();
|
|
38255
|
-
this.
|
|
38596
|
+
this.updateRuntimeSchemaMetaFromServerInfo({ schemaId: info?.schemaId, schemaHash: info?.schemaHash }, { updateVerifiedAt: true });
|
|
38256
38597
|
}
|
|
38257
38598
|
catch { }
|
|
38258
38599
|
const existing = this.config?.columns ?? [];
|
|
@@ -38273,14 +38614,13 @@ class PraxisTable {
|
|
|
38273
38614
|
}
|
|
38274
38615
|
// Notificar hosts sobre atualização de metadados de schema ao concluir o loadSchema
|
|
38275
38616
|
try {
|
|
38276
|
-
const meta = this.config?.meta || {};
|
|
38277
38617
|
// Ao carregar o schema com sucesso, consideramos que não há desatualização pendente
|
|
38278
38618
|
this.schemaOutdated = false;
|
|
38279
38619
|
this.emitMetadataChange('bootstrap');
|
|
38280
38620
|
this.emitSchemaStatus({
|
|
38281
38621
|
outdated: false,
|
|
38282
|
-
serverHash:
|
|
38283
|
-
lastVerifiedAt:
|
|
38622
|
+
serverHash: this.runtimeSchemaMeta.serverHash,
|
|
38623
|
+
lastVerifiedAt: this.runtimeSchemaMeta.lastVerifiedAt,
|
|
38284
38624
|
resourcePath: this.resourcePath,
|
|
38285
38625
|
}, 'bootstrap');
|
|
38286
38626
|
}
|
|
@@ -40931,14 +41271,14 @@ class PraxisTable {
|
|
|
40931
41271
|
isOutdatedIgnored(serverHash) {
|
|
40932
41272
|
if (!serverHash)
|
|
40933
41273
|
return false;
|
|
40934
|
-
if (this.
|
|
41274
|
+
if (this.runtimeSchemaMeta.serverHash === serverHash)
|
|
40935
41275
|
return this.schemaState.ignored;
|
|
40936
41276
|
return false;
|
|
40937
41277
|
}
|
|
40938
41278
|
getOutdatedSnoozeUntil(serverHash) {
|
|
40939
41279
|
if (!serverHash)
|
|
40940
41280
|
return undefined;
|
|
40941
|
-
if (this.
|
|
41281
|
+
if (this.runtimeSchemaMeta.serverHash === serverHash)
|
|
40942
41282
|
return this.schemaState.snoozeUntil;
|
|
40943
41283
|
return undefined;
|
|
40944
41284
|
}
|
|
@@ -40950,12 +41290,12 @@ class PraxisTable {
|
|
|
40950
41290
|
return;
|
|
40951
41291
|
if (until) {
|
|
40952
41292
|
this.saveConfigWithAck(key, until.toISOString(), 'schema-outdated-snooze');
|
|
40953
|
-
if (this.
|
|
41293
|
+
if (this.runtimeSchemaMeta.serverHash === serverHash)
|
|
40954
41294
|
this.schemaState.snoozeUntil = until.getTime();
|
|
40955
41295
|
}
|
|
40956
41296
|
else {
|
|
40957
41297
|
this.clearConfigWithAck(key, 'schema-outdated-snooze-clear');
|
|
40958
|
-
if (this.
|
|
41298
|
+
if (this.runtimeSchemaMeta.serverHash === serverHash)
|
|
40959
41299
|
this.schemaState.snoozeUntil = 0;
|
|
40960
41300
|
}
|
|
40961
41301
|
}
|
|
@@ -40967,12 +41307,12 @@ class PraxisTable {
|
|
|
40967
41307
|
return;
|
|
40968
41308
|
if (val) {
|
|
40969
41309
|
this.saveConfigWithAck(key, true, 'schema-outdated-ignore');
|
|
40970
|
-
if (this.
|
|
41310
|
+
if (this.runtimeSchemaMeta.serverHash === serverHash)
|
|
40971
41311
|
this.schemaState.ignored = true;
|
|
40972
41312
|
}
|
|
40973
41313
|
else {
|
|
40974
41314
|
this.clearConfigWithAck(key, 'schema-outdated-ignore-clear');
|
|
40975
|
-
if (this.
|
|
41315
|
+
if (this.runtimeSchemaMeta.serverHash === serverHash)
|
|
40976
41316
|
this.schemaState.ignored = false;
|
|
40977
41317
|
}
|
|
40978
41318
|
}
|
|
@@ -40983,13 +41323,13 @@ class PraxisTable {
|
|
|
40983
41323
|
if (!key)
|
|
40984
41324
|
return;
|
|
40985
41325
|
this.saveConfigWithAck(key, true, 'schema-outdated-notified');
|
|
40986
|
-
if (this.
|
|
41326
|
+
if (this.runtimeSchemaMeta.serverHash === serverHash)
|
|
40987
41327
|
this.schemaState.notified = true;
|
|
40988
41328
|
}
|
|
40989
41329
|
wasOutdatedNotified(serverHash) {
|
|
40990
41330
|
if (!serverHash)
|
|
40991
41331
|
return false;
|
|
40992
|
-
if (this.
|
|
41332
|
+
if (this.runtimeSchemaMeta.serverHash === serverHash)
|
|
40993
41333
|
return this.schemaState.notified;
|
|
40994
41334
|
return false;
|
|
40995
41335
|
}
|
|
@@ -41044,7 +41384,7 @@ class PraxisTable {
|
|
|
41044
41384
|
return;
|
|
41045
41385
|
if (this.resolvedPrefs.notifyIfOutdated === 'none')
|
|
41046
41386
|
return;
|
|
41047
|
-
const serverHash = this.
|
|
41387
|
+
const serverHash = this.runtimeSchemaMeta.serverHash;
|
|
41048
41388
|
if (!serverHash || !this.schemaOutdated)
|
|
41049
41389
|
return;
|
|
41050
41390
|
if (this.isOutdatedIgnored(serverHash))
|
|
@@ -41071,7 +41411,7 @@ class PraxisTable {
|
|
|
41071
41411
|
catch { }
|
|
41072
41412
|
}
|
|
41073
41413
|
shouldShowOutdatedInline() {
|
|
41074
|
-
const hash = this.
|
|
41414
|
+
const hash = this.runtimeSchemaMeta.serverHash;
|
|
41075
41415
|
const inlineEnabled = this.resolvedPrefs.notifyIfOutdated === 'inline' || this.resolvedPrefs.notifyIfOutdated === 'both';
|
|
41076
41416
|
return (inlineEnabled &&
|
|
41077
41417
|
this.schemaOutdated &&
|
|
@@ -41084,7 +41424,7 @@ class PraxisTable {
|
|
|
41084
41424
|
this.verifyServerSchemaVersion().catch(() => { });
|
|
41085
41425
|
}
|
|
41086
41426
|
onSnoozeOutdated() {
|
|
41087
|
-
const hash = this.
|
|
41427
|
+
const hash = this.runtimeSchemaMeta.serverHash;
|
|
41088
41428
|
if (hash) {
|
|
41089
41429
|
// Default snooze 24h
|
|
41090
41430
|
const until = new Date(Date.now() + (this.snoozeMs ?? 24 * 60 * 60 * 1000));
|
|
@@ -41093,33 +41433,27 @@ class PraxisTable {
|
|
|
41093
41433
|
this.schemaOutdated = false;
|
|
41094
41434
|
}
|
|
41095
41435
|
onIgnoreOutdated() {
|
|
41096
|
-
const hash = this.
|
|
41436
|
+
const hash = this.runtimeSchemaMeta.serverHash;
|
|
41097
41437
|
if (hash) {
|
|
41098
41438
|
this.setOutdatedIgnore(hash, true);
|
|
41099
41439
|
}
|
|
41100
41440
|
this.schemaOutdated = false;
|
|
41101
41441
|
}
|
|
41102
|
-
|
|
41103
|
-
const
|
|
41442
|
+
updateRuntimeSchemaMetaFromServerInfo(info, opts = {}) {
|
|
41443
|
+
const next = { ...this.runtimeSchemaMeta };
|
|
41104
41444
|
if (info.schemaId)
|
|
41105
|
-
|
|
41445
|
+
next.schemaId = info.schemaId;
|
|
41106
41446
|
if (info.schemaHash)
|
|
41107
|
-
|
|
41447
|
+
next.serverHash = info.schemaHash;
|
|
41108
41448
|
const now = new Date().toISOString();
|
|
41109
41449
|
if (opts.updateVerifiedAt)
|
|
41110
|
-
|
|
41111
|
-
|
|
41112
|
-
meta.updatedAt = now;
|
|
41113
|
-
if (!meta.version)
|
|
41114
|
-
meta.version = '2.0.0';
|
|
41115
|
-
if (!meta.createdAt)
|
|
41116
|
-
meta.createdAt = now;
|
|
41117
|
-
this.config.meta = meta;
|
|
41450
|
+
next.lastVerifiedAt = now;
|
|
41451
|
+
this.runtimeSchemaMeta = next;
|
|
41118
41452
|
}
|
|
41119
41453
|
emitMetadataChange(trigger) {
|
|
41120
41454
|
this.metadataChange.emit({
|
|
41121
41455
|
trigger,
|
|
41122
|
-
meta: this.
|
|
41456
|
+
meta: this.getEffectiveMetadataSnapshot(),
|
|
41123
41457
|
tableId: this.tableId
|
|
41124
41458
|
});
|
|
41125
41459
|
}
|
|
@@ -41135,7 +41469,32 @@ class PraxisTable {
|
|
|
41135
41469
|
return this.getNestedPropertyValue(row, idField);
|
|
41136
41470
|
}
|
|
41137
41471
|
getIdField() {
|
|
41138
|
-
return this.idField ||
|
|
41472
|
+
return this.idField || 'id';
|
|
41473
|
+
}
|
|
41474
|
+
syncRuntimeSchemaMetaFromConfig() {
|
|
41475
|
+
const meta = (this.config?.meta || {});
|
|
41476
|
+
this.runtimeSchemaMeta = {
|
|
41477
|
+
schemaId: typeof meta.schemaId === 'string' && meta.schemaId.trim()
|
|
41478
|
+
? meta.schemaId.trim()
|
|
41479
|
+
: undefined,
|
|
41480
|
+
serverHash: typeof meta.serverHash === 'string' && meta.serverHash.trim()
|
|
41481
|
+
? meta.serverHash.trim()
|
|
41482
|
+
: undefined,
|
|
41483
|
+
lastVerifiedAt: typeof meta.lastVerifiedAt === 'string' && meta.lastVerifiedAt.trim()
|
|
41484
|
+
? meta.lastVerifiedAt.trim()
|
|
41485
|
+
: undefined,
|
|
41486
|
+
};
|
|
41487
|
+
}
|
|
41488
|
+
getEffectiveMetadataSnapshot() {
|
|
41489
|
+
const baseMeta = { ...(this.config?.meta || {}) };
|
|
41490
|
+
if (this.runtimeSchemaMeta.schemaId)
|
|
41491
|
+
baseMeta.schemaId = this.runtimeSchemaMeta.schemaId;
|
|
41492
|
+
if (this.runtimeSchemaMeta.serverHash)
|
|
41493
|
+
baseMeta.serverHash = this.runtimeSchemaMeta.serverHash;
|
|
41494
|
+
if (this.runtimeSchemaMeta.lastVerifiedAt) {
|
|
41495
|
+
baseMeta.lastVerifiedAt = this.runtimeSchemaMeta.lastVerifiedAt;
|
|
41496
|
+
}
|
|
41497
|
+
return baseMeta;
|
|
41139
41498
|
}
|
|
41140
41499
|
getActionId(action) {
|
|
41141
41500
|
return getActionId(action);
|
|
@@ -42172,7 +42531,7 @@ const TABLE_AI_CAPABILITIES = {
|
|
|
42172
42531
|
{ path: 'meta.name', category: 'meta', valueKind: 'string', description: 'Nome amigável da configuração para exibição em tooltips ou relatórios.', intentExamples: ['renomear tabela', 'chamar de Funcionários'], example: '"Funcionários ativos"' },
|
|
42173
42532
|
{ path: 'meta.description', category: 'meta', valueKind: 'string', description: 'Descrição breve do propósito da tabela.', intentExamples: ['documentar tabela', 'anotar uso em relatórios'], example: '"Visão dos funcionários ativos com filtros por status."' },
|
|
42174
42533
|
{ path: 'meta.tags', category: 'meta', valueKind: 'array', description: 'Tags para busca, presets e telemetria.', intentExamples: ['adicionar tag demo', 'marcar como financeiro'], example: '["financeiro","demo"]' },
|
|
42175
|
-
{ path: '
|
|
42534
|
+
{ path: 'bindings.idField', category: 'bindings', valueKind: 'string', description: 'Campo usado como chave primária operacional da tabela.', critical: true, intentExamples: ['usar employeeId como chave', 'trocar id da linha'], example: '"employeeId"', safetyNotes: 'Trocar idField sem migrar dados pode quebrar seleção e ações por linha.' },
|
|
42176
42535
|
{ path: 'meta.schemaId', category: 'meta', valueKind: 'string', description: 'Identidade do schema de origem para reconciliação.', intentExamples: ['registrar schema', 'alinhar com API X'], example: '"path:/employees#response"' },
|
|
42177
42536
|
// Columns — propriedades base
|
|
42178
42537
|
{ path: 'columns[].field', category: 'columns', valueKind: 'string', description: 'Identificador técnico no dataset. Deve ser único.', critical: true, intentExamples: ['apontar campo source', 'usar field id'], example: '"status"' },
|
|
@@ -42850,5 +43209,5 @@ const TABLE_COMPONENT_AI_CAPABILITIES = {
|
|
|
42850
43209
|
* Generated bundle index. Do not edit.
|
|
42851
43210
|
*/
|
|
42852
43211
|
|
|
42853
|
-
export { BOOLEAN_PRESETS, BehaviorConfigEditorComponent, CURRENCY_PRESETS, ColumnsConfigEditorComponent, DATE_PRESETS, DataFormatterComponent, DataFormattingService, FORMULA_TEMPLATES, FilterConfigService, FilterSettingsComponent, FormulaGeneratorService, JsonConfigEditorComponent, MessagesLocalizationEditorComponent, NUMBER_PRESETS, PERCENTAGE_PRESETS, PRAXIS_TABLE_COMPONENT_METADATA, PraxisFilter, PraxisTable, PraxisTableConfigEditor, PraxisTableToolbar, STRING_PRESETS, TABLE_AI_CAPABILITIES, TABLE_COMPONENT_AI_CAPABILITIES, TASK_PRESETS, TableDefaultsProvider, TableRulesEditorComponent, ToolbarActionsEditorComponent, ValueMappingEditorComponent, VisualFormulaBuilderComponent, getActionId, getEnum, getTableCapabilities, hasJsonKey, jsonPathGet, jsonPathMatches, providePraxisTableMetadata, registerDateDslFunctions, registerJsonDslFunctions };
|
|
43212
|
+
export { BOOLEAN_PRESETS, BehaviorConfigEditorComponent, CURRENCY_PRESETS, ColumnsConfigEditorComponent, DATE_PRESETS, DataFormatterComponent, DataFormattingService, FORMULA_TEMPLATES, FilterConfigService, FilterSettingsComponent, FormulaGeneratorService, JsonConfigEditorComponent, MessagesLocalizationEditorComponent, NUMBER_PRESETS, PERCENTAGE_PRESETS, PRAXIS_TABLE_COMPONENT_METADATA, PraxisFilter, PraxisTable, PraxisTableConfigEditor, PraxisTableToolbar, STRING_PRESETS, TABLE_AI_CAPABILITIES, TABLE_COMPONENT_AI_CAPABILITIES, TASK_PRESETS, TableDefaultsProvider, TableRulesEditorComponent, ToolbarActionsEditorComponent, ValueMappingEditorComponent, VisualFormulaBuilderComponent, buildTableApplyPlan, createTableAuthoringDocument, getActionId, getEnum, getTableCapabilities, hasJsonKey, jsonPathGet, jsonPathMatches, normalizeTableAuthoringDocument, parseLegacyOrTableDocument, providePraxisTableMetadata, registerDateDslFunctions, registerJsonDslFunctions, serializeTableAuthoringDocument, toCanonicalTableConfig, validateTableAuthoringDocument };
|
|
42854
43213
|
//# sourceMappingURL=praxisui-table.mjs.map
|