@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.
Files changed (40) hide show
  1. package/dist/core/CellTable/CellHistoryDemo.svelte +106 -65
  2. package/dist/core/CellTable/column-config.d.ts +12 -0
  3. package/dist/core/CellTable/column-config.js +79 -2
  4. package/dist/core/CellTable/history-api-helper.d.ts +79 -0
  5. package/dist/core/CellTable/history-api-helper.js +83 -0
  6. package/dist/core/CellTable/index.d.ts +3 -2
  7. package/dist/core/CellTable/index.js +3 -1
  8. package/dist/core/CellTable/types.d.ts +26 -0
  9. package/dist/map-v3/demo/DemoMap.svelte +1 -6
  10. package/dist/map-v3/features/{cells/custom → custom}/components/CustomCellFilterControl.svelte +11 -4
  11. package/dist/map-v3/features/custom/components/CustomCellSetManager.svelte +692 -0
  12. package/dist/map-v3/features/{cells/custom → custom}/index.d.ts +1 -1
  13. package/dist/map-v3/features/{cells/custom → custom}/index.js +1 -1
  14. package/dist/map-v3/features/custom/layers/CustomCellsLayer.svelte +399 -0
  15. package/dist/map-v3/features/{cells/custom → custom}/logic/csv-parser.d.ts +11 -7
  16. package/dist/map-v3/features/{cells/custom → custom}/logic/csv-parser.js +64 -20
  17. package/dist/map-v3/features/{cells/custom → custom}/logic/tree-adapter.d.ts +1 -1
  18. package/dist/map-v3/features/{cells/custom → custom}/stores/custom-cell-sets.svelte.d.ts +4 -3
  19. package/dist/map-v3/features/{cells/custom → custom}/stores/custom-cell-sets.svelte.js +30 -10
  20. package/dist/map-v3/features/{cells/custom → custom}/types.d.ts +32 -12
  21. package/dist/map-v3/features/{cells/custom → custom}/types.js +5 -3
  22. package/dist/map-v3/index.d.ts +1 -1
  23. package/dist/map-v3/index.js +1 -1
  24. package/dist/map-v3/shared/controls/MapControl.svelte +43 -15
  25. package/dist/map-v3/shared/controls/MapControl.svelte.d.ts +3 -1
  26. package/package.json +1 -1
  27. package/dist/map-v3/features/cells/custom/components/CustomCellSetManager.svelte +0 -306
  28. package/dist/map-v3/features/cells/custom/layers/CustomCellsLayer.svelte +0 -262
  29. /package/dist/map-v3/features/{cells/custom → custom}/components/CustomCellFilterControl.svelte.d.ts +0 -0
  30. /package/dist/map-v3/features/{cells/custom → custom}/components/CustomCellSetManager.svelte.d.ts +0 -0
  31. /package/dist/map-v3/features/{cells/custom → custom}/components/index.d.ts +0 -0
  32. /package/dist/map-v3/features/{cells/custom → custom}/components/index.js +0 -0
  33. /package/dist/map-v3/features/{cells/custom → custom}/layers/CustomCellsLayer.svelte.d.ts +0 -0
  34. /package/dist/map-v3/features/{cells/custom → custom}/layers/index.d.ts +0 -0
  35. /package/dist/map-v3/features/{cells/custom → custom}/layers/index.js +0 -0
  36. /package/dist/map-v3/features/{cells/custom → custom}/logic/index.d.ts +0 -0
  37. /package/dist/map-v3/features/{cells/custom → custom}/logic/index.js +0 -0
  38. /package/dist/map-v3/features/{cells/custom → custom}/logic/tree-adapter.js +0 -0
  39. /package/dist/map-v3/features/{cells/custom → custom}/stores/index.d.ts +0 -0
  40. /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, CellData } from './types';
3
+ import type { RowSelectionEvent, CellHistoryData } from './types';
4
4
 
5
- // Generate mock history data for a single cell
6
- function generateMockHistory(cellName: string, count: number = 20): Partial<CellData>[] {
7
- const antennas = [
8
- 'ADU451R76V06',
9
- 'ADU451R79V06',
10
- 'AAHF4518R3V06',
11
- 'AAHF4516R1V06',
12
- '742215'
13
- ];
14
-
15
- const history: Partial<CellData>[] = [];
16
- const baseDate = new Date('2025-10-20');
17
-
18
- let currentAntenna = antennas[0];
19
- let currentET = 4;
20
- let currentMT = 2;
21
- let currentPW = 20;
22
-
23
- for (let i = 0; i < count; i++) {
24
- // Random chance to change values
25
- if (Math.random() > 0.7) {
26
- currentAntenna = antennas[Math.floor(Math.random() * antennas.length)];
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
- let cellName = $state('2918141');
62
- let historyData = $state(generateMockHistory('2918141'));
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
- historyData = generateMockHistory(cellName);
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: CellData, record2: CellData) {
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.atollET || 0));
84
- params.set('etilt2', String(record2.atollET || 0));
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.atollMT || 0));
88
- params.set('mtilt2', String(record2.atollMT || 0));
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', 'antenna', 'atollET', 'atollMT', 'atollPW'],
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: 150,
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/cells/custom';
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"
@@ -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 '../../../../shared';
10
- import { createTreeStore, TreeView } from '../../../../../core/TreeView';
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.length} cells in {set.groups.length} groups
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 cells not found">
134
+ <span class="text-warning ms-1" title="Some IDs not found">
128
135
  ({set.unmatchedTxIds.length} unmatched)
129
136
  </span>
130
137
  {/if}