@smartnet360/svelte-components 0.0.130 → 0.0.132
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/core/CellTable/CellHistoryDemo.svelte +106 -65
- package/dist/core/CellTable/column-config.d.ts +12 -0
- package/dist/core/CellTable/column-config.js +79 -2
- package/dist/core/CellTable/history-api-helper.d.ts +79 -0
- package/dist/core/CellTable/history-api-helper.js +83 -0
- package/dist/core/CellTable/index.d.ts +3 -2
- package/dist/core/CellTable/index.js +3 -1
- package/dist/core/CellTable/types.d.ts +26 -0
- package/dist/map-v3/demo/DemoMap.svelte +1 -6
- package/dist/map-v3/features/{cells/custom → custom}/components/CustomCellFilterControl.svelte +11 -4
- package/dist/map-v3/features/custom/components/CustomCellSetManager.svelte +692 -0
- package/dist/map-v3/features/{cells/custom → custom}/index.d.ts +1 -1
- package/dist/map-v3/features/{cells/custom → custom}/index.js +1 -1
- package/dist/map-v3/features/custom/layers/CustomCellsLayer.svelte +399 -0
- package/dist/map-v3/features/{cells/custom → custom}/logic/csv-parser.d.ts +11 -7
- package/dist/map-v3/features/{cells/custom → custom}/logic/csv-parser.js +64 -20
- package/dist/map-v3/features/{cells/custom → custom}/logic/tree-adapter.d.ts +1 -1
- package/dist/map-v3/features/{cells/custom → custom}/stores/custom-cell-sets.svelte.d.ts +4 -3
- package/dist/map-v3/features/{cells/custom → custom}/stores/custom-cell-sets.svelte.js +30 -10
- package/dist/map-v3/features/{cells/custom → custom}/types.d.ts +32 -12
- package/dist/map-v3/features/{cells/custom → custom}/types.js +5 -3
- package/dist/map-v3/index.d.ts +1 -1
- package/dist/map-v3/index.js +1 -1
- package/dist/map-v3/shared/controls/MapControl.svelte +43 -15
- package/dist/map-v3/shared/controls/MapControl.svelte.d.ts +3 -1
- package/package.json +1 -1
- package/dist/map-v3/features/cells/custom/components/CustomCellSetManager.svelte +0 -306
- package/dist/map-v3/features/cells/custom/layers/CustomCellsLayer.svelte +0 -262
- /package/dist/map-v3/features/{cells/custom → custom}/components/CustomCellFilterControl.svelte.d.ts +0 -0
- /package/dist/map-v3/features/{cells/custom → custom}/components/CustomCellSetManager.svelte.d.ts +0 -0
- /package/dist/map-v3/features/{cells/custom → custom}/components/index.d.ts +0 -0
- /package/dist/map-v3/features/{cells/custom → custom}/components/index.js +0 -0
- /package/dist/map-v3/features/{cells/custom → custom}/layers/CustomCellsLayer.svelte.d.ts +0 -0
- /package/dist/map-v3/features/{cells/custom → custom}/layers/index.d.ts +0 -0
- /package/dist/map-v3/features/{cells/custom → custom}/layers/index.js +0 -0
- /package/dist/map-v3/features/{cells/custom → custom}/logic/index.d.ts +0 -0
- /package/dist/map-v3/features/{cells/custom → custom}/logic/index.js +0 -0
- /package/dist/map-v3/features/{cells/custom → custom}/logic/tree-adapter.js +0 -0
- /package/dist/map-v3/features/{cells/custom → custom}/stores/index.d.ts +0 -0
- /package/dist/map-v3/features/{cells/custom → custom}/stores/index.js +0 -0
|
@@ -1,68 +1,109 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import CellTablePanel from './CellTablePanel.svelte';
|
|
3
|
-
import type { RowSelectionEvent,
|
|
3
|
+
import type { RowSelectionEvent, CellHistoryData } from './types';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
if (Math.random() > 0.6) {
|
|
29
|
-
currentET = Math.floor(Math.random() * 10);
|
|
30
|
-
}
|
|
31
|
-
if (Math.random() > 0.8) {
|
|
32
|
-
currentMT = Math.floor(Math.random() * 6);
|
|
33
|
-
}
|
|
34
|
-
if (Math.random() > 0.7) {
|
|
35
|
-
currentPW = 15 + Math.floor(Math.random() * 15);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const date = new Date(baseDate);
|
|
39
|
-
date.setDate(date.getDate() - i * 7); // Weekly snapshots going back
|
|
40
|
-
|
|
41
|
-
history.push({
|
|
42
|
-
id: `hist-${i}`,
|
|
43
|
-
cellName: cellName,
|
|
44
|
-
configDate: date.toLocaleDateString('en-GB', {
|
|
45
|
-
day: '2-digit',
|
|
46
|
-
month: '2-digit',
|
|
47
|
-
year: 'numeric',
|
|
48
|
-
hour: '2-digit',
|
|
49
|
-
minute: '2-digit'
|
|
50
|
-
}),
|
|
51
|
-
antenna: currentAntenna,
|
|
52
|
-
atollET: currentET,
|
|
53
|
-
atollMT: currentMT,
|
|
54
|
-
atollPW: currentPW,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return history;
|
|
5
|
+
/**
|
|
6
|
+
* Table-ready history record after transformation
|
|
7
|
+
*/
|
|
8
|
+
interface HistoryTableRow {
|
|
9
|
+
id: string;
|
|
10
|
+
cellName: string;
|
|
11
|
+
configDate: string;
|
|
12
|
+
// Current values
|
|
13
|
+
antenna: string;
|
|
14
|
+
electricalTilt: number;
|
|
15
|
+
mechanicalTilt: number;
|
|
16
|
+
power: number;
|
|
17
|
+
// Previous values for change comparison
|
|
18
|
+
prevAntenna: string;
|
|
19
|
+
prevElectricalTilt: number;
|
|
20
|
+
prevMechanicalTilt: number;
|
|
21
|
+
prevPower: number;
|
|
22
|
+
// Virtual fields for the change columns
|
|
23
|
+
antennaChange: string;
|
|
24
|
+
etiltChange: number;
|
|
25
|
+
mtiltChange: number;
|
|
26
|
+
powerChange: number;
|
|
59
27
|
}
|
|
60
28
|
|
|
61
|
-
|
|
62
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Transform API history data to table-friendly format
|
|
31
|
+
* Maps UPPER_CASE API fields to camelCase and adds virtual fields for change detection
|
|
32
|
+
*/
|
|
33
|
+
function transformHistoryData(apiData: CellHistoryData[]): HistoryTableRow[] {
|
|
34
|
+
return apiData.map((record, index) => ({
|
|
35
|
+
id: `hist-${index}`,
|
|
36
|
+
cellName: record.CELLNAME,
|
|
37
|
+
configDate: record.CONFIG_DATE,
|
|
38
|
+
// Current values
|
|
39
|
+
antenna: record.ANTENNA_TYPE,
|
|
40
|
+
electricalTilt: record.ELECTRONIC_TILT,
|
|
41
|
+
mechanicalTilt: record.MECHANIC_TILT,
|
|
42
|
+
power: record.ANTOUTPUTPWR,
|
|
43
|
+
// Previous values for change comparison
|
|
44
|
+
prevAntenna: record.PREV_ANTENNA_TYPE,
|
|
45
|
+
prevElectricalTilt: record.PREV_ELECTRONIC_TILT,
|
|
46
|
+
prevMechanicalTilt: record.PREV_MECHANIC_TILT,
|
|
47
|
+
prevPower: record.PREV_ANTOUTPUTPWR,
|
|
48
|
+
// Virtual fields for the change columns (needed for column filtering)
|
|
49
|
+
antennaChange: record.ANTENNA_TYPE,
|
|
50
|
+
etiltChange: record.ELECTRONIC_TILT,
|
|
51
|
+
mtiltChange: record.MECHANIC_TILT,
|
|
52
|
+
powerChange: record.ANTOUTPUTPWR,
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Example API data for cell configuration history
|
|
58
|
+
*/
|
|
59
|
+
const EXAMPLE_HISTORY_DATA: CellHistoryData[] = [
|
|
60
|
+
{
|
|
61
|
+
CELLNAME: '1001141',
|
|
62
|
+
CONFIG_DATE: '2025-12-03T23:00:00.000Z',
|
|
63
|
+
ANTENNA_TYPE: 'AQU4521R01v06',
|
|
64
|
+
PREV_ANTENNA_TYPE: 'AQU4521R01v06',
|
|
65
|
+
ANTOUTPUTPWR: 20,
|
|
66
|
+
PREV_ANTOUTPUTPWR: 20,
|
|
67
|
+
MECHANIC_TILT: 1,
|
|
68
|
+
PREV_MECHANIC_TILT: 1,
|
|
69
|
+
ELECTRONIC_TILT: 10,
|
|
70
|
+
PREV_ELECTRONIC_TILT: 9
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
CELLNAME: '1001141',
|
|
74
|
+
CONFIG_DATE: '2025-09-11T22:00:00.000Z',
|
|
75
|
+
ANTENNA_TYPE: 'AQU4521R01v06',
|
|
76
|
+
PREV_ANTENNA_TYPE: 'ADU451602v01',
|
|
77
|
+
ANTOUTPUTPWR: 20,
|
|
78
|
+
PREV_ANTOUTPUTPWR: 20,
|
|
79
|
+
MECHANIC_TILT: 1,
|
|
80
|
+
PREV_MECHANIC_TILT: 0,
|
|
81
|
+
ELECTRONIC_TILT: 9,
|
|
82
|
+
PREV_ELECTRONIC_TILT: 10
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
CELLNAME: '1001141',
|
|
86
|
+
CONFIG_DATE: '2021-02-15T23:00:00.000Z',
|
|
87
|
+
ANTENNA_TYPE: 'ADU451602v01',
|
|
88
|
+
PREV_ANTENNA_TYPE: 'ADU451602v01',
|
|
89
|
+
ANTOUTPUTPWR: 20,
|
|
90
|
+
PREV_ANTOUTPUTPWR: 20,
|
|
91
|
+
MECHANIC_TILT: 0,
|
|
92
|
+
PREV_MECHANIC_TILT: 2,
|
|
93
|
+
ELECTRONIC_TILT: 10,
|
|
94
|
+
PREV_ELECTRONIC_TILT: 10
|
|
95
|
+
}
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
let cellName = $state('1001141');
|
|
99
|
+
let rawHistoryData = $state<CellHistoryData[]>(EXAMPLE_HISTORY_DATA);
|
|
100
|
+
let historyData = $derived(transformHistoryData(rawHistoryData));
|
|
63
101
|
|
|
64
102
|
function loadHistory() {
|
|
65
|
-
|
|
103
|
+
// TODO: Replace with actual API call
|
|
104
|
+
// For now, just reload the example data
|
|
105
|
+
console.log(`Loading history for cell: ${cellName}`);
|
|
106
|
+
rawHistoryData = EXAMPLE_HISTORY_DATA.filter(r => r.CELLNAME === cellName);
|
|
66
107
|
}
|
|
67
108
|
|
|
68
109
|
function handleSelectionChange(event: RowSelectionEvent) {
|
|
@@ -72,7 +113,7 @@
|
|
|
72
113
|
/**
|
|
73
114
|
* Compare two selected history records
|
|
74
115
|
*/
|
|
75
|
-
function handleCompareTwoRecords(record1:
|
|
116
|
+
function handleCompareTwoRecords(record1: HistoryTableRow, record2: HistoryTableRow) {
|
|
76
117
|
const params = new URLSearchParams();
|
|
77
118
|
|
|
78
119
|
// Use antenna from both records
|
|
@@ -80,12 +121,12 @@
|
|
|
80
121
|
if (record2.antenna) params.set('ant2', record2.antenna);
|
|
81
122
|
|
|
82
123
|
// Use electrical tilt values
|
|
83
|
-
params.set('etilt1', String(record1.
|
|
84
|
-
params.set('etilt2', String(record2.
|
|
124
|
+
params.set('etilt1', String(record1.electricalTilt || 0));
|
|
125
|
+
params.set('etilt2', String(record2.electricalTilt || 0));
|
|
85
126
|
|
|
86
127
|
// Use mechanical tilt values
|
|
87
|
-
params.set('mtilt1', String(record1.
|
|
88
|
-
params.set('mtilt2', String(record2.
|
|
128
|
+
params.set('mtilt1', String(record1.mechanicalTilt || 0));
|
|
129
|
+
params.set('mtilt2', String(record2.mechanicalTilt || 0));
|
|
89
130
|
|
|
90
131
|
// Add config date as labels
|
|
91
132
|
params.set('label1', `${record1.cellName} (${record1.configDate})`);
|
|
@@ -122,7 +163,7 @@
|
|
|
122
163
|
<!-- CellTablePanel with history preset -->
|
|
123
164
|
<div class="flex-grow-1 overflow-hidden">
|
|
124
165
|
<CellTablePanel
|
|
125
|
-
cells={historyData as CellData[]}
|
|
166
|
+
cells={historyData as unknown as import('./types').CellData[]}
|
|
126
167
|
groupBy="none"
|
|
127
168
|
columnPreset="history"
|
|
128
169
|
selectable={true}
|
|
@@ -150,7 +191,7 @@
|
|
|
150
191
|
class="btn btn-sm btn-outline-success"
|
|
151
192
|
disabled={selectedCount !== 2}
|
|
152
193
|
title={selectedCount === 2 ? 'Compare antenna patterns for selected dates' : 'Select exactly 2 records to compare'}
|
|
153
|
-
onclick={() => handleCompareTwoRecords(selectedRows[0], selectedRows[1])}
|
|
194
|
+
onclick={() => handleCompareTwoRecords(selectedRows[0] as unknown as HistoryTableRow, selectedRows[1] as unknown as HistoryTableRow)}
|
|
154
195
|
>
|
|
155
196
|
<i class="bi bi-broadcast-pin"></i>
|
|
156
197
|
<span class="d-none d-sm-inline ms-1">Compare Antennas</span>
|
|
@@ -61,6 +61,18 @@ export declare function heightFormatter(cell: CellComponent): string;
|
|
|
61
61
|
* - Gray: one or both values missing
|
|
62
62
|
*/
|
|
63
63
|
export declare function createCompareFormatter(atollField: string, nwtField: string): (cell: CellComponent) => string;
|
|
64
|
+
/**
|
|
65
|
+
* Create a history change formatter for showing current vs previous values
|
|
66
|
+
* Highlights changes in yellow/amber, unchanged values in muted style
|
|
67
|
+
*
|
|
68
|
+
* @param currentField - Field name for current value
|
|
69
|
+
* @param prevField - Field name for previous value
|
|
70
|
+
*/
|
|
71
|
+
export declare function createHistoryChangeFormatter(currentField: string, prevField: string): (cell: CellComponent) => string;
|
|
72
|
+
/**
|
|
73
|
+
* Format ISO date string to YYYY.MM.DD format for history display
|
|
74
|
+
*/
|
|
75
|
+
export declare function historyDateFormatter(cell: CellComponent): string;
|
|
64
76
|
/**
|
|
65
77
|
* Creates a sparkline SVG formatter for KPI time-series data
|
|
66
78
|
* Renders a mini line chart showing trend over time
|
|
@@ -61,7 +61,7 @@ export const COLUMN_GROUPS = {
|
|
|
61
61
|
position: ['latitude', 'longitude', 'siteLatitude', 'siteLongitude', 'dx', 'dy'],
|
|
62
62
|
compare: ['compareET', 'comparePW', 'compareRS', 'compareBW'],
|
|
63
63
|
kpi: ['kpiTraffic', 'kpiThroughput', 'kpiAvailability', 'kpiSuccessRate'],
|
|
64
|
-
history: ['configDate', '
|
|
64
|
+
history: ['configDate', 'antennaChange', 'etiltChange', 'mtiltChange', 'powerChange'],
|
|
65
65
|
};
|
|
66
66
|
/**
|
|
67
67
|
* Create a technology badge formatter
|
|
@@ -168,6 +168,49 @@ export function createCompareFormatter(atollField, nwtField) {
|
|
|
168
168
|
return `<span class="badge" style="background-color: ${bgColor}; color: ${textColor}; font-size: 0.75rem; font-weight: normal;">${atollStr} | ${nwtStr}</span>`;
|
|
169
169
|
};
|
|
170
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
|
+
}
|
|
171
214
|
export function createSparklineFormatter(options = {}) {
|
|
172
215
|
const { width = 80, height = 24, lineColor = '#0d6efd', fillColor = 'rgba(13,110,253,0.2)', showDots = false, showLastValue = true, unit = '', decimals = 1 } = options;
|
|
173
216
|
return (cell) => {
|
|
@@ -331,9 +374,39 @@ export function getAllColumns(techColors = DEFAULT_TECH_COLORS, statusColors = D
|
|
|
331
374
|
{
|
|
332
375
|
title: 'Config Date',
|
|
333
376
|
field: 'configDate',
|
|
334
|
-
width:
|
|
377
|
+
width: 120,
|
|
378
|
+
formatter: historyDateFormatter,
|
|
335
379
|
...headerFilterParams,
|
|
336
380
|
},
|
|
381
|
+
// History change columns (current vs previous, highlights changes)
|
|
382
|
+
{
|
|
383
|
+
title: 'Antenna',
|
|
384
|
+
field: 'antennaChange',
|
|
385
|
+
width: 160,
|
|
386
|
+
formatter: createHistoryChangeFormatter('antenna', 'prevAntenna'),
|
|
387
|
+
...headerFilterParams,
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
title: 'E-Tilt',
|
|
391
|
+
field: 'etiltChange',
|
|
392
|
+
width: 80,
|
|
393
|
+
hozAlign: 'center',
|
|
394
|
+
formatter: createHistoryChangeFormatter('electricalTilt', 'prevElectricalTilt'),
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
title: 'M-Tilt',
|
|
398
|
+
field: 'mtiltChange',
|
|
399
|
+
width: 80,
|
|
400
|
+
hozAlign: 'center',
|
|
401
|
+
formatter: createHistoryChangeFormatter('mechanicalTilt', 'prevMechanicalTilt'),
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
title: 'Power',
|
|
405
|
+
field: 'powerChange',
|
|
406
|
+
width: 80,
|
|
407
|
+
hozAlign: 'center',
|
|
408
|
+
formatter: createHistoryChangeFormatter('power', 'prevPower'),
|
|
409
|
+
},
|
|
337
410
|
// Physical columns
|
|
338
411
|
{
|
|
339
412
|
title: 'Antenna',
|
|
@@ -748,6 +821,10 @@ export function getColumnMetadata() {
|
|
|
748
821
|
{ field: 'atollBW', title: 'Atoll BW', group: 'Atoll' },
|
|
749
822
|
// History
|
|
750
823
|
{ field: 'configDate', title: 'Config Date', group: 'History' },
|
|
824
|
+
{ field: 'antennaChange', title: 'Antenna (Δ)', group: 'History' },
|
|
825
|
+
{ field: 'etiltChange', title: 'E-Tilt (Δ)', group: 'History' },
|
|
826
|
+
{ field: 'mtiltChange', title: 'M-Tilt (Δ)', group: 'History' },
|
|
827
|
+
{ field: 'powerChange', title: 'Power (Δ)', group: 'History' },
|
|
751
828
|
// Compare (Atoll vs Network)
|
|
752
829
|
{ field: 'compareET', title: 'Δ ET', group: 'Compare' },
|
|
753
830
|
{ field: 'comparePW', title: 'Δ PW', group: 'Compare' },
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cell History API Helper
|
|
3
|
+
*
|
|
4
|
+
* Transform functions for converting database records to frontend-ready format.
|
|
5
|
+
* Use these in your API endpoint before sending the response.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Raw database record format (Oracle/SQL style - UPPER_CASE)
|
|
9
|
+
*/
|
|
10
|
+
export interface CellHistoryDbRecord {
|
|
11
|
+
CELLNAME: string;
|
|
12
|
+
CONFIG_DATE: string | Date;
|
|
13
|
+
ANTENNA_TYPE: string;
|
|
14
|
+
PREV_ANTENNA_TYPE: string;
|
|
15
|
+
ANTOUTPUTPWR: number;
|
|
16
|
+
PREV_ANTOUTPUTPWR: number;
|
|
17
|
+
MECHANIC_TILT: number;
|
|
18
|
+
PREV_MECHANIC_TILT: number;
|
|
19
|
+
ELECTRONIC_TILT: number;
|
|
20
|
+
PREV_ELECTRONIC_TILT: number;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Frontend-ready API response format (camelCase)
|
|
25
|
+
*/
|
|
26
|
+
export interface CellHistoryApiResponse {
|
|
27
|
+
cellName: string;
|
|
28
|
+
configDate: string;
|
|
29
|
+
antenna: string;
|
|
30
|
+
prevAntenna: string;
|
|
31
|
+
power: number;
|
|
32
|
+
prevPower: number;
|
|
33
|
+
mechanicalTilt: number;
|
|
34
|
+
prevMechanicalTilt: number;
|
|
35
|
+
electricalTilt: number;
|
|
36
|
+
prevElectricalTilt: number;
|
|
37
|
+
hasChanges: boolean;
|
|
38
|
+
changedFields: string[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Format a date to YYYY.MM.DD string
|
|
42
|
+
*/
|
|
43
|
+
export declare function formatConfigDate(date: string | Date): string;
|
|
44
|
+
/**
|
|
45
|
+
* Transform a single database record to API response format
|
|
46
|
+
*/
|
|
47
|
+
export declare function transformDbRecord(record: CellHistoryDbRecord): CellHistoryApiResponse;
|
|
48
|
+
/**
|
|
49
|
+
* Transform an array of database records to API response format
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* // In your API endpoint:
|
|
53
|
+
* const dbRecords = await db.query('SELECT * FROM CELL_CONFIG_HISTORY WHERE CELLNAME = ?', [cellName]);
|
|
54
|
+
* const response = transformDbRecords(dbRecords);
|
|
55
|
+
* return json(response);
|
|
56
|
+
*/
|
|
57
|
+
export declare function transformDbRecords(records: CellHistoryDbRecord[]): CellHistoryApiResponse[];
|
|
58
|
+
/**
|
|
59
|
+
* Example usage in a SvelteKit API endpoint:
|
|
60
|
+
*
|
|
61
|
+
* ```typescript
|
|
62
|
+
* // src/routes/api/cell-history/[cellName]/+server.ts
|
|
63
|
+
* import { json } from '@sveltejs/kit';
|
|
64
|
+
* import { transformDbRecords } from './history-api-helper';
|
|
65
|
+
* import { db } from '../../server/database';
|
|
66
|
+
*
|
|
67
|
+
* export async function GET({ params }) {
|
|
68
|
+
* const { cellName } = params;
|
|
69
|
+
*
|
|
70
|
+
* const dbRecords = await db.query(`
|
|
71
|
+
* SELECT * FROM CELL_CONFIG_HISTORY
|
|
72
|
+
* WHERE CELLNAME = :cellName
|
|
73
|
+
* ORDER BY CONFIG_DATE DESC
|
|
74
|
+
* `, { cellName });
|
|
75
|
+
*
|
|
76
|
+
* return json(transformDbRecords(dbRecords));
|
|
77
|
+
* }
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cell History API Helper
|
|
3
|
+
*
|
|
4
|
+
* Transform functions for converting database records to frontend-ready format.
|
|
5
|
+
* Use these in your API endpoint before sending the response.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Format a date to YYYY.MM.DD string
|
|
9
|
+
*/
|
|
10
|
+
export function formatConfigDate(date) {
|
|
11
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
12
|
+
const year = d.getFullYear();
|
|
13
|
+
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
14
|
+
const day = String(d.getDate()).padStart(2, '0');
|
|
15
|
+
return `${year}.${month}.${day}`;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Transform a single database record to API response format
|
|
19
|
+
*/
|
|
20
|
+
export function transformDbRecord(record) {
|
|
21
|
+
// Detect which fields changed
|
|
22
|
+
const changedFields = [];
|
|
23
|
+
if (record.ANTENNA_TYPE !== record.PREV_ANTENNA_TYPE) {
|
|
24
|
+
changedFields.push('antenna');
|
|
25
|
+
}
|
|
26
|
+
if (record.ANTOUTPUTPWR !== record.PREV_ANTOUTPUTPWR) {
|
|
27
|
+
changedFields.push('power');
|
|
28
|
+
}
|
|
29
|
+
if (record.MECHANIC_TILT !== record.PREV_MECHANIC_TILT) {
|
|
30
|
+
changedFields.push('mechanicalTilt');
|
|
31
|
+
}
|
|
32
|
+
if (record.ELECTRONIC_TILT !== record.PREV_ELECTRONIC_TILT) {
|
|
33
|
+
changedFields.push('electricalTilt');
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
cellName: record.CELLNAME,
|
|
37
|
+
configDate: formatConfigDate(record.CONFIG_DATE),
|
|
38
|
+
antenna: record.ANTENNA_TYPE,
|
|
39
|
+
prevAntenna: record.PREV_ANTENNA_TYPE,
|
|
40
|
+
power: record.ANTOUTPUTPWR,
|
|
41
|
+
prevPower: record.PREV_ANTOUTPUTPWR,
|
|
42
|
+
mechanicalTilt: record.MECHANIC_TILT,
|
|
43
|
+
prevMechanicalTilt: record.PREV_MECHANIC_TILT,
|
|
44
|
+
electricalTilt: record.ELECTRONIC_TILT,
|
|
45
|
+
prevElectricalTilt: record.PREV_ELECTRONIC_TILT,
|
|
46
|
+
hasChanges: changedFields.length > 0,
|
|
47
|
+
changedFields
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Transform an array of database records to API response format
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // In your API endpoint:
|
|
55
|
+
* const dbRecords = await db.query('SELECT * FROM CELL_CONFIG_HISTORY WHERE CELLNAME = ?', [cellName]);
|
|
56
|
+
* const response = transformDbRecords(dbRecords);
|
|
57
|
+
* return json(response);
|
|
58
|
+
*/
|
|
59
|
+
export function transformDbRecords(records) {
|
|
60
|
+
return records.map(transformDbRecord);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Example usage in a SvelteKit API endpoint:
|
|
64
|
+
*
|
|
65
|
+
* ```typescript
|
|
66
|
+
* // src/routes/api/cell-history/[cellName]/+server.ts
|
|
67
|
+
* import { json } from '@sveltejs/kit';
|
|
68
|
+
* import { transformDbRecords } from './history-api-helper';
|
|
69
|
+
* import { db } from '../../server/database';
|
|
70
|
+
*
|
|
71
|
+
* export async function GET({ params }) {
|
|
72
|
+
* const { cellName } = params;
|
|
73
|
+
*
|
|
74
|
+
* const dbRecords = await db.query(`
|
|
75
|
+
* SELECT * FROM CELL_CONFIG_HISTORY
|
|
76
|
+
* WHERE CELLNAME = :cellName
|
|
77
|
+
* ORDER BY CONFIG_DATE DESC
|
|
78
|
+
* `, { cellName });
|
|
79
|
+
*
|
|
80
|
+
* return json(transformDbRecords(dbRecords));
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
@@ -9,6 +9,7 @@ export { default as CellTablePanel } from './CellTablePanel.svelte';
|
|
|
9
9
|
export { default as CellTableDemo } from './CellTableDemo.svelte';
|
|
10
10
|
export { default as CellHistoryDemo } from './CellHistoryDemo.svelte';
|
|
11
11
|
export { default as ColumnPicker } from './ColumnPicker.svelte';
|
|
12
|
+
export { transformDbRecord, transformDbRecords, formatConfigDate, type CellHistoryDbRecord, type CellHistoryApiResponse } from './history-api-helper';
|
|
12
13
|
export { generateCells, generateCellsFromPreset, getGeneratorInfo, GENERATOR_PRESETS, type CellGeneratorConfig, type GeneratorPreset } from '../../shared/demo';
|
|
13
|
-
export type { CellData, CellTableGroupField, ColumnPreset, ColumnVisibility, TechColorMap, StatusColorMap, CellTableProps, RowSelectionEvent, RowClickEvent, RowDblClickEvent, RowContextMenuEvent, DataChangeEvent, CellTableColumn, ColumnGroups } from './types';
|
|
14
|
-
export { DEFAULT_TECH_COLORS, DEFAULT_STATUS_COLORS, FBAND_COLORS, COLUMN_GROUPS, getAllColumns, getColumnsForPreset, getGroupHeaderFormatter, getColumnMetadata, getPresetVisibleFields, fbandSorter, cellNameSectorSorter, cellDataSorter, createTechFormatter, createStatusFormatter, createFbandFormatter, numberFormatter, coordinateFormatter, azimuthFormatter, heightFormatter, type ColumnMeta } from './column-config';
|
|
14
|
+
export type { CellData, CellTableGroupField, ColumnPreset, ColumnVisibility, TechColorMap, StatusColorMap, CellTableProps, RowSelectionEvent, RowClickEvent, RowDblClickEvent, RowContextMenuEvent, DataChangeEvent, CellTableColumn, ColumnGroups, CellHistoryData } from './types';
|
|
15
|
+
export { DEFAULT_TECH_COLORS, DEFAULT_STATUS_COLORS, FBAND_COLORS, COLUMN_GROUPS, getAllColumns, getColumnsForPreset, getGroupHeaderFormatter, getColumnMetadata, getPresetVisibleFields, fbandSorter, cellNameSectorSorter, cellDataSorter, createTechFormatter, createStatusFormatter, createFbandFormatter, createHistoryChangeFormatter, historyDateFormatter, numberFormatter, coordinateFormatter, azimuthFormatter, heightFormatter, type ColumnMeta } from './column-config';
|
|
@@ -10,9 +10,11 @@ export { default as CellTablePanel } from './CellTablePanel.svelte';
|
|
|
10
10
|
export { default as CellTableDemo } from './CellTableDemo.svelte';
|
|
11
11
|
export { default as CellHistoryDemo } from './CellHistoryDemo.svelte';
|
|
12
12
|
export { default as ColumnPicker } from './ColumnPicker.svelte';
|
|
13
|
+
// History API helpers (for backend transformation)
|
|
14
|
+
export { transformDbRecord, transformDbRecords, formatConfigDate } from './history-api-helper';
|
|
13
15
|
// Re-export shared demo data utilities for convenience
|
|
14
16
|
// Note: Cell type is NOT re-exported here to avoid conflicts with map-v2
|
|
15
17
|
// Import Cell from '$lib/shared/demo' or '@smartnet360/svelte-components' directly
|
|
16
18
|
export { generateCells, generateCellsFromPreset, getGeneratorInfo, GENERATOR_PRESETS } from '../../shared/demo';
|
|
17
19
|
// Configuration utilities
|
|
18
|
-
export { DEFAULT_TECH_COLORS, DEFAULT_STATUS_COLORS, FBAND_COLORS, COLUMN_GROUPS, getAllColumns, getColumnsForPreset, getGroupHeaderFormatter, getColumnMetadata, getPresetVisibleFields, fbandSorter, cellNameSectorSorter, cellDataSorter, createTechFormatter, createStatusFormatter, createFbandFormatter, numberFormatter, coordinateFormatter, azimuthFormatter, heightFormatter } from './column-config';
|
|
20
|
+
export { DEFAULT_TECH_COLORS, DEFAULT_STATUS_COLORS, FBAND_COLORS, COLUMN_GROUPS, getAllColumns, getColumnsForPreset, getGroupHeaderFormatter, getColumnMetadata, getPresetVisibleFields, fbandSorter, cellNameSectorSorter, cellDataSorter, createTechFormatter, createStatusFormatter, createFbandFormatter, createHistoryChangeFormatter, historyDateFormatter, numberFormatter, coordinateFormatter, azimuthFormatter, heightFormatter } from './column-config';
|
|
@@ -133,3 +133,29 @@ export interface ColumnGroups {
|
|
|
133
133
|
kpi: string[];
|
|
134
134
|
history: string[];
|
|
135
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Cell configuration history record
|
|
138
|
+
* Each record represents a point in time where a cell's configuration changed
|
|
139
|
+
*/
|
|
140
|
+
export interface CellHistoryData {
|
|
141
|
+
/** Cell name identifier */
|
|
142
|
+
CELLNAME: string;
|
|
143
|
+
/** Date of configuration change (ISO 8601 format) */
|
|
144
|
+
CONFIG_DATE: string;
|
|
145
|
+
/** Current antenna type at this config date */
|
|
146
|
+
ANTENNA_TYPE: string;
|
|
147
|
+
/** Previous antenna type before this change */
|
|
148
|
+
PREV_ANTENNA_TYPE: string;
|
|
149
|
+
/** Current antenna output power (dBm) */
|
|
150
|
+
ANTOUTPUTPWR: number;
|
|
151
|
+
/** Previous antenna output power */
|
|
152
|
+
PREV_ANTOUTPUTPWR: number;
|
|
153
|
+
/** Current mechanical tilt (degrees) */
|
|
154
|
+
MECHANIC_TILT: number;
|
|
155
|
+
/** Previous mechanical tilt */
|
|
156
|
+
PREV_MECHANIC_TILT: number;
|
|
157
|
+
/** Current electrical tilt (degrees) */
|
|
158
|
+
ELECTRONIC_TILT: number;
|
|
159
|
+
/** Previous electrical tilt */
|
|
160
|
+
PREV_ELECTRONIC_TILT: number;
|
|
161
|
+
}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
CustomCellsLayer,
|
|
26
26
|
CustomCellSetManager,
|
|
27
27
|
createCustomCellSetsStore
|
|
28
|
-
} from '../features/
|
|
28
|
+
} from '../features/custom';
|
|
29
29
|
// Custom Sites Feature
|
|
30
30
|
import {
|
|
31
31
|
CustomSitesLayer,
|
|
@@ -105,11 +105,6 @@
|
|
|
105
105
|
setsStore={customCellSets}
|
|
106
106
|
/>
|
|
107
107
|
|
|
108
|
-
<!-- Custom Sites Manager (includes filter controls for each set) -->
|
|
109
|
-
<CustomSiteSetManager
|
|
110
|
-
position="top-left"
|
|
111
|
-
setsStore={customSiteSets}
|
|
112
|
-
/>
|
|
113
108
|
|
|
114
109
|
<FeatureSettingsControl
|
|
115
110
|
position="top-right"
|
package/dist/map-v3/features/{cells/custom → custom}/components/CustomCellFilterControl.svelte
RENAMED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* Shows groups with their cell counts and allows color customization.
|
|
7
7
|
*/
|
|
8
8
|
import { untrack, onDestroy } from 'svelte';
|
|
9
|
-
import { MapControl } from '
|
|
10
|
-
import { createTreeStore, TreeView } from '
|
|
9
|
+
import { MapControl } from '../../../shared';
|
|
10
|
+
import { createTreeStore, TreeView } from '../../../../core/TreeView';
|
|
11
11
|
import type { CustomCellSetsStore } from '../stores/custom-cell-sets.svelte';
|
|
12
12
|
import type { CustomCellSet } from '../types';
|
|
13
13
|
import { buildCustomCellTree } from '../logic/tree-adapter';
|
|
@@ -122,9 +122,16 @@
|
|
|
122
122
|
<!-- Set Info -->
|
|
123
123
|
<div class="set-info mb-2 px-1">
|
|
124
124
|
<small class="text-muted">
|
|
125
|
-
{set.cells.
|
|
125
|
+
{#if set.cells.some(c => c.geometry === 'cell') && set.cells.some(c => c.geometry === 'point')}
|
|
126
|
+
{set.cells.filter(c => c.geometry === 'cell').length} cells, {set.cells.filter(c => c.geometry === 'point').length} points
|
|
127
|
+
{:else if set.cells.some(c => c.geometry === 'cell')}
|
|
128
|
+
{set.cells.filter(c => c.geometry === 'cell').length} cells
|
|
129
|
+
{:else}
|
|
130
|
+
{set.cells.filter(c => c.geometry === 'point').length} points
|
|
131
|
+
{/if}
|
|
132
|
+
in {set.groups.length} groups
|
|
126
133
|
{#if set.unmatchedTxIds.length > 0}
|
|
127
|
-
<span class="text-warning ms-1" title="Some
|
|
134
|
+
<span class="text-warning ms-1" title="Some IDs not found">
|
|
128
135
|
({set.unmatchedTxIds.length} unmatched)
|
|
129
136
|
</span>
|
|
130
137
|
{/if}
|