@smartnet360/svelte-components 0.0.135 → 0.0.137

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.
@@ -1,872 +0,0 @@
1
- /**
2
- * CellTable - Column Configuration
3
- *
4
- * Defines column definitions, presets, and formatters for the cell table
5
- */
6
- /**
7
- * Default technology colors (Bootstrap-aligned)
8
- * 2G: Yellow, 4G: Purple, 5G: Green
9
- */
10
- export const DEFAULT_TECH_COLORS = {
11
- '2G': '#ffc107', // Yellow
12
- '3G': '#0d6efd', // primary blue
13
- '4G': '#9333ea', // Purple
14
- '5G': '#22c55e', // Green
15
- 'LTE': '#9333ea', // Purple (same as 4G)
16
- 'NR': '#22c55e', // Green (same as 5G)
17
- };
18
- /**
19
- * Default status colors (Bootstrap-aligned)
20
- */
21
- export const DEFAULT_STATUS_COLORS = {
22
- 'On_Air': '#198754', // success green
23
- 'On_Air_UNDER_CONSTRUCTION': '#4ce207ff', // warning yellow
24
- 'On_Air_Locked': '#e04001ff', // info cyan
25
- 'RF_Plan_Ready': '#e69603ff', // primary blue
26
- 'Re-Planned_RF_Plan_Ready': '#c1a342ff', // purple
27
- 'Tavlati_RF_Plan_Ready': '#25ddfdff', // orange
28
- };
29
- /**
30
- * Frequency band colors
31
- * GSM: Yellow shades (darker = higher freq)
32
- * LTE: Purple shades (darker = higher freq)
33
- * 5G: Green shades (stronger = higher freq)
34
- */
35
- export const FBAND_COLORS = {
36
- // GSM - Yellow shades (900 lighter, 1800 darker)
37
- 'GSM900': '#ffc107', // Light yellow
38
- 'GSM1800': '#cc9a06', // Darker yellow/gold
39
- // LTE - Purple shades (700 lightest → 2600 darkest)
40
- 'LTE700': '#d8b4fe', // Light purple
41
- 'LTE800': '#c084fc', //
42
- 'LTE900': '#a855f7', //
43
- 'LTE1800': '#9333ea', //
44
- 'LTE2100': '#7e22ce', //
45
- 'LTE2600': '#6b21a8', // Dark purple
46
- // 5G - Green shades (700 lighter → 3500 stronger)
47
- '5G-700': '#86efac', // Light green
48
- '5G-2100': '#22c55e', // Medium green
49
- '5G-3500': '#15803d', // Strong/dark green
50
- 'NR3500': '#15803d', // Same as 5G-3500
51
- };
52
- /**
53
- * Column groups for different presets
54
- */
55
- export const COLUMN_GROUPS = {
56
- core: ['siteId', 'txId', 'cellName', 'tech', 'fband', 'status'],
57
- physical: ['antenna', 'azimuth', 'height', 'beamwidth'],
58
- network: ['nwET', 'nwPW', 'nwRS', 'nwBW', 'dlEarfn', 'bcch', 'pci', 'rru', 'cellID', 'cellId2G', 'ctrlid'],
59
- planning: ['planner', 'comment', 'onAirDate'],
60
- atoll: ['atollET', 'atollMT', 'atollPW', 'atollRS', 'atollBW'],
61
- position: ['latitude', 'longitude', 'siteLatitude', 'siteLongitude', 'dx', 'dy'],
62
- compare: ['compareET', 'comparePW', 'compareRS', 'compareBW'],
63
- kpi: ['kpiTraffic', 'kpiThroughput', 'kpiAvailability', 'kpiSuccessRate'],
64
- history: ['configDate', 'frequency', 'antennaChange', 'etiltChange', 'mtiltChange', 'powerChange'],
65
- };
66
- /**
67
- * Create a technology badge formatter
68
- */
69
- export function createTechFormatter(colors = DEFAULT_TECH_COLORS) {
70
- return (cell) => {
71
- const value = cell.getValue();
72
- const color = colors[value] || '#6c757d';
73
- return `<span class="badge" style="background-color: ${color}; color: white;">${value}</span>`;
74
- };
75
- }
76
- /**
77
- * Create a status badge formatter
78
- */
79
- export function createStatusFormatter(colors = DEFAULT_STATUS_COLORS) {
80
- return (cell) => {
81
- const value = cell.getValue();
82
- const color = colors[value] || '#6c757d';
83
- const displayValue = value.replace(/_/g, ' ');
84
- return `<span class="badge" style="background-color: ${color}; color: white; font-size: 0.7rem;">${displayValue}</span>`;
85
- };
86
- }
87
- /**
88
- * Create an fband badge formatter
89
- */
90
- export function createFbandFormatter() {
91
- return (cell) => {
92
- const value = cell.getValue();
93
- const color = FBAND_COLORS[value] || '#6c757d';
94
- return `<span class="badge" style="background-color: ${color}; color: white;">${value}</span>`;
95
- };
96
- }
97
- /**
98
- * Number formatter with fixed decimals
99
- */
100
- export function numberFormatter(decimals = 2) {
101
- return (cell) => {
102
- const value = cell.getValue();
103
- if (value === null || value === undefined || value === '')
104
- return '';
105
- const num = Number(value);
106
- return isNaN(num) ? String(value) : num.toFixed(decimals);
107
- };
108
- }
109
- /**
110
- * Coordinate formatter (lat/lng)
111
- */
112
- export function coordinateFormatter(cell) {
113
- const value = cell.getValue();
114
- if (value === null || value === undefined || value === '')
115
- return '';
116
- const num = Number(value);
117
- return isNaN(num) ? String(value) : num.toFixed(6);
118
- }
119
- /**
120
- * Azimuth formatter with degree symbol
121
- */
122
- export function azimuthFormatter(cell) {
123
- const value = cell.getValue();
124
- if (value === null || value === undefined || value === '')
125
- return '';
126
- return `${value}°`;
127
- }
128
- /**
129
- * Height formatter with meter unit
130
- */
131
- export function heightFormatter(cell) {
132
- const value = cell.getValue();
133
- if (value === null || value === undefined || value === '')
134
- return '';
135
- return `${value}m`;
136
- }
137
- /**
138
- * Comparison formatter for Atoll vs Network values
139
- * Shows both values side by side with color coding:
140
- * - Green: values match exactly
141
- * - Red: values don't match
142
- * - Gray: one or both values missing
143
- */
144
- export function createCompareFormatter(atollField, nwtField) {
145
- return (cell) => {
146
- const row = cell.getRow().getData();
147
- const atollVal = row[atollField];
148
- const nwtVal = row[nwtField];
149
- const atollStr = atollVal !== null && atollVal !== undefined ? String(atollVal) : '-';
150
- const nwtStr = nwtVal !== null && nwtVal !== undefined ? String(nwtVal) : '-';
151
- // Determine match status
152
- const atollMissing = atollVal === null || atollVal === undefined;
153
- const nwtMissing = nwtVal === null || nwtVal === undefined;
154
- let bgColor;
155
- let textColor = 'white';
156
- if (atollMissing || nwtMissing) {
157
- // One or both missing - gray
158
- bgColor = '#6c757d';
159
- }
160
- else if (atollVal === nwtVal) {
161
- // Exact match - green
162
- bgColor = '#198754';
163
- }
164
- else {
165
- // Mismatch - red
166
- bgColor = '#dc3545';
167
- }
168
- return `<span class="badge" style="background-color: ${bgColor}; color: ${textColor}; font-size: 0.75rem; font-weight: normal;">${atollStr} | ${nwtStr}</span>`;
169
- };
170
- }
171
- /**
172
- * Create a history change formatter for showing current vs previous values
173
- * Highlights changes in yellow/amber, unchanged values in muted style
174
- *
175
- * @param currentField - Field name for current value
176
- * @param prevField - Field name for previous value
177
- */
178
- export function createHistoryChangeFormatter(currentField, prevField) {
179
- return (cell) => {
180
- const row = cell.getRow().getData();
181
- const currentVal = row[currentField];
182
- const prevVal = row[prevField];
183
- const currentStr = currentVal !== null && currentVal !== undefined ? String(currentVal) : '-';
184
- const prevStr = prevVal !== null && prevVal !== undefined ? String(prevVal) : '-';
185
- const hasChanged = currentVal !== prevVal;
186
- if (hasChanged) {
187
- // Value changed - highlight with amber/warning color
188
- return `<span class="badge bg-warning text-dark" style="font-size: 0.75rem; font-weight: 500;" title="Changed from: ${prevStr}">${currentStr}</span>`;
189
- }
190
- else {
191
- // No change - muted style
192
- return `<span class="text-muted">${currentStr}</span>`;
193
- }
194
- };
195
- }
196
- /**
197
- * Format ISO date string to YYYY.MM.DD format for history display
198
- */
199
- export function historyDateFormatter(cell) {
200
- const value = cell.getValue();
201
- if (!value)
202
- return '-';
203
- try {
204
- const date = new Date(value);
205
- const year = date.getFullYear();
206
- const month = String(date.getMonth() + 1).padStart(2, '0');
207
- const day = String(date.getDate()).padStart(2, '0');
208
- return `${year}-${month}-${day}`;
209
- }
210
- catch {
211
- return value;
212
- }
213
- }
214
- export function createSparklineFormatter(options = {}) {
215
- const { width = 80, height = 24, lineColor = '#0d6efd', fillColor = 'rgba(13,110,253,0.2)', showDots = false, showLastValue = true, unit = '', decimals = 1 } = options;
216
- return (cell) => {
217
- const data = cell.getValue();
218
- if (!data || !Array.isArray(data) || data.length === 0) {
219
- return '<span class="text-muted">—</span>';
220
- }
221
- const values = data.filter(v => typeof v === 'number' && !isNaN(v));
222
- if (values.length === 0) {
223
- return '<span class="text-muted">—</span>';
224
- }
225
- const min = Math.min(...values);
226
- const max = Math.max(...values);
227
- const range = max - min || 1;
228
- const padding = 2;
229
- const chartWidth = showLastValue ? width - 35 : width - 4;
230
- const chartHeight = height - 4;
231
- // Generate SVG path
232
- const points = values.map((v, i) => {
233
- const x = padding + (i / (values.length - 1 || 1)) * (chartWidth - padding * 2);
234
- const y = padding + (1 - (v - min) / range) * (chartHeight - padding * 2);
235
- return { x, y, v };
236
- });
237
- // Line path
238
- const linePath = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x.toFixed(1)} ${p.y.toFixed(1)}`).join(' ');
239
- // Fill path (closed polygon)
240
- const fillPath = `${linePath} L ${points[points.length - 1].x.toFixed(1)} ${chartHeight - padding} L ${padding} ${chartHeight - padding} Z`;
241
- // Dots
242
- const dots = showDots
243
- ? points.map(p => `<circle cx="${p.x.toFixed(1)}" cy="${p.y.toFixed(1)}" r="1.5" fill="${lineColor}"/>`).join('')
244
- : '';
245
- // Last value text
246
- const lastValue = values[values.length - 1];
247
- const valueText = showLastValue
248
- ? `<text x="${width - 2}" y="${height / 2 + 4}" text-anchor="end" font-size="10" fill="#333">${lastValue.toFixed(decimals)}${unit}</text>`
249
- : '';
250
- // Trend indicator (comparing last vs first)
251
- const trend = values.length > 1 ? values[values.length - 1] - values[0] : 0;
252
- const trendColor = trend >= 0 ? '#198754' : '#dc3545';
253
- return `
254
- <svg width="${width}" height="${height}" style="vertical-align: middle;">
255
- <path d="${fillPath}" fill="${fillColor}" />
256
- <path d="${linePath}" fill="none" stroke="${lineColor}" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
257
- ${dots}
258
- ${valueText}
259
- </svg>
260
- `.trim().replace(/\s+/g, ' ');
261
- };
262
- }
263
- /**
264
- * Custom sorter for fband - extracts numeric portion and sorts numerically
265
- * Examples: LTE700 → 700, GSM900 → 900, LTE1800 → 1800, 5G-3500 → 3500
266
- */
267
- export function fbandSorter(a, b) {
268
- const numA = parseInt(a.replace(/\D/g, ''), 10) || 0;
269
- const numB = parseInt(b.replace(/\D/g, ''), 10) || 0;
270
- return numA - numB;
271
- }
272
- /**
273
- * Custom sorter for cellName - sorts by the 5th character (sector digit)
274
- * Example: 10001 → '1', 10002 → '2', 10003 → '3'
275
- */
276
- export function cellNameSectorSorter(a, b) {
277
- const charA = a.charAt(4) || '0';
278
- const charB = b.charAt(4) || '0';
279
- return charA.localeCompare(charB);
280
- }
281
- /**
282
- * Combined multi-level sorter for cell data
283
- * Sort order: tech (asc) → fband by numeric value (asc) → cellName by 5th digit (asc)
284
- */
285
- export function cellDataSorter(a, b) {
286
- // 1. Sort by tech (string comparison)
287
- const techA = String(a.tech || '');
288
- const techB = String(b.tech || '');
289
- const techCompare = techA.localeCompare(techB);
290
- if (techCompare !== 0)
291
- return techCompare;
292
- // 2. Sort by fband (numeric extraction)
293
- const fbandA = String(a.fband || '');
294
- const fbandB = String(b.fband || '');
295
- const fbandCompare = fbandSorter(fbandA, fbandB);
296
- if (fbandCompare !== 0)
297
- return fbandCompare;
298
- // 3. Sort by cellName 5th character (sector)
299
- const cellNameA = String(a.cellName || '');
300
- const cellNameB = String(b.cellName || '');
301
- return cellNameSectorSorter(cellNameA, cellNameB);
302
- }
303
- /**
304
- * Get all column definitions
305
- */
306
- export function getAllColumns(techColors = DEFAULT_TECH_COLORS, statusColors = DEFAULT_STATUS_COLORS, headerFilters = true) {
307
- const headerFilterParams = headerFilters ? { headerFilter: 'input' } : {};
308
- const selectHeaderFilter = headerFilters ? { headerFilter: 'list', headerFilterParams: { valuesLookup: true } } : {};
309
- return [
310
- // Core columns - siteId, txId, cellName first (frozen)
311
- {
312
- title: 'Site ID',
313
- field: 'siteId',
314
- width: 120,
315
- frozen: true,
316
- ...headerFilterParams,
317
- },
318
- {
319
- title: 'TX ID',
320
- field: 'txId',
321
- width: 100,
322
- frozen: true,
323
- ...headerFilterParams,
324
- },
325
- {
326
- title: 'Cell Name',
327
- field: 'cellName',
328
- width: 150,
329
- frozen: true,
330
- ...headerFilterParams,
331
- },
332
- {
333
- title: 'Tech',
334
- field: 'tech',
335
- width: 80,
336
- hozAlign: 'center',
337
- formatter: createTechFormatter(techColors),
338
- ...selectHeaderFilter,
339
- },
340
- {
341
- title: 'Band',
342
- field: 'fband',
343
- width: 100,
344
- hozAlign: 'center',
345
- formatter: createFbandFormatter(),
346
- ...selectHeaderFilter,
347
- },
348
- {
349
- title: 'Freq',
350
- field: 'frq',
351
- width: 80,
352
- hozAlign: 'center',
353
- ...selectHeaderFilter,
354
- },
355
- {
356
- title: 'Status',
357
- field: 'status',
358
- width: 180,
359
- formatter: createStatusFormatter(statusColors),
360
- ...selectHeaderFilter,
361
- },
362
- {
363
- title: 'On Air Date',
364
- field: 'onAirDate',
365
- width: 120,
366
- ...headerFilterParams,
367
- },
368
- {
369
- title: 'Config Date',
370
- field: 'configDate',
371
- width: 120,
372
- formatter: historyDateFormatter,
373
- ...headerFilterParams,
374
- },
375
- {
376
- title: 'Freq (MHz)',
377
- field: 'frequency',
378
- width: 100,
379
- hozAlign: 'right',
380
- ...headerFilterParams,
381
- },
382
- // History change columns (current vs previous, highlights changes)
383
- {
384
- title: 'Antenna',
385
- field: 'antennaChange',
386
- width: 160,
387
- formatter: createHistoryChangeFormatter('antenna', 'prevAntenna'),
388
- ...headerFilterParams,
389
- },
390
- {
391
- title: 'E-Tilt',
392
- field: 'etiltChange',
393
- width: 80,
394
- hozAlign: 'center',
395
- formatter: createHistoryChangeFormatter('electricalTilt', 'prevElectricalTilt'),
396
- },
397
- {
398
- title: 'M-Tilt',
399
- field: 'mtiltChange',
400
- width: 80,
401
- hozAlign: 'center',
402
- formatter: createHistoryChangeFormatter('mechanicalTilt', 'prevMechanicalTilt'),
403
- },
404
- {
405
- title: 'Power',
406
- field: 'powerChange',
407
- width: 80,
408
- hozAlign: 'center',
409
- formatter: createHistoryChangeFormatter('power', 'prevPower'),
410
- },
411
- // Physical columns
412
- {
413
- title: 'Antenna',
414
- field: 'antenna',
415
- width: 150,
416
- ...headerFilterParams,
417
- },
418
- {
419
- title: 'Azimuth',
420
- field: 'azimuth',
421
- width: 90,
422
- hozAlign: 'right',
423
- formatter: azimuthFormatter,
424
- ...headerFilterParams,
425
- },
426
- {
427
- title: 'Height',
428
- field: 'height',
429
- width: 80,
430
- hozAlign: 'right',
431
- formatter: heightFormatter,
432
- ...headerFilterParams,
433
- },
434
- {
435
- title: 'Beamwidth',
436
- field: 'beamwidth',
437
- width: 100,
438
- hozAlign: 'right',
439
- formatter: azimuthFormatter,
440
- ...headerFilterParams,
441
- },
442
- // Network columns
443
- {
444
- title: 'DL EARFCN',
445
- field: 'dlEarfn',
446
- width: 100,
447
- hozAlign: 'right',
448
- ...headerFilterParams,
449
- },
450
- {
451
- title: 'BCCH',
452
- field: 'bcch',
453
- width: 80,
454
- hozAlign: 'right',
455
- ...headerFilterParams,
456
- },
457
- {
458
- title: 'PCI',
459
- field: 'pci',
460
- width: 80,
461
- hozAlign: 'right',
462
- ...headerFilterParams,
463
- },
464
- {
465
- title: 'Cell ID',
466
- field: 'cellID',
467
- width: 100,
468
- ...headerFilterParams,
469
- },
470
- {
471
- title: 'Cell ID 2G',
472
- field: 'cellID2G',
473
- width: 100,
474
- ...headerFilterParams,
475
- },
476
- {
477
- title: 'Ctrl ID',
478
- field: 'ctrlid',
479
- width: 100,
480
- ...headerFilterParams,
481
- },
482
- {
483
- title: 'RRU',
484
- field: 'rru',
485
- width: 100,
486
- ...headerFilterParams,
487
- },
488
- {
489
- title: 'NW ET',
490
- field: 'nwET',
491
- width: 80,
492
- hozAlign: 'right',
493
- formatter: numberFormatter(1),
494
- ...headerFilterParams,
495
- },
496
- {
497
- title: 'NW PW',
498
- field: 'nwPW',
499
- width: 80,
500
- hozAlign: 'right',
501
- formatter: numberFormatter(1),
502
- ...headerFilterParams,
503
- },
504
- {
505
- title: 'NW RS',
506
- field: 'nwRS',
507
- width: 80,
508
- hozAlign: 'right',
509
- ...headerFilterParams,
510
- },
511
- {
512
- title: 'NW BW',
513
- field: 'nwBW',
514
- width: 80,
515
- hozAlign: 'right',
516
- ...headerFilterParams,
517
- },
518
- // Atoll columns
519
- {
520
- title: 'Atoll ET',
521
- field: 'atollET',
522
- width: 90,
523
- hozAlign: 'right',
524
- formatter: numberFormatter(1),
525
- ...headerFilterParams,
526
- },
527
- {
528
- title: 'Atoll MT',
529
- field: 'atollMT',
530
- width: 90,
531
- hozAlign: 'right',
532
- formatter: numberFormatter(1),
533
- ...headerFilterParams,
534
- },
535
- {
536
- title: 'Atoll PW',
537
- field: 'atollPW',
538
- width: 90,
539
- hozAlign: 'right',
540
- formatter: numberFormatter(1),
541
- ...headerFilterParams,
542
- },
543
- {
544
- title: 'Atoll RS',
545
- field: 'atollRS',
546
- width: 90,
547
- hozAlign: 'right',
548
- formatter: numberFormatter(1),
549
- ...headerFilterParams,
550
- },
551
- {
552
- title: 'Atoll BW',
553
- field: 'atollBW',
554
- width: 90,
555
- hozAlign: 'right',
556
- formatter: numberFormatter(1),
557
- ...headerFilterParams,
558
- },
559
- // Compare columns (Atoll vs Network)
560
- {
561
- title: 'Δ ET',
562
- field: 'compareET',
563
- width: 110,
564
- hozAlign: 'center',
565
- formatter: createCompareFormatter('atollET', 'nwET'),
566
- headerTooltip: 'Atoll ET | Network ET',
567
- },
568
- {
569
- title: 'Δ PW',
570
- field: 'comparePW',
571
- width: 110,
572
- hozAlign: 'center',
573
- formatter: createCompareFormatter('atollPW', 'nwPW'),
574
- headerTooltip: 'Atoll PW | Network PW',
575
- },
576
- {
577
- title: 'Δ RS',
578
- field: 'compareRS',
579
- width: 110,
580
- hozAlign: 'center',
581
- formatter: createCompareFormatter('atollRS', 'nwRS'),
582
- headerTooltip: 'Atoll RS | Network RS',
583
- },
584
- {
585
- title: 'Δ BW',
586
- field: 'compareBW',
587
- width: 110,
588
- hozAlign: 'center',
589
- formatter: createCompareFormatter('atollBW', 'nwBW'),
590
- headerTooltip: 'Atoll BW | Network BW',
591
- },
592
- // KPI Sparkline columns
593
- {
594
- title: 'Traffic',
595
- field: 'kpiTraffic',
596
- width: 120,
597
- hozAlign: 'center',
598
- formatter: createSparklineFormatter({
599
- unit: '',
600
- lineColor: '#0d6efd',
601
- fillColor: 'rgba(13,110,253,0.15)',
602
- decimals: 0
603
- }),
604
- headerTooltip: 'Traffic volume trend (GB)',
605
- headerSort: false,
606
- },
607
- {
608
- title: 'Throughput',
609
- field: 'kpiThroughput',
610
- width: 120,
611
- hozAlign: 'center',
612
- formatter: createSparklineFormatter({
613
- unit: '',
614
- lineColor: '#6f42c1',
615
- fillColor: 'rgba(111,66,193,0.15)',
616
- decimals: 1
617
- }),
618
- headerTooltip: 'Throughput trend (Mbps)',
619
- headerSort: false,
620
- },
621
- {
622
- title: 'Avail %',
623
- field: 'kpiAvailability',
624
- width: 120,
625
- hozAlign: 'center',
626
- formatter: createSparklineFormatter({
627
- unit: '%',
628
- lineColor: '#198754',
629
- fillColor: 'rgba(25,135,84,0.15)',
630
- decimals: 1
631
- }),
632
- headerTooltip: 'Availability trend (%)',
633
- headerSort: false,
634
- },
635
- {
636
- title: 'Success %',
637
- field: 'kpiSuccessRate',
638
- width: 120,
639
- hozAlign: 'center',
640
- formatter: createSparklineFormatter({
641
- unit: '%',
642
- lineColor: '#fd7e14',
643
- fillColor: 'rgba(253,126,20,0.15)',
644
- decimals: 1
645
- }),
646
- headerTooltip: 'Success rate trend (%)',
647
- headerSort: false,
648
- },
649
- // Position columns
650
- {
651
- title: 'Latitude',
652
- field: 'latitude',
653
- width: 120,
654
- hozAlign: 'right',
655
- formatter: coordinateFormatter,
656
- ...headerFilterParams,
657
- },
658
- {
659
- title: 'Longitude',
660
- field: 'longitude',
661
- width: 120,
662
- hozAlign: 'right',
663
- formatter: coordinateFormatter,
664
- ...headerFilterParams,
665
- },
666
- {
667
- title: 'Site Lat',
668
- field: 'siteLatitude',
669
- width: 120,
670
- hozAlign: 'right',
671
- formatter: coordinateFormatter,
672
- ...headerFilterParams,
673
- },
674
- {
675
- title: 'Site Lng',
676
- field: 'siteLongitude',
677
- width: 120,
678
- hozAlign: 'right',
679
- formatter: coordinateFormatter,
680
- ...headerFilterParams,
681
- },
682
- {
683
- title: 'DX',
684
- field: 'dx',
685
- width: 80,
686
- hozAlign: 'right',
687
- formatter: numberFormatter(2),
688
- ...headerFilterParams,
689
- },
690
- {
691
- title: 'DY',
692
- field: 'dy',
693
- width: 80,
694
- hozAlign: 'right',
695
- formatter: numberFormatter(2),
696
- ...headerFilterParams,
697
- },
698
- // Planning columns
699
- {
700
- title: 'Planner',
701
- field: 'planner',
702
- width: 120,
703
- ...headerFilterParams,
704
- },
705
- {
706
- title: 'Comment',
707
- field: 'comment',
708
- width: 200,
709
- ...headerFilterParams,
710
- },
711
- {
712
- title: 'Subgroup',
713
- field: 'customSubgroup',
714
- width: 120,
715
- ...headerFilterParams,
716
- },
717
- ];
718
- }
719
- /**
720
- * Get columns for a specific preset
721
- */
722
- export function getColumnsForPreset(preset, techColors = DEFAULT_TECH_COLORS, statusColors = DEFAULT_STATUS_COLORS, headerFilters = true) {
723
- const allColumns = getAllColumns(techColors, statusColors, headerFilters);
724
- let visibleFields;
725
- switch (preset) {
726
- case 'compact':
727
- visibleFields = ['siteId', 'txId', 'cellName', 'tech', 'fband', 'status'];
728
- break;
729
- case 'full':
730
- return allColumns;
731
- case 'physical':
732
- visibleFields = [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.physical];
733
- break;
734
- case 'network':
735
- visibleFields = [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.network];
736
- break;
737
- case 'planning':
738
- visibleFields = [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.planning];
739
- break;
740
- case 'compare':
741
- visibleFields = [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.compare];
742
- break;
743
- case 'kpi':
744
- visibleFields = [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.kpi];
745
- break;
746
- case 'history':
747
- // Simplified view for config history - just cellName, date, and config fields
748
- visibleFields = ['cellName', ...COLUMN_GROUPS.history];
749
- break;
750
- case 'default':
751
- default:
752
- visibleFields = [
753
- 'siteId', 'txId', 'cellName', 'tech', 'fband', 'frq', 'status',
754
- 'azimuth', 'height', 'antenna'
755
- ];
756
- }
757
- return allColumns.filter(col => visibleFields.includes(col.field));
758
- }
759
- /**
760
- * Get group header formatter for a specific field
761
- */
762
- export function getGroupHeaderFormatter(groupField) {
763
- return (value, count) => {
764
- const displayValue = String(value).replace(/_/g, ' ');
765
- let color = '#6c757d';
766
- if (groupField === 'tech') {
767
- color = DEFAULT_TECH_COLORS[String(value)] || color;
768
- }
769
- else if (groupField === 'fband') {
770
- color = FBAND_COLORS[String(value)] || color;
771
- }
772
- else if (groupField === 'status') {
773
- color = DEFAULT_STATUS_COLORS[String(value)] || color;
774
- }
775
- return `<span class="badge me-2" style="background-color: ${color}; color: white;">${displayValue}</span>
776
- <span class="text-muted">(${count} cell${count !== 1 ? 's' : ''})</span>`;
777
- };
778
- }
779
- /**
780
- * Get column metadata for the column picker UI
781
- */
782
- export function getColumnMetadata() {
783
- return [
784
- // Core
785
- { field: 'siteId', title: 'Site ID', group: 'Core' },
786
- { field: 'txId', title: 'TX ID', group: 'Core' },
787
- { field: 'cellName', title: 'Cell Name', group: 'Core' },
788
- { field: 'tech', title: 'Technology', group: 'Core' },
789
- { field: 'fband', title: 'Band', group: 'Core' },
790
- { field: 'frq', title: 'Frequency', group: 'Core' },
791
- { field: 'status', title: 'Status', group: 'Core' },
792
- { field: 'onAirDate', title: 'On Air Date', group: 'Core' },
793
- // Antenna Physical
794
- { field: 'antenna', title: 'Antenna', group: 'Antenna Physical' },
795
- { field: 'azimuth', title: 'Azimuth', group: 'Antenna Physical' },
796
- { field: 'height', title: 'Height', group: 'Antenna Physical' },
797
- { field: 'beamwidth', title: 'Beamwidth', group: 'Antenna Physical' },
798
- { field: 'atollMT', title: 'Atoll MT', group: 'Atoll' },
799
- // Atoll antenna settings
800
- { field: 'atollET', title: 'Atoll ET', group: 'Atoll' },
801
- { field: 'atollPW', title: 'Atoll PW', group: 'Atoll' },
802
- { field: 'atollRS', title: 'Atoll RS', group: 'Atoll' },
803
- { field: 'atollBW', title: 'Atoll BW', group: 'Atoll' },
804
- // Network antena settings
805
- { field: 'nwET', title: 'NW ET', group: 'Network' },
806
- { field: 'nwPW', title: 'NW PW', group: 'Network' },
807
- { field: 'nwRS', title: 'NW RS', group: 'Network' },
808
- { field: 'nwBW', title: 'NW BW', group: 'Network' },
809
- // Compare (Atoll vs Network)
810
- { field: 'compareET', title: 'Δ ET', group: 'Compare' },
811
- { field: 'comparePW', title: 'Δ PW', group: 'Compare' },
812
- { field: 'compareRS', title: 'Δ RS', group: 'Compare' },
813
- { field: 'compareBW', title: 'Δ BW', group: 'Compare' },
814
- // Network settings
815
- { field: 'dlEarfn', title: 'DL EARFCN', group: 'Network' },
816
- { field: 'bcch', title: 'BCCH', group: 'Network' },
817
- { field: 'pci', title: 'PCI', group: 'Network' },
818
- { field: 'rru', title: 'RRU', group: 'Network' },
819
- { field: 'cellID', title: 'Cell ID', group: 'Network' },
820
- { field: 'cellId2G', title: 'Cell ID 2G', group: 'Network' },
821
- { field: 'ctrlid', title: 'Ctrl ID', group: 'Network' },
822
- // History
823
- { field: 'configDate', title: 'Config Date', group: 'History' },
824
- { field: 'frequency', title: 'Freq (MHz)', group: 'History' },
825
- { field: 'antennaChange', title: 'Antenna (Δ)', group: 'History' },
826
- { field: 'etiltChange', title: 'E-Tilt (Δ)', group: 'History' },
827
- { field: 'mtiltChange', title: 'M-Tilt (Δ)', group: 'History' },
828
- { field: 'powerChange', title: 'Power (Δ)', group: 'History' },
829
- // KPI Trends
830
- { field: 'kpiTraffic', title: 'Traffic', group: 'KPI' },
831
- { field: 'kpiThroughput', title: 'Throughput', group: 'KPI' },
832
- { field: 'kpiAvailability', title: 'Availability', group: 'KPI' },
833
- { field: 'kpiSuccessRate', title: 'Success Rate', group: 'KPI' },
834
- // Position
835
- // { field: 'latitude', title: 'Latitude', group: 'Position' },
836
- // { field: 'longitude', title: 'Longitude', group: 'Position' },
837
- // { field: 'siteLatitude', title: 'Site Latitude', group: 'Position' },
838
- // { field: 'siteLongitude', title: 'Site Longitude', group: 'Position' },
839
- // { field: 'dx', title: 'DX', group: 'Position' },
840
- // { field: 'dy', title: 'DY', group: 'Position' },
841
- // Planning
842
- { field: 'planner', title: 'Planner', group: 'Planning' },
843
- { field: 'comment', title: 'Comment', group: 'Planning' },
844
- // { field: 'customSubgroup', title: 'Subgroup', group: 'Planning' },
845
- ];
846
- }
847
- /**
848
- * Get default visible columns for a preset
849
- */
850
- export function getPresetVisibleFields(preset) {
851
- switch (preset) {
852
- case 'compact':
853
- return ['siteId', 'txId', 'cellName', 'tech', 'fband', 'status'];
854
- case 'full':
855
- return getColumnMetadata().map(c => c.field);
856
- case 'physical':
857
- return [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.physical];
858
- case 'network':
859
- return [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.network];
860
- case 'planning':
861
- return [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.planning];
862
- case 'compare':
863
- return [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.compare];
864
- case 'kpi':
865
- return [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.kpi];
866
- case 'history':
867
- return ['cellName', ...COLUMN_GROUPS.history];
868
- case 'default':
869
- default:
870
- return ['siteId', 'txId', 'cellName', 'tech', 'fband', 'frq', 'status', 'azimuth', 'height', 'antenna'];
871
- }
872
- }