@smartnet360/svelte-components 0.0.131 → 0.0.133

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,68 +1,114 @@
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
+ frequency: number;
12
+ configDate: string;
13
+ // Current values
14
+ antenna: string;
15
+ electricalTilt: number;
16
+ mechanicalTilt: number;
17
+ power: number;
18
+ // Previous values for change comparison
19
+ prevAntenna: string;
20
+ prevElectricalTilt: number;
21
+ prevMechanicalTilt: number;
22
+ prevPower: number;
23
+ // Virtual fields for the change columns
24
+ antennaChange: string;
25
+ etiltChange: number;
26
+ mtiltChange: number;
27
+ powerChange: number;
59
28
  }
60
29
 
61
- let cellName = $state('2918141');
62
- let historyData = $state(generateMockHistory('2918141'));
30
+ /**
31
+ * Transform API history data to table-friendly format
32
+ * Maps UPPER_CASE API fields to camelCase and adds virtual fields for change detection
33
+ */
34
+ function transformHistoryData(apiData: CellHistoryData[]): HistoryTableRow[] {
35
+ return apiData.map((record, index) => ({
36
+ id: `hist-${index}`,
37
+ cellName: record.CELLNAME,
38
+ frequency: record.FREQUENCY,
39
+ configDate: record.CONFIG_DATE,
40
+ // Current values
41
+ antenna: record.ANTENNA_TYPE,
42
+ electricalTilt: record.ELECTRONIC_TILT,
43
+ mechanicalTilt: record.MECHANIC_TILT,
44
+ power: record.ANTOUTPUTPWR,
45
+ // Previous values for change comparison
46
+ prevAntenna: record.PREV_ANTENNA_TYPE,
47
+ prevElectricalTilt: record.PREV_ELECTRONIC_TILT,
48
+ prevMechanicalTilt: record.PREV_MECHANIC_TILT,
49
+ prevPower: record.PREV_ANTOUTPUTPWR,
50
+ // Virtual fields for the change columns (needed for column filtering)
51
+ antennaChange: record.ANTENNA_TYPE,
52
+ etiltChange: record.ELECTRONIC_TILT,
53
+ mtiltChange: record.MECHANIC_TILT,
54
+ powerChange: record.ANTOUTPUTPWR,
55
+ }));
56
+ }
57
+
58
+ /**
59
+ * Example API data for cell configuration history
60
+ */
61
+ const EXAMPLE_HISTORY_DATA: CellHistoryData[] = [
62
+ {
63
+ CELLNAME: '1001141',
64
+ FREQUENCY: 1800,
65
+ CONFIG_DATE: '2025-12-03T23:00:00.000Z',
66
+ ANTENNA_TYPE: 'AQU4521R01v06',
67
+ PREV_ANTENNA_TYPE: 'AQU4521R01v06',
68
+ ANTOUTPUTPWR: 20,
69
+ PREV_ANTOUTPUTPWR: 20,
70
+ MECHANIC_TILT: 1,
71
+ PREV_MECHANIC_TILT: 1,
72
+ ELECTRONIC_TILT: 10,
73
+ PREV_ELECTRONIC_TILT: 9
74
+ },
75
+ {
76
+ CELLNAME: '1001141',
77
+ FREQUENCY: 1800,
78
+ CONFIG_DATE: '2025-09-11T22:00:00.000Z',
79
+ ANTENNA_TYPE: 'AQU4521R01v06',
80
+ PREV_ANTENNA_TYPE: 'ADU451602v01',
81
+ ANTOUTPUTPWR: 20,
82
+ PREV_ANTOUTPUTPWR: 20,
83
+ MECHANIC_TILT: 1,
84
+ PREV_MECHANIC_TILT: 0,
85
+ ELECTRONIC_TILT: 9,
86
+ PREV_ELECTRONIC_TILT: 10
87
+ },
88
+ {
89
+ CELLNAME: '1001141',
90
+ FREQUENCY: 1800,
91
+ CONFIG_DATE: '2021-02-15T23:00:00.000Z',
92
+ ANTENNA_TYPE: 'ADU451602v01',
93
+ PREV_ANTENNA_TYPE: 'ADU451602v01',
94
+ ANTOUTPUTPWR: 20,
95
+ PREV_ANTOUTPUTPWR: 20,
96
+ MECHANIC_TILT: 0,
97
+ PREV_MECHANIC_TILT: 2,
98
+ ELECTRONIC_TILT: 10,
99
+ PREV_ELECTRONIC_TILT: 10
100
+ }
101
+ ];
102
+
103
+ let cellName = $state('1001141');
104
+ let rawHistoryData = $state<CellHistoryData[]>(EXAMPLE_HISTORY_DATA);
105
+ let historyData = $derived(transformHistoryData(rawHistoryData));
63
106
 
64
107
  function loadHistory() {
65
- historyData = generateMockHistory(cellName);
108
+ // TODO: Replace with actual API call
109
+ // For now, just reload the example data
110
+ console.log(`Loading history for cell: ${cellName}`);
111
+ rawHistoryData = EXAMPLE_HISTORY_DATA.filter(r => r.CELLNAME === cellName);
66
112
  }
67
113
 
68
114
  function handleSelectionChange(event: RowSelectionEvent) {
@@ -72,20 +118,24 @@
72
118
  /**
73
119
  * Compare two selected history records
74
120
  */
75
- function handleCompareTwoRecords(record1: CellData, record2: CellData) {
121
+ function handleCompareTwoRecords(record1: HistoryTableRow, record2: HistoryTableRow) {
76
122
  const params = new URLSearchParams();
77
123
 
78
124
  // Use antenna from both records
79
125
  if (record1.antenna) params.set('ant1', record1.antenna);
80
126
  if (record2.antenna) params.set('ant2', record2.antenna);
81
127
 
128
+ // Use frequency (same for both records typically)
129
+ params.set('freq1', String(record1.frequency || 1800));
130
+ params.set('freq2', String(record2.frequency || 1800));
131
+
82
132
  // Use electrical tilt values
83
- params.set('etilt1', String(record1.atollET || 0));
84
- params.set('etilt2', String(record2.atollET || 0));
133
+ params.set('etilt1', String(record1.electricalTilt || 0));
134
+ params.set('etilt2', String(record2.electricalTilt || 0));
85
135
 
86
136
  // Use mechanical tilt values
87
- params.set('mtilt1', String(record1.atollMT || 0));
88
- params.set('mtilt2', String(record2.atollMT || 0));
137
+ params.set('mtilt1', String(record1.mechanicalTilt || 0));
138
+ params.set('mtilt2', String(record2.mechanicalTilt || 0));
89
139
 
90
140
  // Add config date as labels
91
141
  params.set('label1', `${record1.cellName} (${record1.configDate})`);
@@ -122,9 +172,16 @@
122
172
  <!-- CellTablePanel with history preset -->
123
173
  <div class="flex-grow-1 overflow-hidden">
124
174
  <CellTablePanel
125
- cells={historyData as CellData[]}
175
+ cells={historyData as unknown as import('./types').CellData[]}
126
176
  groupBy="none"
177
+ groupOptions={[
178
+ { value: 'none', label: 'No Grouping' },
179
+ { value: 'antenna', label: 'Antenna Type' },
180
+ { value: 'cellname', label: 'Cell Name' },
181
+
182
+ ]}
127
183
  columnPreset="history"
184
+ showColumnPresets={false}
128
185
  selectable={true}
129
186
  multiSelect={true}
130
187
  showToolbar={true}
@@ -150,7 +207,7 @@
150
207
  class="btn btn-sm btn-outline-success"
151
208
  disabled={selectedCount !== 2}
152
209
  title={selectedCount === 2 ? 'Compare antenna patterns for selected dates' : 'Select exactly 2 records to compare'}
153
- onclick={() => handleCompareTwoRecords(selectedRows[0], selectedRows[1])}
210
+ onclick={() => handleCompareTwoRecords(selectedRows[0] as unknown as HistoryTableRow, selectedRows[1] as unknown as HistoryTableRow)}
154
211
  >
155
212
  <i class="bi bi-broadcast-pin"></i>
156
213
  <span class="d-none d-sm-inline ms-1">Compare Antennas</span>
@@ -13,7 +13,8 @@
13
13
  RowContextMenuEvent,
14
14
  DataChangeEvent,
15
15
  TechColorMap,
16
- StatusColorMap
16
+ StatusColorMap,
17
+ GroupOption
17
18
  } from './types';
18
19
 
19
20
  interface Props {
@@ -21,6 +22,8 @@
21
22
  cells: CellData[];
22
23
  /** Initial grouping field */
23
24
  groupBy?: CellTableGroupField;
25
+ /** Custom grouping options (overrides default tech/fband/status options) */
26
+ groupOptions?: GroupOption[];
24
27
  /** Initial column preset */
25
28
  columnPreset?: ColumnPreset;
26
29
  /** Enable row selection */
@@ -31,6 +34,8 @@
31
34
  height?: string;
32
35
  /** Show toolbar */
33
36
  showToolbar?: boolean;
37
+ /** Show column presets dropdown and column picker (set false for simple tables) */
38
+ showColumnPresets?: boolean;
34
39
  /** Show export buttons */
35
40
  showExport?: boolean;
36
41
  /** Show JSON export button (requires showExport=true) */
@@ -76,11 +81,13 @@
76
81
  let {
77
82
  cells = [],
78
83
  groupBy = $bindable('none'),
84
+ groupOptions,
79
85
  columnPreset = $bindable('default'),
80
86
  selectable = true,
81
87
  multiSelect = true,
82
88
  height = '100%',
83
89
  showToolbar = true,
90
+ showColumnPresets = true,
84
91
  showExport = true,
85
92
  showJsonExport = false,
86
93
  techColors,
@@ -435,12 +442,14 @@
435
442
  {#if showToolbar}
436
443
  <CellTableToolbar
437
444
  {groupBy}
445
+ {groupOptions}
438
446
  {columnPreset}
439
447
  totalCount={cells.length}
440
448
  {filteredCount}
441
449
  {selectedCount}
442
450
  {showExport}
443
451
  {showJsonExport}
452
+ showPresets={showColumnPresets}
444
453
  ongroupchange={handleGroupChange}
445
454
  onpresetchange={handlePresetChange}
446
455
  onexportcsv={handleExportCSV}
@@ -1,10 +1,12 @@
1
1
  import type { Snippet } from 'svelte';
2
- import type { CellData, CellTableGroupField, ColumnPreset, RowSelectionEvent, RowClickEvent, RowDblClickEvent, TechColorMap, StatusColorMap } from './types';
2
+ import type { CellData, CellTableGroupField, ColumnPreset, RowSelectionEvent, RowClickEvent, RowDblClickEvent, TechColorMap, StatusColorMap, GroupOption } from './types';
3
3
  interface Props {
4
4
  /** Cell data array to display */
5
5
  cells: CellData[];
6
6
  /** Initial grouping field */
7
7
  groupBy?: CellTableGroupField;
8
+ /** Custom grouping options (overrides default tech/fband/status options) */
9
+ groupOptions?: GroupOption[];
8
10
  /** Initial column preset */
9
11
  columnPreset?: ColumnPreset;
10
12
  /** Enable row selection */
@@ -15,6 +17,8 @@ interface Props {
15
17
  height?: string;
16
18
  /** Show toolbar */
17
19
  showToolbar?: boolean;
20
+ /** Show column presets dropdown and column picker (set false for simple tables) */
21
+ showColumnPresets?: boolean;
18
22
  /** Show export buttons */
19
23
  showExport?: boolean;
20
24
  /** Show JSON export button (requires showExport=true) */
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import type { CellTableGroupField, ColumnPreset } from './types';
2
+ import type { CellTableGroupField, ColumnPreset, GroupOption } from './types';
3
3
  import type { ColumnMeta } from './column-config';
4
4
  import ColumnPicker from './ColumnPicker.svelte';
5
5
 
@@ -20,6 +20,8 @@
20
20
  showJsonExport?: boolean;
21
21
  /** Show grouping dropdown */
22
22
  showGrouping?: boolean;
23
+ /** Custom grouping options (overrides default options) */
24
+ groupOptions?: GroupOption[];
23
25
  /** Show preset dropdown */
24
26
  showPresets?: boolean;
25
27
  /** Group change event */
@@ -65,6 +67,7 @@
65
67
  showExport = true,
66
68
  showJsonExport = false,
67
69
  showGrouping = true,
70
+ groupOptions: customGroupOptions,
68
71
  showPresets = true,
69
72
  ongroupchange,
70
73
  onpresetchange,
@@ -84,7 +87,7 @@
84
87
  ontogglescrollspy
85
88
  }: Props = $props();
86
89
 
87
- const groupOptions: { value: CellTableGroupField; label: string }[] = [
90
+ const defaultGroupOptions: GroupOption[] = [
88
91
  { value: 'none', label: 'No Grouping' },
89
92
  { value: 'tech', label: 'Technology' },
90
93
  { value: 'fband', label: 'Frequency Band' },
@@ -93,6 +96,9 @@
93
96
  { value: 'siteId', label: 'Site ID' }
94
97
  ];
95
98
 
99
+ // Use custom options if provided, otherwise use defaults
100
+ const groupOptions = customGroupOptions ?? defaultGroupOptions;
101
+
96
102
  const presetOptions: { value: ColumnPreset; label: string }[] = [
97
103
  { value: 'default', label: 'Default' },
98
104
  { value: 'compact', label: 'Compact' },
@@ -105,8 +111,8 @@
105
111
  ];
106
112
 
107
113
  function handleGroupChange(e: Event) {
108
- const value = (e.target as HTMLSelectElement).value as CellTableGroupField;
109
- ongroupchange?.(value);
114
+ const value = (e.target as HTMLSelectElement).value;
115
+ ongroupchange?.(value as CellTableGroupField);
110
116
  }
111
117
 
112
118
  function handlePresetChange(e: Event) {
@@ -1,4 +1,4 @@
1
- import type { CellTableGroupField, ColumnPreset } from './types';
1
+ import type { CellTableGroupField, ColumnPreset, GroupOption } from './types';
2
2
  import type { ColumnMeta } from './column-config';
3
3
  interface Props {
4
4
  /** Current grouping field */
@@ -17,6 +17,8 @@ interface Props {
17
17
  showJsonExport?: boolean;
18
18
  /** Show grouping dropdown */
19
19
  showGrouping?: boolean;
20
+ /** Custom grouping options (overrides default options) */
21
+ groupOptions?: GroupOption[];
20
22
  /** Show preset dropdown */
21
23
  showPresets?: boolean;
22
24
  /** Group change event */
@@ -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', 'frequency', '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,46 @@ 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,
379
+ ...headerFilterParams,
380
+ },
381
+ {
382
+ title: 'Freq (MHz)',
383
+ field: 'frequency',
384
+ width: 100,
385
+ hozAlign: 'right',
386
+ ...headerFilterParams,
387
+ },
388
+ // History change columns (current vs previous, highlights changes)
389
+ {
390
+ title: 'Antenna',
391
+ field: 'antennaChange',
392
+ width: 160,
393
+ formatter: createHistoryChangeFormatter('antenna', 'prevAntenna'),
335
394
  ...headerFilterParams,
336
395
  },
396
+ {
397
+ title: 'E-Tilt',
398
+ field: 'etiltChange',
399
+ width: 80,
400
+ hozAlign: 'center',
401
+ formatter: createHistoryChangeFormatter('electricalTilt', 'prevElectricalTilt'),
402
+ },
403
+ {
404
+ title: 'M-Tilt',
405
+ field: 'mtiltChange',
406
+ width: 80,
407
+ hozAlign: 'center',
408
+ formatter: createHistoryChangeFormatter('mechanicalTilt', 'prevMechanicalTilt'),
409
+ },
410
+ {
411
+ title: 'Power',
412
+ field: 'powerChange',
413
+ width: 80,
414
+ hozAlign: 'center',
415
+ formatter: createHistoryChangeFormatter('power', 'prevPower'),
416
+ },
337
417
  // Physical columns
338
418
  {
339
419
  title: 'Antenna',
@@ -748,6 +828,11 @@ export function getColumnMetadata() {
748
828
  { field: 'atollBW', title: 'Atoll BW', group: 'Atoll' },
749
829
  // History
750
830
  { field: 'configDate', title: 'Config Date', group: 'History' },
831
+ { field: 'frequency', title: 'Freq (MHz)', group: 'History' },
832
+ { field: 'antennaChange', title: 'Antenna (Δ)', group: 'History' },
833
+ { field: 'etiltChange', title: 'E-Tilt (Δ)', group: 'History' },
834
+ { field: 'mtiltChange', title: 'M-Tilt (Δ)', group: 'History' },
835
+ { field: 'powerChange', title: 'Power (Δ)', group: 'History' },
751
836
  // Compare (Atoll vs Network)
752
837
  { field: 'compareET', title: 'Δ ET', group: 'Compare' },
753
838
  { 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, GroupOption } 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';
@@ -23,6 +23,15 @@ export type ColumnPreset = 'default' | 'compact' | 'full' | 'physical' | 'networ
23
23
  export interface ColumnVisibility {
24
24
  [key: string]: boolean;
25
25
  }
26
+ /**
27
+ * Grouping option for toolbar dropdown
28
+ */
29
+ export interface GroupOption {
30
+ /** Field name to group by (or 'none') */
31
+ value: string;
32
+ /** Display label in dropdown */
33
+ label: string;
34
+ }
26
35
  /**
27
36
  * Technology color mapping
28
37
  */
@@ -133,3 +142,31 @@ export interface ColumnGroups {
133
142
  kpi: string[];
134
143
  history: string[];
135
144
  }
145
+ /**
146
+ * Cell configuration history record
147
+ * Each record represents a point in time where a cell's configuration changed
148
+ */
149
+ export interface CellHistoryData {
150
+ /** Cell name identifier */
151
+ CELLNAME: string;
152
+ /** Frequency in MHz (e.g., 1800, 2100, 3500) */
153
+ FREQUENCY: number;
154
+ /** Date of configuration change (ISO 8601 format) */
155
+ CONFIG_DATE: string;
156
+ /** Current antenna type at this config date */
157
+ ANTENNA_TYPE: string;
158
+ /** Previous antenna type before this change */
159
+ PREV_ANTENNA_TYPE: string;
160
+ /** Current antenna output power (dBm) */
161
+ ANTOUTPUTPWR: number;
162
+ /** Previous antenna output power */
163
+ PREV_ANTOUTPUTPWR: number;
164
+ /** Current mechanical tilt (degrees) */
165
+ MECHANIC_TILT: number;
166
+ /** Previous mechanical tilt */
167
+ PREV_MECHANIC_TILT: number;
168
+ /** Current electrical tilt (degrees) */
169
+ ELECTRONIC_TILT: number;
170
+ /** Previous electrical tilt */
171
+ PREV_ELECTRONIC_TILT: number;
172
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartnet360/svelte-components",
3
- "version": "0.0.131",
3
+ "version": "0.0.133",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",