@smartnet360/svelte-components 0.0.82 → 0.0.84
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/site-check/SiteCheck.svelte +118 -42
- package/dist/apps/site-check/data-loader.d.ts +5 -4
- package/dist/apps/site-check/helper.d.ts +3 -2
- package/dist/apps/site-check/helper.js +8 -7
- package/dist/apps/site-check/transforms.d.ts +3 -1
- package/dist/apps/site-check/transforms.js +65 -4
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<script lang="ts">
|
|
4
4
|
import { TreeView, createTreeStore } from '../../core/TreeView';
|
|
5
5
|
import { ChartComponent, type Layout, type CellStylingConfig } from '../../core/Charts';
|
|
6
|
-
import { buildTreeNodes, filterChartData, transformChartData, type CellTrafficRecord, defaultCellStyling, type TreeGroupingConfig, defaultTreeGrouping } from './index';
|
|
6
|
+
import { buildTreeNodes, filterChartData, transformChartData, type CellTrafficRecord, defaultCellStyling, type TreeGroupingConfig, type TreeGroupField, defaultTreeGrouping } from './index';
|
|
7
7
|
import { expandLayoutForCells } from './helper';
|
|
8
8
|
import { log } from '../../core/logger';
|
|
9
9
|
import type {ChartMarker, Mode } from '../../index.js';
|
|
@@ -58,19 +58,56 @@
|
|
|
58
58
|
// Internal state for current grouping
|
|
59
59
|
let treeGrouping = $state<TreeGroupingConfig>(initialGrouping);
|
|
60
60
|
|
|
61
|
-
// Available grouping
|
|
62
|
-
const
|
|
63
|
-
{
|
|
64
|
-
{
|
|
65
|
-
{
|
|
66
|
-
|
|
67
|
-
// { label: 'Azimuth → Site → Cell', value: { level0: 'azimuth', level1: 'site', level2: 'cell' } as TreeGroupingConfig },
|
|
68
|
-
{ label: 'Band → Cell', value: { level0: 'band', level1: null, level2: 'cell' } as TreeGroupingConfig },
|
|
69
|
-
// { label: 'Site → Cell (2-level)', value: { level0: 'site', level1: null, level2: 'cell' } as TreeGroupingConfig },
|
|
70
|
-
// { label: 'Azimuth → Cell (2-level)', value: { level0: 'azimuth', level1: null, level2: 'cell' } as TreeGroupingConfig },
|
|
61
|
+
// Available field options for grouping levels
|
|
62
|
+
const fieldOptions: { value: TreeGroupField; label: string }[] = [
|
|
63
|
+
{ value: 'site', label: 'Site' },
|
|
64
|
+
{ value: 'band', label: 'Band' },
|
|
65
|
+
{ value: 'azimuth', label: 'Azimuth' },
|
|
66
|
+
{ value: 'cell', label: 'Cell' }
|
|
71
67
|
];
|
|
72
68
|
|
|
73
|
-
|
|
69
|
+
// Handlers for level changes
|
|
70
|
+
function handleLevel0Change(value: TreeGroupField) {
|
|
71
|
+
// Clear level1 if it conflicts with new level0
|
|
72
|
+
const newLevel1 = treeGrouping.level1 === value ? null : treeGrouping.level1;
|
|
73
|
+
// Clear level2 if it conflicts with new level0
|
|
74
|
+
let newLevel2 = treeGrouping.level2;
|
|
75
|
+
if (newLevel2 !== null && newLevel2 !== 'cell' && newLevel2 === value) {
|
|
76
|
+
newLevel2 = null;
|
|
77
|
+
}
|
|
78
|
+
treeGrouping = {
|
|
79
|
+
level0: value,
|
|
80
|
+
level1: newLevel1,
|
|
81
|
+
level2: newLevel2
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function handleLevel1Change(value: TreeGroupField | 'none') {
|
|
86
|
+
const newLevel1 = value === 'none' ? null : value;
|
|
87
|
+
// Clear level2 if it conflicts with new level1
|
|
88
|
+
let newLevel2 = treeGrouping.level2;
|
|
89
|
+
if (newLevel2 !== null && newLevel2 !== 'cell' && newLevel2 === newLevel1) {
|
|
90
|
+
newLevel2 = null;
|
|
91
|
+
}
|
|
92
|
+
treeGrouping = {
|
|
93
|
+
level0: treeGrouping.level0,
|
|
94
|
+
level1: newLevel1,
|
|
95
|
+
level2: newLevel2
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Get available options for level1 (exclude level0)
|
|
100
|
+
let availableLevel1Options = $derived.by(() => {
|
|
101
|
+
return fieldOptions.filter(opt => opt.value !== treeGrouping.level0);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Get available options for level2 (exclude level0 and level1)
|
|
105
|
+
let availableLevel2Options = $derived.by(() => {
|
|
106
|
+
return fieldOptions.filter(opt =>
|
|
107
|
+
opt.value !== treeGrouping.level0 &&
|
|
108
|
+
opt.value !== treeGrouping.level1
|
|
109
|
+
);
|
|
110
|
+
}); let treeStore = $state<ReturnType<typeof createTreeStore> | null>(null);
|
|
74
111
|
|
|
75
112
|
// Rebuild tree whenever treeGrouping changes
|
|
76
113
|
$effect(() => {
|
|
@@ -137,14 +174,16 @@
|
|
|
137
174
|
|
|
138
175
|
// Expand layout based on selected cells and chosen base layout
|
|
139
176
|
let chartLayout = $derived.by(() => {
|
|
140
|
-
// Pass cellStyling - helper will decide per-section whether to use
|
|
141
|
-
|
|
177
|
+
// Pass cellStyling and treeGrouping - helper will decide per-section whether to use styling,
|
|
178
|
+
// and generate appropriate labels based on grouping
|
|
179
|
+
const expanded = expandLayoutForCells(selectedBaseLayout, filteredData, treeGrouping, cellStyling);
|
|
142
180
|
log('📐 Chart Layout:', {
|
|
143
181
|
layoutName: selectedBaseLayout.layoutName,
|
|
144
182
|
layoutDefaultColors: selectedBaseLayout.useDefaultChartColors ?? false,
|
|
145
183
|
sectionsCount: expanded.sections.length,
|
|
146
184
|
totalCharts: expanded.sections.reduce((sum, s) => sum + s.charts.length, 0),
|
|
147
|
-
firstSection: expanded.sections[0]
|
|
185
|
+
firstSection: expanded.sections[0],
|
|
186
|
+
grouping: treeGrouping
|
|
148
187
|
});
|
|
149
188
|
return expanded;
|
|
150
189
|
});
|
|
@@ -278,34 +317,71 @@
|
|
|
278
317
|
<i class="bi bi-search"></i>
|
|
279
318
|
</button>
|
|
280
319
|
</div>
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
<!-- Grouping Selector -->
|
|
285
|
-
{#if showGroupingSelector}
|
|
286
|
-
<div class="p-3 border-bottom flex-shrink-0">
|
|
287
|
-
<label for="groupingSelect" class="form-label small fw-semibold mb-2">
|
|
288
|
-
Tree Grouping
|
|
289
|
-
</label>
|
|
290
|
-
<select
|
|
291
|
-
id="groupingSelect"
|
|
292
|
-
class="form-select form-select-sm"
|
|
293
|
-
onchange={(e) => {
|
|
294
|
-
const index = parseInt(e.currentTarget.value);
|
|
295
|
-
treeGrouping = groupingPresets[index].value;
|
|
296
|
-
}}
|
|
297
|
-
>
|
|
298
|
-
{#each groupingPresets as preset, i}
|
|
299
|
-
<option value={i} selected={JSON.stringify(preset.value) === JSON.stringify(treeGrouping)}>
|
|
300
|
-
{preset.label}
|
|
301
|
-
</option>
|
|
302
|
-
{/each}
|
|
303
|
-
</select>
|
|
304
|
-
</div>
|
|
305
|
-
{/if}
|
|
306
|
-
{/if}
|
|
320
|
+
</div>
|
|
321
|
+
{/if}
|
|
307
322
|
|
|
308
|
-
|
|
323
|
+
<!-- Grouping Selector -->
|
|
324
|
+
{#if showGroupingSelector}
|
|
325
|
+
<div class="p-3 border-bottom flex-shrink-0">
|
|
326
|
+
<div class="small fw-semibold mb-2">Tree Grouping</div>
|
|
327
|
+
|
|
328
|
+
<!-- Level 0 (Mandatory) -->
|
|
329
|
+
<div class="mb-2">
|
|
330
|
+
<label for="level0Select" class="form-label small mb-1">Level 0 (Root)</label>
|
|
331
|
+
<select
|
|
332
|
+
id="level0Select"
|
|
333
|
+
class="form-select form-select-sm"
|
|
334
|
+
value={treeGrouping.level0}
|
|
335
|
+
onchange={(e) => handleLevel0Change(e.currentTarget.value as TreeGroupField)}
|
|
336
|
+
>
|
|
337
|
+
{#each fieldOptions as option}
|
|
338
|
+
<option value={option.value}>{option.label}</option>
|
|
339
|
+
{/each}
|
|
340
|
+
</select>
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
<!-- Level 1 (Optional) -->
|
|
344
|
+
<div class="mb-2">
|
|
345
|
+
<label for="level1Select" class="form-label small mb-1">Level 1 (Optional)</label>
|
|
346
|
+
<select
|
|
347
|
+
id="level1Select"
|
|
348
|
+
class="form-select form-select-sm"
|
|
349
|
+
value={treeGrouping.level1 ?? 'none'}
|
|
350
|
+
onchange={(e) => handleLevel1Change(e.currentTarget.value as TreeGroupField | 'none')}
|
|
351
|
+
>
|
|
352
|
+
<option value="none">None</option>
|
|
353
|
+
{#each availableLevel1Options as option}
|
|
354
|
+
<option value={option.value}>{option.label}</option>
|
|
355
|
+
{/each}
|
|
356
|
+
</select>
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
<!-- Level 2 (Optional) -->
|
|
360
|
+
<div class="mb-0">
|
|
361
|
+
<label for="level2Select" class="form-label small mb-1">Level 2 (Optional)</label>
|
|
362
|
+
<select
|
|
363
|
+
id="level2Select"
|
|
364
|
+
class="form-select form-select-sm"
|
|
365
|
+
value={treeGrouping.level2 ?? 'none'}
|
|
366
|
+
onchange={(e) => {
|
|
367
|
+
const value = e.currentTarget.value;
|
|
368
|
+
treeGrouping = {
|
|
369
|
+
level0: treeGrouping.level0,
|
|
370
|
+
level1: treeGrouping.level1,
|
|
371
|
+
level2: value === 'none' ? null : (value as 'cell')
|
|
372
|
+
};
|
|
373
|
+
}}
|
|
374
|
+
>
|
|
375
|
+
<option value="none">None</option>
|
|
376
|
+
<option value="cell">Cell</option>
|
|
377
|
+
{#each availableLevel2Options as option}
|
|
378
|
+
<option value={option.value}>{option.label}</option>
|
|
379
|
+
{/each}
|
|
380
|
+
</select>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
{/if}
|
|
384
|
+
{/if} <!-- Tree View -->
|
|
309
385
|
<div class="flex-grow-1" style="min-height: 0; overflow: hidden;">
|
|
310
386
|
{#if treeStore}
|
|
311
387
|
<TreeView store={$treeStore!} showControls={true} showIndeterminate={true} height="100%" />
|
|
@@ -14,17 +14,18 @@ export interface CellTrafficRecord {
|
|
|
14
14
|
/**
|
|
15
15
|
* Tree grouping field types
|
|
16
16
|
*/
|
|
17
|
-
export type TreeGroupField = 'site' | 'azimuth' | 'band' | '
|
|
17
|
+
export type TreeGroupField = 'site' | 'azimuth' | 'band' | 'cell';
|
|
18
18
|
/**
|
|
19
19
|
* Configuration for tree hierarchy grouping
|
|
20
20
|
* Defines which fields appear at each level of the tree
|
|
21
|
-
* - For 3-level tree: level0 → level1 →
|
|
22
|
-
* - For 2-level tree: level0 →
|
|
21
|
+
* - For 3-level tree: level0 → level1 → level2
|
|
22
|
+
* - For 2-level tree: level0 → level1 (set level2 to null)
|
|
23
|
+
* - For 1-level tree: level0 only (set level1 and level2 to null)
|
|
23
24
|
*/
|
|
24
25
|
export interface TreeGroupingConfig {
|
|
25
26
|
level0: TreeGroupField;
|
|
26
27
|
level1: TreeGroupField | null;
|
|
27
|
-
level2: 'cell';
|
|
28
|
+
level2: TreeGroupField | 'cell' | null;
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
30
31
|
* Default tree grouping: Site → Azimuth → Cell (3-level)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Layout, CellStylingConfig } from '../../core/Charts';
|
|
2
|
-
import type { CellTrafficRecord } from './';
|
|
2
|
+
import type { CellTrafficRecord, TreeGroupingConfig } from './';
|
|
3
3
|
import { type StackGroupMode } from './transforms.js';
|
|
4
4
|
/**
|
|
5
5
|
* Expand base layout configuration with dynamic KPIs based on selected cells
|
|
@@ -7,11 +7,12 @@ import { type StackGroupMode } from './transforms.js';
|
|
|
7
7
|
*
|
|
8
8
|
* @param baseLayout - The base layout configuration from JSON
|
|
9
9
|
* @param data - Filtered cell traffic records for selected cells
|
|
10
|
+
* @param grouping - Current tree grouping configuration (determines label format)
|
|
10
11
|
* @param stylingConfig - Optional cell styling configuration (band colors, sector line styles)
|
|
11
12
|
* @param stackGroupMode - Optional stackgroup strategy for stacked charts (default: 'none' = single stack)
|
|
12
13
|
* @returns Expanded layout with cell-specific KPIs
|
|
13
14
|
*/
|
|
14
|
-
export declare function expandLayoutForCells(baseLayout: Layout, data: CellTrafficRecord[], stylingConfig?: CellStylingConfig, stackGroupMode?: StackGroupMode): Layout;
|
|
15
|
+
export declare function expandLayoutForCells(baseLayout: Layout, data: CellTrafficRecord[], grouping: TreeGroupingConfig, stylingConfig?: CellStylingConfig, stackGroupMode?: StackGroupMode): Layout;
|
|
15
16
|
/**
|
|
16
17
|
* Extract base metric names from a layout configuration
|
|
17
18
|
* Returns unique metric rawNames that need to be pivoted
|
|
@@ -5,11 +5,12 @@ import { createStyledKPI, sortCellsByBandFrequency, assignStackGroups } from './
|
|
|
5
5
|
*
|
|
6
6
|
* @param baseLayout - The base layout configuration from JSON
|
|
7
7
|
* @param data - Filtered cell traffic records for selected cells
|
|
8
|
+
* @param grouping - Current tree grouping configuration (determines label format)
|
|
8
9
|
* @param stylingConfig - Optional cell styling configuration (band colors, sector line styles)
|
|
9
10
|
* @param stackGroupMode - Optional stackgroup strategy for stacked charts (default: 'none' = single stack)
|
|
10
11
|
* @returns Expanded layout with cell-specific KPIs
|
|
11
12
|
*/
|
|
12
|
-
export function expandLayoutForCells(baseLayout, data, stylingConfig, stackGroupMode = 'none') {
|
|
13
|
+
export function expandLayoutForCells(baseLayout, data, grouping, stylingConfig, stackGroupMode = 'none') {
|
|
13
14
|
// Get unique cells and their metadata, sorted by band frequency
|
|
14
15
|
const cellMap = new Map();
|
|
15
16
|
data.forEach((record) => {
|
|
@@ -37,8 +38,8 @@ export function expandLayoutForCells(baseLayout, data, stylingConfig, stackGroup
|
|
|
37
38
|
...section,
|
|
38
39
|
charts: section.charts.map((chart) => ({
|
|
39
40
|
...chart,
|
|
40
|
-
yLeft: expandKPIs(chart.yLeft, cells, effectiveStyling, stackGroupMode),
|
|
41
|
-
yRight: expandKPIs(chart.yRight, cells, effectiveStyling, stackGroupMode)
|
|
41
|
+
yLeft: expandKPIs(chart.yLeft, cells, grouping, effectiveStyling, stackGroupMode),
|
|
42
|
+
yRight: expandKPIs(chart.yRight, cells, grouping, effectiveStyling, stackGroupMode)
|
|
42
43
|
}))
|
|
43
44
|
};
|
|
44
45
|
})
|
|
@@ -52,17 +53,18 @@ export function expandLayoutForCells(baseLayout, data, stylingConfig, stackGroup
|
|
|
52
53
|
*
|
|
53
54
|
* @param baseKPIs - Array of base KPIs from layout
|
|
54
55
|
* @param cells - Array of [cellName, record] tuples
|
|
56
|
+
* @param grouping - Current tree grouping configuration (determines label format)
|
|
55
57
|
* @param stylingConfig - Optional cell styling configuration
|
|
56
58
|
* @param stackGroupMode - Stackgroup strategy for this set of KPIs
|
|
57
59
|
* @returns Expanded array of KPIs (styled or default, with stackgroups assigned)
|
|
58
60
|
*/
|
|
59
|
-
function expandKPIs(baseKPIs, cells, stylingConfig, stackGroupMode = 'none') {
|
|
61
|
+
function expandKPIs(baseKPIs, cells, grouping, stylingConfig, stackGroupMode = 'none') {
|
|
60
62
|
let expandedKPIs = [];
|
|
61
63
|
baseKPIs.forEach((baseKPI) => {
|
|
62
64
|
cells.forEach(([cellName, record]) => {
|
|
63
65
|
if (stylingConfig) {
|
|
64
66
|
// Apply custom styling (band colors, sector line styles)
|
|
65
|
-
const styledKPI = createStyledKPI(baseKPI.rawName, record, baseKPI.unit, stylingConfig);
|
|
67
|
+
const styledKPI = createStyledKPI(baseKPI.rawName, record, baseKPI.unit, grouping, stylingConfig);
|
|
66
68
|
expandedKPIs.push({
|
|
67
69
|
...styledKPI,
|
|
68
70
|
stackGroup: undefined // Initialize for treeshake-safe property assignment
|
|
@@ -78,8 +80,7 @@ function expandKPIs(baseKPIs, cells, stylingConfig, stackGroupMode = 'none') {
|
|
|
78
80
|
});
|
|
79
81
|
}
|
|
80
82
|
});
|
|
81
|
-
});
|
|
82
|
-
// Apply stackgroups to all expanded KPIs
|
|
83
|
+
}); // Apply stackgroups to all expanded KPIs
|
|
83
84
|
expandedKPIs = assignStackGroups(expandedKPIs, cells, stackGroupMode);
|
|
84
85
|
return expandedKPIs;
|
|
85
86
|
}
|
|
@@ -74,10 +74,12 @@ export declare function filterChartData(data: CellTrafficRecord[], checkedPaths:
|
|
|
74
74
|
export declare function transformChartData(data: CellTrafficRecord[], baseMetrics: string[]): any[];
|
|
75
75
|
/**
|
|
76
76
|
* Create a styled KPI with band colors and sector line styles
|
|
77
|
+
* Label format adapts to tree grouping configuration
|
|
77
78
|
* @param metricName - Base metric name (e.g., 'DL_GBYTES')
|
|
78
79
|
* @param cellRecord - Cell traffic record with metadata
|
|
79
80
|
* @param unit - Unit string (e.g., 'GB', '%')
|
|
81
|
+
* @param grouping - Current tree grouping configuration (determines label format)
|
|
80
82
|
* @param stylingConfig - Optional styling configuration
|
|
81
83
|
* @returns KPI with cell-specific styling applied
|
|
82
84
|
*/
|
|
83
|
-
export declare function createStyledKPI(metricName: string, cellRecord: CellTrafficRecord, unit: string, stylingConfig?: CellStylingConfig): KPI;
|
|
85
|
+
export declare function createStyledKPI(metricName: string, cellRecord: CellTrafficRecord, unit: string, grouping: TreeGroupingConfig, stylingConfig?: CellStylingConfig): KPI;
|
|
@@ -443,20 +443,22 @@ export function transformChartData(data, baseMetrics) {
|
|
|
443
443
|
}
|
|
444
444
|
/**
|
|
445
445
|
* Create a styled KPI with band colors and sector line styles
|
|
446
|
+
* Label format adapts to tree grouping configuration
|
|
446
447
|
* @param metricName - Base metric name (e.g., 'DL_GBYTES')
|
|
447
448
|
* @param cellRecord - Cell traffic record with metadata
|
|
448
449
|
* @param unit - Unit string (e.g., 'GB', '%')
|
|
450
|
+
* @param grouping - Current tree grouping configuration (determines label format)
|
|
449
451
|
* @param stylingConfig - Optional styling configuration
|
|
450
452
|
* @returns KPI with cell-specific styling applied
|
|
451
453
|
*/
|
|
452
|
-
export function createStyledKPI(metricName, cellRecord, unit, stylingConfig) {
|
|
453
|
-
const { band, sector, azimuth, cellName } = cellRecord;
|
|
454
|
+
export function createStyledKPI(metricName, cellRecord, unit, grouping, stylingConfig) {
|
|
455
|
+
const { band, sector, azimuth, cellName, siteName } = cellRecord;
|
|
454
456
|
// Get color from band (if config provided)
|
|
455
457
|
const color = stylingConfig?.bandColors?.[band];
|
|
456
458
|
// Get line style from sector (if config provided)
|
|
457
459
|
const lineStyle = stylingConfig?.sectorLineStyles?.[sector.toString()];
|
|
458
|
-
//
|
|
459
|
-
const displayName =
|
|
460
|
+
// Generate label based on tree grouping configuration
|
|
461
|
+
const displayName = generateAdaptiveLabel(cellRecord, grouping);
|
|
460
462
|
// Build KPI with cell-specific styling
|
|
461
463
|
const kpi = {
|
|
462
464
|
rawName: `${metricName}_${cellName}`, // Column name in pivoted data
|
|
@@ -478,3 +480,62 @@ export function createStyledKPI(metricName, cellRecord, unit, stylingConfig) {
|
|
|
478
480
|
});
|
|
479
481
|
return kpi;
|
|
480
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* Generate adaptive label based on tree grouping configuration
|
|
485
|
+
* Label exactly matches the complete tree path shown in the tree view
|
|
486
|
+
* Only includes levels that are not null in the grouping config
|
|
487
|
+
* @param record - Cell traffic record with all metadata
|
|
488
|
+
* @param grouping - Current tree grouping configuration
|
|
489
|
+
* @returns Formatted label string matching complete tree hierarchy (only non-null levels)
|
|
490
|
+
*/
|
|
491
|
+
function generateAdaptiveLabel(record, grouping) {
|
|
492
|
+
const { level0, level1, level2 } = grouping;
|
|
493
|
+
// Helper to format field values exactly as shown in tree
|
|
494
|
+
const formatField = (field, value) => {
|
|
495
|
+
if (field === 'cell') {
|
|
496
|
+
return value; // Cell name as-is
|
|
497
|
+
}
|
|
498
|
+
switch (field) {
|
|
499
|
+
case 'azimuth':
|
|
500
|
+
return `${value}°`;
|
|
501
|
+
case 'sector':
|
|
502
|
+
return `Sector ${value}`;
|
|
503
|
+
case 'band':
|
|
504
|
+
return value;
|
|
505
|
+
case 'site':
|
|
506
|
+
return value;
|
|
507
|
+
default:
|
|
508
|
+
return String(value);
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
// Get field values from record
|
|
512
|
+
const getFieldValue = (field) => {
|
|
513
|
+
if (field === 'cell') {
|
|
514
|
+
return record.cellName;
|
|
515
|
+
}
|
|
516
|
+
switch (field) {
|
|
517
|
+
case 'site':
|
|
518
|
+
return record.siteName;
|
|
519
|
+
case 'band':
|
|
520
|
+
return record.band;
|
|
521
|
+
case 'azimuth':
|
|
522
|
+
return record.azimuth;
|
|
523
|
+
case 'sector':
|
|
524
|
+
return record.sector;
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
// Build label parts only for non-null levels
|
|
528
|
+
const parts = [];
|
|
529
|
+
// Level 0 is always present
|
|
530
|
+
parts.push(formatField(level0, getFieldValue(level0)));
|
|
531
|
+
// Level 1 (optional)
|
|
532
|
+
if (level1 !== null) {
|
|
533
|
+
parts.push(formatField(level1, getFieldValue(level1)));
|
|
534
|
+
}
|
|
535
|
+
// Level 2 (optional)
|
|
536
|
+
if (level2 !== null) {
|
|
537
|
+
parts.push(formatField(level2, getFieldValue(level2)));
|
|
538
|
+
}
|
|
539
|
+
// Join with arrow separator
|
|
540
|
+
return parts.join('_');
|
|
541
|
+
}
|