@smartnet360/svelte-components 0.0.110 → 0.0.112

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.
@@ -190,21 +190,18 @@
190
190
  }
191
191
 
192
192
  // Track previous values to avoid unnecessary updates
193
- let prevCellsLength = 0;
194
- let prevCellsFirstId: string | null = null;
193
+ let prevCellsRef: unknown[] | null = null;
195
194
  let prevGroupBy: string | null = null;
196
195
  let prevColumnPreset: string | null = null;
197
196
 
198
- // Update table data when cells actually change (not just reference)
197
+ // Update table data when cells change (reference comparison)
199
198
  $effect(() => {
200
- const currentLength = sortedCells?.length ?? 0;
201
- const currentFirstId = sortedCells?.[0]?.id ?? null;
199
+ // Track the cells array reference to detect any change
200
+ const currentRef = sortedCells;
202
201
 
203
- // Only update if length or first item changed (rough equality check)
204
- if (isInitialized && table &&
205
- (currentLength !== prevCellsLength || currentFirstId !== prevCellsFirstId)) {
206
- prevCellsLength = currentLength;
207
- prevCellsFirstId = currentFirstId;
202
+ // Update if reference changed (new array assignment)
203
+ if (isInitialized && table && currentRef !== prevCellsRef) {
204
+ prevCellsRef = currentRef;
208
205
  table.replaceData(sortedCells);
209
206
  ondatachange?.({
210
207
  type: 'load',
@@ -9,6 +9,7 @@
9
9
 
10
10
  let groupBy: CellTableGroupField = $state('none');
11
11
  let columnPreset: ColumnPreset = $state('default');
12
+ let searchTerm = $state('');
12
13
 
13
14
  function handleSelectionChange(event: RowSelectionEvent) {
14
15
  console.log('Selection changed:', event.ids);
@@ -17,6 +18,28 @@
17
18
  function regenerateData() {
18
19
  demoCells = generateCellsFromPreset(datasetSize);
19
20
  }
21
+
22
+ function handleSearch() {
23
+ const term = searchTerm.trim().toLowerCase();
24
+ if (!term) {
25
+ // Reset to full dataset
26
+ demoCells = generateCellsFromPreset(datasetSize);
27
+ return;
28
+ }
29
+ // Filter by siteId (case-insensitive, partial match)
30
+ const allCells = generateCellsFromPreset(datasetSize);
31
+ demoCells = allCells.filter(cell =>
32
+ cell.siteId.toLowerCase().includes(term) ||
33
+ cell.cellName.toLowerCase().includes(term)
34
+ );
35
+ console.log(`Search "${term}": found ${demoCells.length} cells`);
36
+ }
37
+
38
+ function handleSearchKeydown(event: KeyboardEvent) {
39
+ if (event.key === 'Enter') {
40
+ handleSearch();
41
+ }
42
+ }
20
43
  </script>
21
44
 
22
45
  <div class="cell-table-page vh-100 d-flex flex-column">
@@ -55,6 +78,27 @@
55
78
  title="Cell Data"
56
79
  onselectionchange={handleSelectionChange}
57
80
  >
81
+ {#snippet headerSearch()}
82
+ <div class="input-group input-group-sm" style="width: 180px;">
83
+ <input
84
+ type="text"
85
+ class="form-control"
86
+ placeholder="Search site or cell..."
87
+ bind:value={searchTerm}
88
+ onkeydown={handleSearchKeydown}
89
+ />
90
+ <button
91
+ class="btn btn-outline-primary"
92
+ type="button"
93
+ onclick={handleSearch}
94
+ title="Search"
95
+ aria-label="Search"
96
+ >
97
+ <i class="bi bi-search"></i>
98
+ </button>
99
+ </div>
100
+ {/snippet}
101
+
58
102
  {#snippet footer({ selectedRows, selectedCount })}
59
103
  <div class="d-flex align-items-center justify-content-between">
60
104
  <span class="text-muted small">
@@ -2,7 +2,7 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import CellTable from './CellTable.svelte';
4
4
  import CellTableToolbar from './CellTableToolbar.svelte';
5
- import { getColumnMetadata, getPresetVisibleFields } from './column-config';
5
+ import { getColumnMetadata, getPresetVisibleFields, DEFAULT_TECH_COLORS, DEFAULT_STATUS_COLORS, FBAND_COLORS } from './column-config';
6
6
  import type {
7
7
  CellData,
8
8
  CellTableGroupField,
@@ -44,6 +44,10 @@
44
44
  showDetailsSidebar?: boolean;
45
45
  /** Sidebar width in pixels */
46
46
  sidebarWidth?: number;
47
+ /** Persist settings (groupBy, visibleColumns) to localStorage */
48
+ persistSettings?: boolean;
49
+ /** Storage key prefix for persisted settings */
50
+ storageKey?: string;
47
51
  /** Bindable reference to table methods */
48
52
  tableRef?: { redraw: () => void } | null;
49
53
  /** Row selection change event */
@@ -52,6 +56,8 @@
52
56
  onrowclick?: (event: RowClickEvent) => void;
53
57
  /** Row double-click event */
54
58
  onrowdblclick?: (event: RowDblClickEvent) => void;
59
+ /** Custom header search slot (appears next to title) */
60
+ headerSearch?: Snippet;
55
61
  /** Custom header actions slot */
56
62
  headerActions?: Snippet;
57
63
  /** Custom footer slot */
@@ -75,15 +81,81 @@
75
81
  title = 'Cell Data',
76
82
  showDetailsSidebar = false,
77
83
  sidebarWidth = 320,
84
+ persistSettings = true,
85
+ storageKey = 'cell-table',
78
86
  tableRef = $bindable(null),
79
87
  onselectionchange,
80
88
  onrowclick,
81
89
  onrowdblclick,
90
+ headerSearch,
82
91
  headerActions,
83
92
  footer,
84
93
  detailsContent
85
94
  }: Props = $props();
86
95
 
96
+ // Storage keys
97
+ const STORAGE_KEY_GROUP = `${storageKey}-groupBy`;
98
+ const STORAGE_KEY_COLUMNS = `${storageKey}-visibleColumns`;
99
+ const STORAGE_KEY_FILTERS = `${storageKey}-filtersVisible`;
100
+
101
+ // Load persisted settings
102
+ function loadPersistedSettings() {
103
+ if (!persistSettings || typeof localStorage === 'undefined') return { columns: null, filtersVisible: true };
104
+
105
+ let columns: string[] | null = null;
106
+ let filters = true;
107
+
108
+ try {
109
+ const savedGroup = localStorage.getItem(STORAGE_KEY_GROUP);
110
+ if (savedGroup) {
111
+ groupBy = savedGroup as CellTableGroupField;
112
+ }
113
+
114
+ const savedColumns = localStorage.getItem(STORAGE_KEY_COLUMNS);
115
+ if (savedColumns) {
116
+ columns = JSON.parse(savedColumns) as string[];
117
+ }
118
+
119
+ const savedFilters = localStorage.getItem(STORAGE_KEY_FILTERS);
120
+ if (savedFilters !== null) {
121
+ filters = savedFilters === 'true';
122
+ }
123
+ } catch (e) {
124
+ console.warn('Failed to load CellTable settings:', e);
125
+ }
126
+ return { columns, filtersVisible: filters };
127
+ }
128
+
129
+ // Save group setting
130
+ function saveGroupSetting(group: CellTableGroupField) {
131
+ if (!persistSettings || typeof localStorage === 'undefined') return;
132
+ try {
133
+ localStorage.setItem(STORAGE_KEY_GROUP, group);
134
+ } catch (e) {
135
+ console.warn('Failed to save group setting:', e);
136
+ }
137
+ }
138
+
139
+ // Save column visibility
140
+ function saveColumnVisibility(columns: string[]) {
141
+ if (!persistSettings || typeof localStorage === 'undefined') return;
142
+ try {
143
+ localStorage.setItem(STORAGE_KEY_COLUMNS, JSON.stringify(columns));
144
+ } catch (e) {
145
+ console.warn('Failed to save column visibility:', e);
146
+ }
147
+ }
148
+
149
+ // Save filter visibility
150
+ function saveFilterVisibility(visible: boolean) {
151
+ if (!persistSettings || typeof localStorage === 'undefined') return;
152
+ try {
153
+ localStorage.setItem(STORAGE_KEY_FILTERS, String(visible));
154
+ } catch (e) {
155
+ console.warn('Failed to save filter visibility:', e);
156
+ }
157
+ }
158
+
87
159
  let cellTable: CellTable;
88
160
  let selectedCount = $state(0);
89
161
  let selectedRows = $state<CellData[]>([]);
@@ -91,13 +163,16 @@
91
163
  let sidebarOpen = $state(false);
92
164
  let clickedCell: CellData | null = $state(null);
93
165
  let tableRefSet = false;
94
- let filtersVisible = $state(true);
95
166
 
96
167
  // Column visibility management
97
168
  const columnMeta = getColumnMetadata();
98
- let visibleColumns = $state<string[]>(getPresetVisibleFields(columnPreset));
169
+
170
+ // Initialize from storage or defaults
171
+ const persistedSettings = loadPersistedSettings();
172
+ let filtersVisible = $state(persistedSettings.filtersVisible);
173
+ let visibleColumns = $state<string[]>(persistedSettings.columns ?? getPresetVisibleFields(columnPreset));
99
174
 
100
- // Update visible columns when preset changes
175
+ // Update visible columns when preset changes (but not from storage load)
101
176
  $effect(() => {
102
177
  visibleColumns = getPresetVisibleFields(columnPreset);
103
178
  });
@@ -109,6 +184,10 @@
109
184
  tableRef = {
110
185
  redraw: () => cellTable?.redraw()
111
186
  };
187
+ // Apply persisted filter visibility after table is ready
188
+ if (!filtersVisible) {
189
+ setTimeout(() => cellTable?.toggleHeaderFilters(false), 100);
190
+ }
112
191
  }
113
192
  });
114
193
 
@@ -133,6 +212,7 @@
133
212
 
134
213
  function handleGroupChange(group: CellTableGroupField) {
135
214
  groupBy = group;
215
+ saveGroupSetting(group);
136
216
  }
137
217
 
138
218
  function handlePresetChange(preset: ColumnPreset) {
@@ -162,6 +242,7 @@
162
242
  function handleToggleFilters() {
163
243
  filtersVisible = !filtersVisible;
164
244
  cellTable?.toggleHeaderFilters(filtersVisible);
245
+ saveFilterVisibility(filtersVisible);
165
246
  }
166
247
 
167
248
  function handleColumnVisibilityChange(field: string, visible: boolean) {
@@ -174,6 +255,7 @@
174
255
  visibleColumns = visibleColumns.filter(f => f !== field);
175
256
  cellTable?.hideColumn(field);
176
257
  }
258
+ saveColumnVisibility(visibleColumns);
177
259
  }
178
260
 
179
261
  function handleResetColumns() {
@@ -187,6 +269,14 @@
187
269
  cellTable?.hideColumn(col.field);
188
270
  }
189
271
  });
272
+ // Clear persisted column visibility (use preset defaults)
273
+ if (persistSettings && typeof localStorage !== 'undefined') {
274
+ try {
275
+ localStorage.removeItem(STORAGE_KEY_COLUMNS);
276
+ } catch (e) {
277
+ console.warn('Failed to clear column visibility:', e);
278
+ }
279
+ }
190
280
  }
191
281
 
192
282
  function toggleSidebar() {
@@ -225,10 +315,15 @@
225
315
  <div class="cell-table-panel d-flex flex-column" style:height>
226
316
  <!-- Header -->
227
317
  <div class="panel-header d-flex align-items-center justify-content-between px-3 py-2 bg-body-secondary border-bottom">
228
- <h6 class="mb-0 d-flex align-items-center gap-2">
229
- <i class="bi bi-table text-primary"></i>
230
- {title}
231
- </h6>
318
+ <div class="d-flex align-items-center gap-3">
319
+ <h6 class="mb-0 d-flex align-items-center gap-2">
320
+ <i class="bi bi-table text-primary"></i>
321
+ {title}
322
+ </h6>
323
+ {#if headerSearch}
324
+ {@render headerSearch()}
325
+ {/if}
326
+ </div>
232
327
  <div class="d-flex align-items-center gap-2">
233
328
  {#if headerActions}
234
329
  <div class="header-actions">
@@ -334,12 +429,12 @@
334
429
 
335
430
  <dt class="col-5 text-muted">Technology</dt>
336
431
  <dd class="col-7">
337
- <span class="badge bg-secondary">{clickedCell.tech}</span>
432
+ <span class="badge" style="background-color: {techColors?.[clickedCell.tech] ?? DEFAULT_TECH_COLORS[clickedCell.tech] ?? '#6c757d'}; color: white;">{clickedCell.tech}</span>
338
433
  </dd>
339
434
 
340
435
  <dt class="col-5 text-muted">Band</dt>
341
436
  <dd class="col-7">
342
- <span class="badge bg-info">{clickedCell.fband}</span>
437
+ <span class="badge" style="background-color: {FBAND_COLORS[clickedCell.fband] ?? '#6c757d'}; color: white;">{clickedCell.fband}</span>
343
438
  </dd>
344
439
 
345
440
  <dt class="col-5 text-muted">Frequency</dt>
@@ -347,7 +442,7 @@
347
442
 
348
443
  <dt class="col-5 text-muted">Status</dt>
349
444
  <dd class="col-7">
350
- <span class="badge bg-success">{clickedCell.status.replace(/_/g, ' ')}</span>
445
+ <span class="badge" style="background-color: {statusColors?.[clickedCell.status] ?? DEFAULT_STATUS_COLORS[clickedCell.status] ?? '#6c757d'}; color: white;">{clickedCell.status.replace(/_/g, ' ')}</span>
351
446
  </dd>
352
447
 
353
448
  <dt class="col-12 text-muted mt-2 mb-1 border-top pt-2">Physical</dt>
@@ -29,6 +29,10 @@ interface Props {
29
29
  showDetailsSidebar?: boolean;
30
30
  /** Sidebar width in pixels */
31
31
  sidebarWidth?: number;
32
+ /** Persist settings (groupBy, visibleColumns) to localStorage */
33
+ persistSettings?: boolean;
34
+ /** Storage key prefix for persisted settings */
35
+ storageKey?: string;
32
36
  /** Bindable reference to table methods */
33
37
  tableRef?: {
34
38
  redraw: () => void;
@@ -39,6 +43,8 @@ interface Props {
39
43
  onrowclick?: (event: RowClickEvent) => void;
40
44
  /** Row double-click event */
41
45
  onrowdblclick?: (event: RowDblClickEvent) => void;
46
+ /** Custom header search slot (appears next to title) */
47
+ headerSearch?: Snippet;
42
48
  /** Custom header actions slot */
43
49
  headerActions?: Snippet;
44
50
  /** Custom footer slot */
@@ -87,7 +87,8 @@
87
87
  { value: 'full', label: 'All Columns' },
88
88
  { value: 'physical', label: 'Physical' },
89
89
  { value: 'network', label: 'Network' },
90
- { value: 'planning', label: 'Planning' }
90
+ { value: 'planning', label: 'Planning' },
91
+ { value: 'compare', label: 'Compare (Atoll/Nwt)' }
91
92
  ];
92
93
 
93
94
  function handleGroupChange(e: Event) {
@@ -53,6 +53,14 @@ export declare function azimuthFormatter(cell: CellComponent): string;
53
53
  * Height formatter with meter unit
54
54
  */
55
55
  export declare function heightFormatter(cell: CellComponent): string;
56
+ /**
57
+ * Comparison formatter for Atoll vs Network values
58
+ * Shows both values side by side with color coding:
59
+ * - Green: values match exactly
60
+ * - Red: values don't match
61
+ * - Gray: one or both values missing
62
+ */
63
+ export declare function createCompareFormatter(atollField: string, nwtField: string): (cell: CellComponent) => string;
56
64
  /**
57
65
  * Custom sorter for fband - extracts numeric portion and sorts numerically
58
66
  * Examples: LTE700 → 700, GSM900 → 900, LTE1800 → 1800, 5G-3500 → 3500
@@ -53,12 +53,13 @@ export const FBAND_COLORS = {
53
53
  * Column groups for different presets
54
54
  */
55
55
  export const COLUMN_GROUPS = {
56
- core: ['id', 'cellName', 'siteId', 'tech', 'fband', 'status'],
56
+ core: ['siteId', 'txId', 'cellName', 'tech', 'fband', 'status'],
57
57
  physical: ['antenna', 'azimuth', 'height', 'electricalTilt', 'beamwidth'],
58
- network: ['dlEarfn', 'bcch', 'pci1', 'cellId3', 'nwtP1', 'nwtP2', 'nwtRS', 'nwtBW'],
58
+ network: ['dlEarfn', 'bcch', 'pci1', 'cellId3', 'nwtET', 'nwtPW', 'nwtRS', 'nwtBW'],
59
59
  planning: ['planner', 'comment', 'onAirDate', 'type'],
60
- atoll: ['atollETP', 'atollPW', 'atollRS', 'atollBW'],
60
+ atoll: ['atollET', 'atollPW', 'atollRS', 'atollBW'],
61
61
  position: ['latitude', 'longitude', 'siteLatitude', 'siteLongitude', 'dx', 'dy'],
62
+ compare: ['compareET', 'comparePW', 'compareRS', 'compareBW'],
62
63
  };
63
64
  /**
64
65
  * Create a technology badge formatter
@@ -131,6 +132,40 @@ export function heightFormatter(cell) {
131
132
  return '';
132
133
  return `${value}m`;
133
134
  }
135
+ /**
136
+ * Comparison formatter for Atoll vs Network values
137
+ * Shows both values side by side with color coding:
138
+ * - Green: values match exactly
139
+ * - Red: values don't match
140
+ * - Gray: one or both values missing
141
+ */
142
+ export function createCompareFormatter(atollField, nwtField) {
143
+ return (cell) => {
144
+ const row = cell.getRow().getData();
145
+ const atollVal = row[atollField];
146
+ const nwtVal = row[nwtField];
147
+ const atollStr = atollVal !== null && atollVal !== undefined ? String(atollVal) : '-';
148
+ const nwtStr = nwtVal !== null && nwtVal !== undefined ? String(nwtVal) : '-';
149
+ // Determine match status
150
+ const atollMissing = atollVal === null || atollVal === undefined;
151
+ const nwtMissing = nwtVal === null || nwtVal === undefined;
152
+ let bgColor;
153
+ let textColor = 'white';
154
+ if (atollMissing || nwtMissing) {
155
+ // One or both missing - gray
156
+ bgColor = '#6c757d';
157
+ }
158
+ else if (atollVal === nwtVal) {
159
+ // Exact match - green
160
+ bgColor = '#198754';
161
+ }
162
+ else {
163
+ // Mismatch - red
164
+ bgColor = '#dc3545';
165
+ }
166
+ return `<span class="badge" style="background-color: ${bgColor}; color: ${textColor}; font-size: 0.75rem; font-weight: normal;">${atollStr} | ${nwtStr}</span>`;
167
+ };
168
+ }
134
169
  /**
135
170
  * Custom sorter for fband - extracts numeric portion and sorts numerically
136
171
  * Examples: LTE700 → 700, GSM900 → 900, LTE1800 → 1800, 5G-3500 → 3500
@@ -178,24 +213,26 @@ export function getAllColumns(techColors = DEFAULT_TECH_COLORS, statusColors = D
178
213
  const headerFilterParams = headerFilters ? { headerFilter: 'input' } : {};
179
214
  const selectHeaderFilter = headerFilters ? { headerFilter: 'list', headerFilterParams: { valuesLookup: true } } : {};
180
215
  return [
181
- // Core columns
216
+ // Core columns - siteId, txId, cellName first (frozen)
182
217
  {
183
- title: 'ID',
184
- field: 'id',
218
+ title: 'Site ID',
219
+ field: 'siteId',
185
220
  width: 120,
186
221
  frozen: true,
187
222
  ...headerFilterParams,
188
223
  },
189
224
  {
190
- title: 'Cell Name',
191
- field: 'cellName',
192
- width: 150,
225
+ title: 'TX ID',
226
+ field: 'txId',
227
+ width: 100,
228
+ frozen: true,
193
229
  ...headerFilterParams,
194
230
  },
195
231
  {
196
- title: 'Site ID',
197
- field: 'siteId',
198
- width: 120,
232
+ title: 'Cell Name',
233
+ field: 'cellName',
234
+ width: 150,
235
+ frozen: true,
199
236
  ...headerFilterParams,
200
237
  },
201
238
  {
@@ -318,12 +355,6 @@ export function getAllColumns(techColors = DEFAULT_TECH_COLORS, statusColors = D
318
355
  width: 100,
319
356
  ...headerFilterParams,
320
357
  },
321
- {
322
- title: 'TX ID',
323
- field: 'txId',
324
- width: 100,
325
- ...headerFilterParams,
326
- },
327
358
  {
328
359
  title: 'Ctrl ID',
329
360
  field: 'ctrlid',
@@ -360,8 +391,8 @@ export function getAllColumns(techColors = DEFAULT_TECH_COLORS, statusColors = D
360
391
  },
361
392
  // Atoll columns
362
393
  {
363
- title: 'Atoll ETP',
364
- field: 'atollETP',
394
+ title: 'Atoll ET',
395
+ field: 'atollET',
365
396
  width: 90,
366
397
  hozAlign: 'right',
367
398
  formatter: numberFormatter(1),
@@ -391,6 +422,39 @@ export function getAllColumns(techColors = DEFAULT_TECH_COLORS, statusColors = D
391
422
  formatter: numberFormatter(1),
392
423
  ...headerFilterParams,
393
424
  },
425
+ // Compare columns (Atoll vs Network)
426
+ {
427
+ title: 'Δ ET',
428
+ field: 'compareET',
429
+ width: 110,
430
+ hozAlign: 'center',
431
+ formatter: createCompareFormatter('atollET', 'nwtET'),
432
+ headerTooltip: 'Atoll ET | Network ET',
433
+ },
434
+ {
435
+ title: 'Δ PW',
436
+ field: 'comparePW',
437
+ width: 110,
438
+ hozAlign: 'center',
439
+ formatter: createCompareFormatter('atollPW', 'nwtPW'),
440
+ headerTooltip: 'Atoll PW | Network PW',
441
+ },
442
+ {
443
+ title: 'Δ RS',
444
+ field: 'compareRS',
445
+ width: 110,
446
+ hozAlign: 'center',
447
+ formatter: createCompareFormatter('atollRS', 'nwtRS'),
448
+ headerTooltip: 'Atoll RS | Network RS',
449
+ },
450
+ {
451
+ title: 'Δ BW',
452
+ field: 'compareBW',
453
+ width: 110,
454
+ hozAlign: 'center',
455
+ formatter: createCompareFormatter('atollBW', 'nwtBW'),
456
+ headerTooltip: 'Atoll BW | Network BW',
457
+ },
394
458
  // Position columns
395
459
  {
396
460
  title: 'Latitude',
@@ -469,7 +533,7 @@ export function getColumnsForPreset(preset, techColors = DEFAULT_TECH_COLORS, st
469
533
  let visibleFields;
470
534
  switch (preset) {
471
535
  case 'compact':
472
- visibleFields = ['cellName', 'siteId', 'tech', 'fband', 'status'];
536
+ visibleFields = ['siteId', 'txId', 'cellName', 'tech', 'fband', 'status'];
473
537
  break;
474
538
  case 'full':
475
539
  return allColumns;
@@ -482,10 +546,13 @@ export function getColumnsForPreset(preset, techColors = DEFAULT_TECH_COLORS, st
482
546
  case 'planning':
483
547
  visibleFields = [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.planning];
484
548
  break;
549
+ case 'compare':
550
+ visibleFields = [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.compare];
551
+ break;
485
552
  case 'default':
486
553
  default:
487
554
  visibleFields = [
488
- 'id', 'cellName', 'siteId', 'tech', 'fband', 'frq', 'status',
555
+ 'siteId', 'txId', 'cellName', 'tech', 'fband', 'frq', 'status',
489
556
  'azimuth', 'height', 'antenna'
490
557
  ];
491
558
  }
@@ -517,9 +584,9 @@ export function getGroupHeaderFormatter(groupField) {
517
584
  export function getColumnMetadata() {
518
585
  return [
519
586
  // Core
520
- { field: 'id', title: 'ID', group: 'Core' },
521
- { field: 'cellName', title: 'Cell Name', group: 'Core' },
522
587
  { field: 'siteId', title: 'Site ID', group: 'Core' },
588
+ { field: 'txId', title: 'TX ID', group: 'Core' },
589
+ { field: 'cellName', title: 'Cell Name', group: 'Core' },
523
590
  { field: 'tech', title: 'Technology', group: 'Core' },
524
591
  { field: 'fband', title: 'Band', group: 'Core' },
525
592
  { field: 'frq', title: 'Frequency', group: 'Core' },
@@ -539,7 +606,6 @@ export function getColumnMetadata() {
539
606
  { field: 'rru', title: 'RRU', group: 'Network' },
540
607
  { field: 'cellID', title: 'Cell ID', group: 'Network' },
541
608
  { field: 'cellId2G', title: 'Cell ID 2G', group: 'Network' },
542
- { field: 'txId', title: 'TX ID', group: 'Network' },
543
609
  { field: 'ctrlid', title: 'Ctrl ID', group: 'Network' },
544
610
  { field: 'nwtET', title: 'NWT ET', group: 'Network' },
545
611
  { field: 'nwtPW', title: 'NWT PW', group: 'Network' },
@@ -550,6 +616,11 @@ export function getColumnMetadata() {
550
616
  { field: 'atollPW', title: 'Atoll PW', group: 'Atoll' },
551
617
  { field: 'atollRS', title: 'Atoll RS', group: 'Atoll' },
552
618
  { field: 'atollBW', title: 'Atoll BW', group: 'Atoll' },
619
+ // Compare (Atoll vs Network)
620
+ { field: 'compareET', title: 'Δ ET', group: 'Compare' },
621
+ { field: 'comparePW', title: 'Δ PW', group: 'Compare' },
622
+ { field: 'compareRS', title: 'Δ RS', group: 'Compare' },
623
+ { field: 'compareBW', title: 'Δ BW', group: 'Compare' },
553
624
  // Position
554
625
  { field: 'latitude', title: 'Latitude', group: 'Position' },
555
626
  { field: 'longitude', title: 'Longitude', group: 'Position' },
@@ -569,7 +640,7 @@ export function getColumnMetadata() {
569
640
  export function getPresetVisibleFields(preset) {
570
641
  switch (preset) {
571
642
  case 'compact':
572
- return ['cellName', 'siteId', 'tech', 'fband', 'status'];
643
+ return ['siteId', 'txId', 'cellName', 'tech', 'fband', 'status'];
573
644
  case 'full':
574
645
  return getColumnMetadata().map(c => c.field);
575
646
  case 'physical':
@@ -578,8 +649,10 @@ export function getPresetVisibleFields(preset) {
578
649
  return [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.network];
579
650
  case 'planning':
580
651
  return [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.planning];
652
+ case 'compare':
653
+ return [...COLUMN_GROUPS.core, ...COLUMN_GROUPS.compare];
581
654
  case 'default':
582
655
  default:
583
- return ['id', 'cellName', 'siteId', 'tech', 'fband', 'frq', 'status', 'azimuth', 'height', 'antenna'];
656
+ return ['siteId', 'txId', 'cellName', 'tech', 'fband', 'frq', 'status', 'azimuth', 'height', 'antenna'];
584
657
  }
585
658
  }
@@ -16,7 +16,7 @@ export type CellTableGroupField = CellGroupingField | 'none';
16
16
  /**
17
17
  * Column preset configurations
18
18
  */
19
- export type ColumnPreset = 'default' | 'compact' | 'full' | 'physical' | 'network' | 'planning';
19
+ export type ColumnPreset = 'default' | 'compact' | 'full' | 'physical' | 'network' | 'planning' | 'compare';
20
20
  /**
21
21
  * Column visibility configuration
22
22
  */
@@ -122,4 +122,5 @@ export interface ColumnGroups {
122
122
  planning: string[];
123
123
  atoll: string[];
124
124
  position: string[];
125
+ compare: string[];
125
126
  }
@@ -39,12 +39,12 @@ export interface Cell {
39
39
  atollPW?: number;
40
40
  atollRS?: number;
41
41
  atollBW?: number;
42
- rru: string;
43
42
  nwtET?: number;
44
43
  nwtPW?: number;
45
- pci?: number;
46
44
  nwtRS?: number;
47
45
  nwtBW?: number;
46
+ pci?: number;
47
+ rru: string;
48
48
  other?: Record<string, unknown>;
49
49
  customSubgroup: string;
50
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartnet360/svelte-components",
3
- "version": "0.0.110",
3
+ "version": "0.0.112",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",