@smartnet360/svelte-components 0.0.102 → 0.0.104
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/dist/apps/antenna-pattern/index.d.ts +1 -0
- package/dist/apps/antenna-pattern/index.js +1 -0
- package/dist/apps/antenna-pattern/utils/load-static-antennas.d.ts +17 -0
- package/dist/apps/antenna-pattern/utils/load-static-antennas.js +83 -0
- package/dist/apps/site-check/SiteCheck.svelte +13 -81
- package/dist/apps/site-check/SiteCheckControls.svelte +0 -7
- package/dist/apps/site-check/helper.js +0 -33
- package/dist/apps/site-check/transforms.js +15 -65
- package/dist/core/CellTable/CellTable.svelte +456 -0
- package/dist/core/CellTable/CellTable.svelte.d.ts +27 -0
- package/dist/core/CellTable/CellTablePanel.svelte +211 -0
- package/dist/core/CellTable/CellTablePanel.svelte.d.ts +49 -0
- package/dist/core/CellTable/CellTableToolbar.svelte +218 -0
- package/dist/core/CellTable/CellTableToolbar.svelte.d.ts +32 -0
- package/dist/core/CellTable/column-config.d.ts +63 -0
- package/dist/core/CellTable/column-config.js +465 -0
- package/dist/core/CellTable/index.d.ts +10 -0
- package/dist/core/CellTable/index.js +11 -0
- package/dist/core/CellTable/types.d.ts +166 -0
- package/dist/core/CellTable/types.js +6 -0
- package/dist/core/Charts/ChartCard.svelte +118 -31
- package/dist/core/Charts/ChartCard.svelte.d.ts +2 -0
- package/dist/core/Charts/ChartComponent.svelte +8 -31
- package/dist/core/Charts/data-processor.js +1 -19
- package/dist/core/CoverageMap/ai/AITools.d.ts +117 -0
- package/dist/core/CoverageMap/ai/AITools.js +380 -0
- package/dist/core/CoverageMap/core/CoverageCalculator.d.ts +138 -0
- package/dist/core/CoverageMap/core/CoverageCalculator.js +375 -0
- package/dist/core/CoverageMap/core/GridCalculator.d.ts +115 -0
- package/dist/core/CoverageMap/core/GridCalculator.js +484 -0
- package/dist/core/CoverageMap/core/PathLossModels.d.ts +253 -0
- package/dist/core/CoverageMap/core/PathLossModels.js +380 -0
- package/dist/core/CoverageMap/core/SignalProcessor.d.ts +288 -0
- package/dist/core/CoverageMap/core/SignalProcessor.js +424 -0
- package/dist/core/CoverageMap/data/AntennaStore.d.ts +165 -0
- package/dist/core/CoverageMap/data/AntennaStore.js +327 -0
- package/dist/core/CoverageMap/data/SiteStore.d.ts +155 -0
- package/dist/core/CoverageMap/data/SiteStore.js +355 -0
- package/dist/core/CoverageMap/index.d.ts +74 -0
- package/dist/core/CoverageMap/index.js +103 -0
- package/dist/core/CoverageMap/types.d.ts +252 -0
- package/dist/core/CoverageMap/types.js +7 -0
- package/dist/core/CoverageMap/utils/geoUtils.d.ts +223 -0
- package/dist/core/CoverageMap/utils/geoUtils.js +374 -0
- package/dist/core/CoverageMap/utils/rfUtils.d.ts +329 -0
- package/dist/core/CoverageMap/utils/rfUtils.js +434 -0
- package/dist/core/CoverageMap/visualization/ColorSchemes.d.ts +149 -0
- package/dist/core/CoverageMap/visualization/ColorSchemes.js +377 -0
- package/dist/core/TreeView/index.d.ts +4 -4
- package/dist/core/TreeView/index.js +5 -5
- package/dist/core/TreeView/tree-utils.d.ts +12 -0
- package/dist/core/TreeView/tree-utils.js +115 -6
- package/dist/core/TreeView/tree.store.svelte.d.ts +94 -0
- package/dist/core/TreeView/tree.store.svelte.js +274 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +2 -0
- package/dist/map-v2/features/cells/controls/CellFilterControl.svelte +16 -27
- package/dist/map-v2/features/repeaters/controls/RepeaterFilterControl.svelte +33 -42
- package/dist/map-v2/features/sites/controls/SiteFilterControl.svelte +12 -19
- package/dist/map-v3/core/components/Map.svelte +4 -0
- package/dist/map-v3/core/stores/map.store.svelte.js +2 -0
- package/dist/map-v3/features/cells/components/CellFilterControl.svelte +24 -30
- package/dist/map-v3/features/coverage/index.d.ts +12 -0
- package/dist/map-v3/features/coverage/index.js +16 -0
- package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte +198 -0
- package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte.d.ts +10 -0
- package/dist/map-v3/features/coverage/logic/coloring.d.ts +28 -0
- package/dist/map-v3/features/coverage/logic/coloring.js +77 -0
- package/dist/map-v3/features/coverage/logic/geometry.d.ts +33 -0
- package/dist/map-v3/features/coverage/logic/geometry.js +112 -0
- package/dist/map-v3/features/coverage/stores/coverage.data.svelte.d.ts +46 -0
- package/dist/map-v3/features/coverage/stores/coverage.data.svelte.js +95 -0
- package/dist/map-v3/features/coverage/stores/coverage.display.svelte.d.ts +33 -0
- package/dist/map-v3/features/coverage/stores/coverage.display.svelte.js +90 -0
- package/dist/map-v3/features/coverage/types.d.ts +52 -0
- package/dist/map-v3/features/coverage/types.js +7 -0
- package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte +14 -20
- package/dist/map-v3/features/sites/components/SiteFilterControl.svelte +23 -33
- package/dist/map-v3/index.d.ts +4 -0
- package/dist/map-v3/index.js +5 -0
- package/package.json +4 -3
- package/dist/apps/site-check/transforms-old.d.ts +0 -56
- package/dist/apps/site-check/transforms-old.js +0 -273
- package/dist/core/TreeView/tree.store.d.ts +0 -10
- package/dist/core/TreeView/tree.store.js +0 -320
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load antennas from static/antennas folder via fetch
|
|
3
|
+
* This is a helper for loading pre-bundled antenna files without user interaction
|
|
4
|
+
*/
|
|
5
|
+
import type { Antenna } from '../db';
|
|
6
|
+
/**
|
|
7
|
+
* Load and parse all static antenna files
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadStaticAntennas(): Promise<Antenna[]>;
|
|
10
|
+
/**
|
|
11
|
+
* Load static antennas and save to database
|
|
12
|
+
*/
|
|
13
|
+
export declare function importStaticAntennas(): Promise<{
|
|
14
|
+
success: boolean;
|
|
15
|
+
count: number;
|
|
16
|
+
error?: string;
|
|
17
|
+
}>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load antennas from static/antennas folder via fetch
|
|
3
|
+
* This is a helper for loading pre-bundled antenna files without user interaction
|
|
4
|
+
*/
|
|
5
|
+
import { parseMSIFile } from './msi-parser';
|
|
6
|
+
import { saveAntennas } from './db-utils';
|
|
7
|
+
const STATIC_ANTENNA_FILES = [
|
|
8
|
+
'ADU4515R17v06_0699_X_CO_M45_03T_Lr1.msi',
|
|
9
|
+
'ADU4515R17v06_0699_X_CO_M45_04T_Lr1.msi',
|
|
10
|
+
'ADU4515R17v06_0699_X_CO_M45_05T_Lr1.msi',
|
|
11
|
+
'ADU4515R17v06_0699_X_CO_M45_06T_Lr1.msi',
|
|
12
|
+
'ADU4515R17v06_0699_X_CO_M45_07T_Lr1.msi',
|
|
13
|
+
'ADU4515R17v06_0699_X_CO_M45_08T_Lr1.msi',
|
|
14
|
+
'ADU4515R17v06_0699_X_CO_M45_09T_Lr1.msi',
|
|
15
|
+
'ADU4515R17v06_0699_X_CO_M45_10T_Lr1.msi',
|
|
16
|
+
'ADU4515R17v06_0699_X_CO_M45_11T_Lr1.msi',
|
|
17
|
+
'ADU4515R17v06_0699_X_CO_M45_12T_Lr1.msi'
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Load and parse all static antenna files
|
|
21
|
+
*/
|
|
22
|
+
export async function loadStaticAntennas() {
|
|
23
|
+
const antennas = [];
|
|
24
|
+
console.log(`[loadStaticAntennas] Starting to load ${STATIC_ANTENNA_FILES.length} antenna files...`);
|
|
25
|
+
for (const filename of STATIC_ANTENNA_FILES) {
|
|
26
|
+
try {
|
|
27
|
+
// Fetch the file from static folder
|
|
28
|
+
const url = `/antennas/${filename}`;
|
|
29
|
+
console.log(`[loadStaticAntennas] Fetching: ${url}`);
|
|
30
|
+
const response = await fetch(url);
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
console.warn(`[loadStaticAntennas] Failed to fetch ${filename}: ${response.status} ${response.statusText}`);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
// Convert to File object for parser
|
|
36
|
+
const blob = await response.blob();
|
|
37
|
+
const file = new File([blob], filename, { type: 'text/plain' });
|
|
38
|
+
console.log(`[loadStaticAntennas] Parsing ${filename}...`);
|
|
39
|
+
// Parse the MSI file
|
|
40
|
+
const antenna = await parseMSIFile(file);
|
|
41
|
+
antenna.sourcePath = `static/antennas/${filename}`;
|
|
42
|
+
antennas.push(antenna);
|
|
43
|
+
console.log(`[loadStaticAntennas] ✓ Loaded: ${antenna.name} (${antenna.frequency} MHz, tilt: ${antenna.tilt}°)`);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error(`[loadStaticAntennas] Error parsing ${filename}:`, error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
console.log(`[loadStaticAntennas] Completed. Loaded ${antennas.length}/${STATIC_ANTENNA_FILES.length} antennas`);
|
|
50
|
+
return antennas;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Load static antennas and save to database
|
|
54
|
+
*/
|
|
55
|
+
export async function importStaticAntennas() {
|
|
56
|
+
try {
|
|
57
|
+
console.log('[importStaticAntennas] Starting import process...');
|
|
58
|
+
const antennas = await loadStaticAntennas();
|
|
59
|
+
if (antennas.length === 0) {
|
|
60
|
+
console.error('[importStaticAntennas] No antennas were successfully loaded');
|
|
61
|
+
return {
|
|
62
|
+
success: false,
|
|
63
|
+
count: 0,
|
|
64
|
+
error: 'No antennas were successfully loaded'
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
console.log(`[importStaticAntennas] Saving ${antennas.length} antennas to database...`);
|
|
68
|
+
await saveAntennas(antennas);
|
|
69
|
+
console.log(`[importStaticAntennas] ✓ Successfully saved ${antennas.length} antennas to database`);
|
|
70
|
+
return {
|
|
71
|
+
success: true,
|
|
72
|
+
count: antennas.length
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
console.error('[importStaticAntennas] Failed to import static antennas:', error);
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
count: 0,
|
|
80
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import { ChartComponent, type Layout, type CellStylingConfig } from '../../core/Charts';
|
|
6
6
|
import { buildTreeNodes, filterChartData, transformChartData, type CellTrafficRecord, defaultCellStyling, type TreeGroupingConfig, type TreeGroupField, type ColorDimension, defaultTreeGrouping } from './index';
|
|
7
7
|
import { expandLayoutForCells } from './helper';
|
|
8
|
-
import { log } from '../../core/logger';
|
|
9
8
|
import type {ChartMarker, Mode } from '../../index.js';
|
|
10
9
|
import { checkHealth } from '../../core/FeatureRegistry';
|
|
11
10
|
import SiteCheckControls from './SiteCheckControls.svelte';
|
|
@@ -52,24 +51,16 @@
|
|
|
52
51
|
|
|
53
52
|
// Rebuild tree whenever treeGrouping, singleRootSelect, or singleLevel1Select changes
|
|
54
53
|
$effect(() => {
|
|
55
|
-
|
|
56
|
-
log('🔄 Rebuilding tree with grouping', { treeGrouping, singleRootSelect, singleLevel1Select });
|
|
57
|
-
|
|
58
54
|
// Clear any existing localStorage data to prevent stale state
|
|
59
55
|
// This includes both tree state AND chart settings to avoid cell mismatches
|
|
60
56
|
if (typeof window !== 'undefined') {
|
|
61
57
|
localStorage.removeItem('site-check:treeState');
|
|
62
58
|
localStorage.removeItem('charts:globalControls');
|
|
63
|
-
log('🧹 Cleared localStorage: tree state and chart settings');
|
|
64
59
|
}
|
|
65
60
|
|
|
66
61
|
// Build tree nodes from raw data with custom grouping
|
|
67
62
|
const treeNodes = buildTreeNodes(rawData, treeGrouping);
|
|
68
|
-
|
|
69
|
-
nodeCount: treeNodes.length,
|
|
70
|
-
firstNode: treeNodes[0],
|
|
71
|
-
grouping: treeGrouping
|
|
72
|
-
});
|
|
63
|
+
|
|
73
64
|
if(!isHealthy) {
|
|
74
65
|
return;
|
|
75
66
|
}
|
|
@@ -82,57 +73,24 @@
|
|
|
82
73
|
singleRootSelect, // Pass single root select mode
|
|
83
74
|
singleLevel1Select // Pass single Level 1 select mode
|
|
84
75
|
});
|
|
85
|
-
log('✅ Tree Store Created', {
|
|
86
|
-
namespace: 'site-check',
|
|
87
|
-
grouping: treeGrouping,
|
|
88
|
-
singleRootSelect,
|
|
89
|
-
singleLevel1Select
|
|
90
|
-
});
|
|
91
76
|
});
|
|
92
77
|
|
|
93
78
|
// Derive chart data from tree selection
|
|
94
79
|
let filteredData = $derived.by(() => {
|
|
95
80
|
if (!treeStore) return [];
|
|
96
|
-
|
|
97
|
-
if (!storeValue) return [];
|
|
98
|
-
const filtered = filterChartData(rawData, storeValue.state.checkedPaths);
|
|
99
|
-
log('🔍 Filtered Data:', {
|
|
100
|
-
totalRaw: rawData.length,
|
|
101
|
-
checkedPaths: Array.from(storeValue.state.checkedPaths),
|
|
102
|
-
filteredCount: filtered.length,
|
|
103
|
-
cells: Array.from(new Set(filtered.map(r => r.cellName)))
|
|
104
|
-
});
|
|
105
|
-
return filtered;
|
|
81
|
+
return filterChartData(rawData, treeStore.state.checkedPaths);
|
|
106
82
|
});
|
|
107
83
|
|
|
108
84
|
// Transform data using base metrics from layout
|
|
109
85
|
let chartData = $derived.by(() => {
|
|
110
|
-
|
|
111
|
-
log('📊 Chart Data:', {
|
|
112
|
-
filteredRows: filteredData.length,
|
|
113
|
-
transformedRows: transformed.length,
|
|
114
|
-
baseMetrics,
|
|
115
|
-
sampleRow: transformed[0],
|
|
116
|
-
columns: transformed[0] ? Object.keys(transformed[0]) : []
|
|
117
|
-
});
|
|
118
|
-
return transformed;
|
|
86
|
+
return transformChartData(filteredData, baseMetrics);
|
|
119
87
|
});
|
|
120
88
|
|
|
121
89
|
// Expand layout based on selected cells and chosen base layout
|
|
122
90
|
let chartLayout = $derived.by(() => {
|
|
123
91
|
// Pass cellStyling, treeGrouping, colorDimension, and useSectorLineStyles - helper will decide per-section whether to use styling,
|
|
124
92
|
// and generate appropriate labels based on grouping, colors based on colorDimension, and line styles based on useSectorLineStyles
|
|
125
|
-
|
|
126
|
-
log('📐 Chart Layout:', {
|
|
127
|
-
layoutName: selectedBaseLayout.layoutName,
|
|
128
|
-
layoutDefaultColors: selectedBaseLayout.useDefaultChartColors ?? false,
|
|
129
|
-
sectionsCount: expanded.sections.length,
|
|
130
|
-
totalCharts: expanded.sections.reduce((sum, s) => sum + s.charts.length, 0),
|
|
131
|
-
firstSection: expanded.sections[0],
|
|
132
|
-
grouping: treeGrouping,
|
|
133
|
-
colorDimension
|
|
134
|
-
});
|
|
135
|
-
return expanded;
|
|
93
|
+
return expandLayoutForCells(selectedBaseLayout, filteredData, treeGrouping, colorDimension, useSectorLineStyles, cellStyling);
|
|
136
94
|
});
|
|
137
95
|
|
|
138
96
|
let totalRecords = $derived(rawData.length);
|
|
@@ -140,14 +98,10 @@
|
|
|
140
98
|
|
|
141
99
|
// Compute simple stats
|
|
142
100
|
let totalCells = $derived.by(() => {
|
|
143
|
-
|
|
144
|
-
log('📱 Total Cells:', count);
|
|
145
|
-
return count;
|
|
101
|
+
return new Set(filteredData.map((r) => r.cellName)).size;
|
|
146
102
|
});
|
|
147
103
|
let totalSites = $derived.by(() => {
|
|
148
|
-
|
|
149
|
-
log('📡 Total Sites:', count);
|
|
150
|
-
return count;
|
|
104
|
+
return new Set(filteredData.map((r) => r.siteName)).size;
|
|
151
105
|
});
|
|
152
106
|
|
|
153
107
|
// Detect cell technology (LTE vs NR) for single-cell layout selection
|
|
@@ -157,45 +111,23 @@
|
|
|
157
111
|
const cell = filteredData[0];
|
|
158
112
|
const band = cell?.band?.toUpperCase() || '';
|
|
159
113
|
|
|
160
|
-
if (band.startsWith('LTE'))
|
|
161
|
-
|
|
162
|
-
return 'LTE';
|
|
163
|
-
}
|
|
164
|
-
if (band.startsWith('NR') || band.startsWith('5G')) {
|
|
165
|
-
log('📡 Detected Technology: NR', { band });
|
|
166
|
-
return 'NR';
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
log('📡 Detected Technology: UNKNOWN', { band });
|
|
114
|
+
if (band.startsWith('LTE')) return 'LTE';
|
|
115
|
+
if (band.startsWith('NR') || band.startsWith('5G')) return 'NR';
|
|
170
116
|
return 'UNKNOWN';
|
|
171
117
|
});
|
|
172
118
|
|
|
173
119
|
// Select appropriate layout based on cell count and technology
|
|
174
120
|
let selectedBaseLayout = $derived.by(() => {
|
|
175
121
|
// Multiple cells → always use multi-cell layout
|
|
176
|
-
if (totalCells !== 1)
|
|
177
|
-
log('📐 Layout Selection: Multi-cell (count=' + totalCells + ')');
|
|
178
|
-
return multiCellLayout;
|
|
179
|
-
}
|
|
122
|
+
if (totalCells !== 1) return multiCellLayout;
|
|
180
123
|
|
|
181
124
|
// Single LTE cell → use LTE layout if available, otherwise fallback
|
|
182
|
-
if (cellTechnology === 'LTE' && singleLteLayout)
|
|
183
|
-
log('📐 Layout Selection: Single LTE (optimized)');
|
|
184
|
-
return singleLteLayout;
|
|
185
|
-
}
|
|
125
|
+
if (cellTechnology === 'LTE' && singleLteLayout) return singleLteLayout;
|
|
186
126
|
|
|
187
127
|
// Single NR cell → use NR layout if available, otherwise fallback
|
|
188
|
-
if (cellTechnology === 'NR' && singleNrLayout)
|
|
189
|
-
log('📐 Layout Selection: Single NR (optimized)');
|
|
190
|
-
return singleNrLayout;
|
|
191
|
-
}
|
|
128
|
+
if (cellTechnology === 'NR' && singleNrLayout) return singleNrLayout;
|
|
192
129
|
|
|
193
130
|
// Fallback to multi-cell layout for single cells (works fine)
|
|
194
|
-
log('📐 Layout Selection: Multi-cell (fallback for single cell)', {
|
|
195
|
-
technology: cellTechnology,
|
|
196
|
-
lteLayout: !!singleLteLayout,
|
|
197
|
-
nrLayout: !!singleNrLayout
|
|
198
|
-
});
|
|
199
131
|
return multiCellLayout;
|
|
200
132
|
});
|
|
201
133
|
</script>
|
|
@@ -210,7 +142,7 @@
|
|
|
210
142
|
{colorDimension}
|
|
211
143
|
{singleRootSelect}
|
|
212
144
|
{singleLevel1Select}
|
|
213
|
-
|
|
145
|
+
{treeStore}
|
|
214
146
|
{showGroupingSelector}
|
|
215
147
|
{onSearch}
|
|
216
148
|
{searchPlaceholder}
|
|
@@ -223,7 +155,7 @@
|
|
|
223
155
|
<!-- Tree View -->
|
|
224
156
|
<div class="flex-grow-1" style="min-height: 0; overflow: hidden;">
|
|
225
157
|
{#if treeStore}
|
|
226
|
-
<TreeView store={
|
|
158
|
+
<TreeView store={treeStore} showControls={true} showIndeterminate={true} height="100%" />
|
|
227
159
|
{/if}
|
|
228
160
|
</div>
|
|
229
161
|
</div>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<svelte:options runes={true} />
|
|
2
2
|
|
|
3
3
|
<script lang="ts">
|
|
4
|
-
import { log } from '../../core/logger';
|
|
5
4
|
import type { TreeGroupingConfig, TreeGroupField, ColorDimension } from './index';
|
|
6
5
|
|
|
7
6
|
interface Props {
|
|
@@ -68,7 +67,6 @@
|
|
|
68
67
|
function handleSearch() {
|
|
69
68
|
if (onSearch) {
|
|
70
69
|
onSearch(searchTerm);
|
|
71
|
-
log('🔍 Search triggered:', searchTerm);
|
|
72
70
|
}
|
|
73
71
|
}
|
|
74
72
|
|
|
@@ -76,7 +74,6 @@
|
|
|
76
74
|
searchTerm = '';
|
|
77
75
|
if (onSearch) {
|
|
78
76
|
onSearch('');
|
|
79
|
-
log('🧹 Search cleared');
|
|
80
77
|
}
|
|
81
78
|
}
|
|
82
79
|
|
|
@@ -100,12 +97,10 @@
|
|
|
100
97
|
|
|
101
98
|
function handleColorDimensionChange(dimension: ColorDimension) {
|
|
102
99
|
onColorDimensionChange?.(dimension);
|
|
103
|
-
log('🎨 Color dimension changed:', dimension);
|
|
104
100
|
}
|
|
105
101
|
|
|
106
102
|
function handleSingleRootSelectChange(enabled: boolean) {
|
|
107
103
|
onSingleRootSelectChange?.(enabled);
|
|
108
|
-
log('🔘 Single root select mode:', enabled);
|
|
109
104
|
|
|
110
105
|
// When enabling single root mode, uncheck all roots except the first one
|
|
111
106
|
if (enabled && treeStore) {
|
|
@@ -115,7 +110,6 @@
|
|
|
115
110
|
store.state.checkedPaths.has(path)
|
|
116
111
|
);
|
|
117
112
|
if (checkedRoots.length > 1) {
|
|
118
|
-
log('🔘 Multiple roots selected, keeping only first one:', checkedRoots[0]);
|
|
119
113
|
for (let i = 1; i < checkedRoots.length; i++) {
|
|
120
114
|
store.toggle(checkedRoots[i]);
|
|
121
115
|
}
|
|
@@ -126,7 +120,6 @@
|
|
|
126
120
|
|
|
127
121
|
function handleSingleLevel1SelectChange(enabled: boolean) {
|
|
128
122
|
onSingleLevel1SelectChange?.(enabled);
|
|
129
|
-
log('🔘 Single Level 1 select mode:', enabled);
|
|
130
123
|
}
|
|
131
124
|
</script>
|
|
132
125
|
|
|
@@ -103,36 +103,3 @@ export function extractBaseMetrics(layout) {
|
|
|
103
103
|
});
|
|
104
104
|
return Array.from(metrics);
|
|
105
105
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Get a distinct color for each cell line
|
|
108
|
-
* Uses a predefined color palette with good contrast
|
|
109
|
-
*/
|
|
110
|
-
function getColorForIndex(index) {
|
|
111
|
-
const colors = [
|
|
112
|
-
'#0d6efd', // Blue
|
|
113
|
-
'#198754', // Green
|
|
114
|
-
'#dc3545', // Red
|
|
115
|
-
'#ffc107', // Yellow
|
|
116
|
-
'#0dcaf0', // Cyan
|
|
117
|
-
'#6f42c1', // Purple
|
|
118
|
-
'#fd7e14', // Orange
|
|
119
|
-
'#20c997', // Teal
|
|
120
|
-
'#d63384', // Pink
|
|
121
|
-
'#6610f2', // Indigo
|
|
122
|
-
'#17a2b8', // Info
|
|
123
|
-
'#28a745', // Success
|
|
124
|
-
'#e83e8c', // Magenta
|
|
125
|
-
'#6c757d', // Gray
|
|
126
|
-
'#007bff', // Primary
|
|
127
|
-
'#28a745', // Green variant
|
|
128
|
-
'#17a2b8', // Cyan variant
|
|
129
|
-
'#ffc107', // Amber
|
|
130
|
-
'#dc3545', // Danger
|
|
131
|
-
'#343a40', // Dark
|
|
132
|
-
'#6c757d', // Secondary
|
|
133
|
-
'#fd7e14', // Orange variant
|
|
134
|
-
'#20c997', // Teal variant
|
|
135
|
-
'#6f42c1' // Violet
|
|
136
|
-
];
|
|
137
|
-
return colors[index % colors.length];
|
|
138
|
-
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Data Transforms for Site Check Component
|
|
3
3
|
* Converts raw CSV data to TreeView nodes and Chart configurations
|
|
4
4
|
*/
|
|
5
|
-
import { log } from '../../core/logger';
|
|
6
5
|
/**
|
|
7
6
|
* Generate a deterministic, production-safe stackgroup ID
|
|
8
7
|
* This function ensures stackgroups are:
|
|
@@ -37,16 +36,17 @@ export function createStackGroupId(value, mode) {
|
|
|
37
36
|
export function assignStackGroups(kpis, cells, mode) {
|
|
38
37
|
// Create a mapping of cellName → metadata for quick lookup
|
|
39
38
|
const cellMetadata = new Map(cells);
|
|
40
|
-
|
|
39
|
+
// Track missing cells for aggregated warning
|
|
40
|
+
const missingCells = [];
|
|
41
|
+
const result = kpis.map(kpi => {
|
|
41
42
|
// Extract cellName from the rawName (format: "METRIC_CELLNAME")
|
|
42
43
|
const cellName = kpi.rawName.split('_').slice(1).join('_');
|
|
43
44
|
const record = cellMetadata.get(cellName);
|
|
44
45
|
if (!record) {
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
});
|
|
46
|
+
// Track missing cell for aggregated warning
|
|
47
|
+
if (!missingCells.includes(cellName)) {
|
|
48
|
+
missingCells.push(cellName);
|
|
49
|
+
}
|
|
50
50
|
return {
|
|
51
51
|
...kpi,
|
|
52
52
|
stackGroup: createStackGroupId(null, mode)
|
|
@@ -77,18 +77,16 @@ export function assignStackGroups(kpis, cells, mode) {
|
|
|
77
77
|
}
|
|
78
78
|
// Create deterministic stackgroup ID
|
|
79
79
|
const stackGroup = createStackGroupId(groupValue, mode);
|
|
80
|
-
log('📊 Assigned stackgroup', {
|
|
81
|
-
kpiName: kpi.name,
|
|
82
|
-
cellName,
|
|
83
|
-
mode,
|
|
84
|
-
groupValue,
|
|
85
|
-
stackGroup
|
|
86
|
-
});
|
|
87
80
|
return {
|
|
88
81
|
...kpi,
|
|
89
82
|
stackGroup
|
|
90
83
|
};
|
|
91
84
|
});
|
|
85
|
+
// Log aggregated warning if any cells were missing
|
|
86
|
+
if (missingCells.length > 0) {
|
|
87
|
+
console.warn(`[SiteCheck] ${missingCells.length} cells not found for KPIs, using default stackgroup. Cells: ${missingCells.slice(0, 5).join(', ')}${missingCells.length > 5 ? ` (+${missingCells.length - 5} more)` : ''}`);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
92
90
|
}
|
|
93
91
|
/**
|
|
94
92
|
* Band frequency mapping for consistent ordering
|
|
@@ -155,11 +153,6 @@ export function sortCellsByBandFrequency(items) {
|
|
|
155
153
|
* @param grouping - Tree grouping configuration (defaults to Site → Azimuth → Cell)
|
|
156
154
|
*/
|
|
157
155
|
export function buildTreeNodes(data, grouping = { level0: 'site', level1: 'azimuth' }) {
|
|
158
|
-
log('🔄 Building tree nodes', {
|
|
159
|
-
recordCount: data.length,
|
|
160
|
-
grouping,
|
|
161
|
-
treeDepth: grouping.level1 === null ? 2 : 3
|
|
162
|
-
});
|
|
163
156
|
// Check if this is a 2-level tree (no level1)
|
|
164
157
|
if (grouping.level1 === null) {
|
|
165
158
|
return build2LevelTree(data, grouping);
|
|
@@ -237,11 +230,6 @@ export function buildTreeNodes(data, grouping = { level0: 'site', level1: 'azimu
|
|
|
237
230
|
});
|
|
238
231
|
treeNodes.push(level0Node);
|
|
239
232
|
});
|
|
240
|
-
log('✅ Tree nodes built', {
|
|
241
|
-
totalNodes: treeNodes.length,
|
|
242
|
-
grouping,
|
|
243
|
-
sampleNode: treeNodes[0]?.label
|
|
244
|
-
});
|
|
245
233
|
return treeNodes;
|
|
246
234
|
}
|
|
247
235
|
/**
|
|
@@ -299,11 +287,6 @@ function build2LevelTree(data, grouping) {
|
|
|
299
287
|
});
|
|
300
288
|
treeNodes.push(level0Node);
|
|
301
289
|
});
|
|
302
|
-
log('✅ 2-level tree nodes built', {
|
|
303
|
-
totalNodes: treeNodes.length,
|
|
304
|
-
grouping: `${grouping.level0} → cell`,
|
|
305
|
-
sampleNode: treeNodes[0]?.label
|
|
306
|
-
});
|
|
307
290
|
return treeNodes;
|
|
308
291
|
}
|
|
309
292
|
/**
|
|
@@ -351,14 +334,10 @@ function compareValues(a, b) {
|
|
|
351
334
|
}
|
|
352
335
|
/**
|
|
353
336
|
* Get icon emoji based on band technology
|
|
337
|
+
* Currently disabled - returns empty string to keep tree labels clean
|
|
354
338
|
*/
|
|
355
|
-
function getBandIcon(
|
|
339
|
+
function getBandIcon(_band) {
|
|
356
340
|
return '';
|
|
357
|
-
if (band.startsWith('NR'))
|
|
358
|
-
return '📶'; // 5G
|
|
359
|
-
if (band.startsWith('LTE'))
|
|
360
|
-
return '📱'; // 4G
|
|
361
|
-
return '📡'; // Fallback
|
|
362
341
|
}
|
|
363
342
|
/**
|
|
364
343
|
* Filter chart data based on selected tree paths
|
|
@@ -366,11 +345,6 @@ function getBandIcon(band) {
|
|
|
366
345
|
* Handles both 2-level (level0:cellName) and 3-level (level0:level1:cellName) paths
|
|
367
346
|
*/
|
|
368
347
|
export function filterChartData(data, checkedPaths) {
|
|
369
|
-
log('🔄 Filtering chart data', {
|
|
370
|
-
totalRecords: data.length,
|
|
371
|
-
checkedPathsCount: checkedPaths.size,
|
|
372
|
-
paths: Array.from(checkedPaths)
|
|
373
|
-
});
|
|
374
348
|
// Extract cell names from checked leaf paths
|
|
375
349
|
const selectedCells = new Set();
|
|
376
350
|
checkedPaths.forEach((path) => {
|
|
@@ -385,12 +359,7 @@ export function filterChartData(data, checkedPaths) {
|
|
|
385
359
|
}
|
|
386
360
|
});
|
|
387
361
|
// Filter data to only include selected cells
|
|
388
|
-
|
|
389
|
-
log('✅ Filtered chart data', {
|
|
390
|
-
selectedCells: Array.from(selectedCells),
|
|
391
|
-
filteredCount: filtered.length
|
|
392
|
-
});
|
|
393
|
-
return filtered;
|
|
362
|
+
return data.filter((record) => selectedCells.has(record.cellName));
|
|
394
363
|
}
|
|
395
364
|
/**
|
|
396
365
|
* Transform data for chart component consumption
|
|
@@ -401,10 +370,6 @@ export function filterChartData(data, checkedPaths) {
|
|
|
401
370
|
* @param baseMetrics - Array of metric names to pivot (e.g., ['DL_GBYTES', 'UL_GBYTES'])
|
|
402
371
|
*/
|
|
403
372
|
export function transformChartData(data, baseMetrics) {
|
|
404
|
-
log('🔄 Transforming chart data', {
|
|
405
|
-
rowCount: data.length,
|
|
406
|
-
baseMetrics
|
|
407
|
-
});
|
|
408
373
|
// Group data by date
|
|
409
374
|
const dateMap = new Map();
|
|
410
375
|
data.forEach((record) => {
|
|
@@ -435,10 +400,6 @@ export function transformChartData(data, baseMetrics) {
|
|
|
435
400
|
});
|
|
436
401
|
// Sort by date
|
|
437
402
|
pivotedData.sort((a, b) => a.TIMESTAMP.localeCompare(b.TIMESTAMP));
|
|
438
|
-
log('✅ Chart data transformed', {
|
|
439
|
-
outputRows: pivotedData.length,
|
|
440
|
-
sampleColumns: pivotedData[0] ? Object.keys(pivotedData[0]) : []
|
|
441
|
-
});
|
|
442
403
|
return pivotedData;
|
|
443
404
|
}
|
|
444
405
|
/**
|
|
@@ -508,17 +469,6 @@ export function createStyledKPI(metricName, cellRecord, unit, grouping, colorDim
|
|
|
508
469
|
...(color && { color }),
|
|
509
470
|
...(lineStyle && { lineStyle })
|
|
510
471
|
};
|
|
511
|
-
log('🎨 Styled KPI created', {
|
|
512
|
-
metricName,
|
|
513
|
-
cellName,
|
|
514
|
-
displayName,
|
|
515
|
-
band,
|
|
516
|
-
sector,
|
|
517
|
-
azimuth,
|
|
518
|
-
colorDimension,
|
|
519
|
-
color,
|
|
520
|
-
lineStyle
|
|
521
|
-
});
|
|
522
472
|
return kpi;
|
|
523
473
|
}
|
|
524
474
|
/**
|