@smartnet360/svelte-components 0.0.123 → 0.0.125

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 (36) hide show
  1. package/dist/apps/antenna-tools/components/AntennaControls.svelte +71 -9
  2. package/dist/apps/antenna-tools/components/AntennaControls.svelte.d.ts +2 -0
  3. package/dist/apps/antenna-tools/components/AntennaSettingsModal.svelte +4 -174
  4. package/dist/apps/antenna-tools/components/AntennaTools.svelte +48 -82
  5. package/dist/apps/antenna-tools/components/DatabaseViewer.svelte +5 -8
  6. package/dist/apps/antenna-tools/components/MSIConverter.svelte +377 -52
  7. package/dist/apps/antenna-tools/db.js +4 -0
  8. package/dist/apps/antenna-tools/utils/db-utils.d.ts +19 -0
  9. package/dist/apps/antenna-tools/utils/db-utils.js +108 -0
  10. package/dist/apps/antenna-tools/utils/msi-parser.d.ts +35 -1
  11. package/dist/apps/antenna-tools/utils/msi-parser.js +105 -35
  12. package/dist/core/Auth/LoginForm.svelte +397 -0
  13. package/dist/core/Auth/LoginForm.svelte.d.ts +16 -0
  14. package/dist/core/Auth/auth.svelte.d.ts +22 -0
  15. package/dist/core/Auth/auth.svelte.js +184 -0
  16. package/dist/core/Auth/config.d.ts +25 -0
  17. package/dist/core/Auth/config.js +256 -0
  18. package/dist/core/Auth/index.d.ts +4 -0
  19. package/dist/core/Auth/index.js +5 -0
  20. package/dist/core/Auth/types.d.ts +140 -0
  21. package/dist/core/Auth/types.js +2 -0
  22. package/dist/core/Benchmark/Benchmark.svelte +662 -0
  23. package/dist/core/Benchmark/Benchmark.svelte.d.ts +3 -0
  24. package/dist/core/Benchmark/benchmark-utils.d.ts +48 -0
  25. package/dist/core/Benchmark/benchmark-utils.js +80 -0
  26. package/dist/core/Benchmark/index.d.ts +2 -0
  27. package/dist/core/Benchmark/index.js +3 -0
  28. package/dist/core/LandingPage/App.svelte +102 -0
  29. package/dist/core/LandingPage/App.svelte.d.ts +20 -0
  30. package/dist/core/LandingPage/LandingPage.svelte +480 -0
  31. package/dist/core/LandingPage/LandingPage.svelte.d.ts +21 -0
  32. package/dist/core/LandingPage/index.d.ts +2 -0
  33. package/dist/core/LandingPage/index.js +3 -0
  34. package/dist/core/index.d.ts +3 -0
  35. package/dist/core/index.js +6 -0
  36. package/package.json +1 -1
@@ -15,6 +15,8 @@
15
15
  onElectricalTiltChange?: (index: number) => void;
16
16
  onMechanicalTiltChange?: (tilt: number) => void;
17
17
  showStatus?: boolean;
18
+ /** Requested frequency from external mode - will be highlighted */
19
+ requestedFrequency?: number | null;
18
20
  }
19
21
 
20
22
  let {
@@ -27,7 +29,8 @@
27
29
  onAntennaChange,
28
30
  onElectricalTiltChange,
29
31
  onMechanicalTiltChange,
30
- showStatus = true
32
+ showStatus = true,
33
+ requestedFrequency = null
31
34
  }: Props = $props();
32
35
 
33
36
  // Internal state for slider values
@@ -36,6 +39,20 @@
36
39
  let internalSelectedAntenna = $state(selectedAntenna);
37
40
  let selectedFrequency = $state<number | null>(null);
38
41
 
42
+ // Check if requested frequency is available for the selected antenna
43
+ let frequencyError = $derived.by(() => {
44
+ if (!requestedFrequency || !internalSelectedAntenna) return null;
45
+
46
+ // Get all frequencies available for this antenna
47
+ const sameNameAntennas = antennas.filter(a => a.name === internalSelectedAntenna!.name);
48
+ const availableFreqs = [...new Set(sameNameAntennas.map(a => a.frequency))];
49
+
50
+ if (!availableFreqs.includes(requestedFrequency)) {
51
+ return `Requested frequency ${requestedFrequency} MHz is not available for this antenna. Available: ${availableFreqs.sort((a, b) => a - b).join(', ')} MHz`;
52
+ }
53
+ return null;
54
+ });
55
+
39
56
  // Update internal state when props change
40
57
  $effect(() => {
41
58
  internalElectricalTiltIndex = electricalTiltIndex;
@@ -305,18 +322,36 @@
305
322
  return colors[theme];
306
323
  }
307
324
 
325
+ // Type for frequency info with original frequency
326
+ interface FrequencyInfo {
327
+ band: number;
328
+ originalFrequency: number;
329
+ }
330
+
308
331
  // Get unique frequency bands for the selected antenna name
309
- let uniqueFrequencies = $state<number[]>([]);
332
+ let uniqueFrequencies = $state<FrequencyInfo[]>([]);
310
333
  $effect(() => {
311
334
  if (antennas.length > 0 && internalSelectedAntenna) {
312
335
  // Get frequencies only for the selected antenna name
313
336
  const sameNameAntennas = antennas.filter(a => a.name === internalSelectedAntenna!.name);
314
- const frequencies = [...new Set(sameNameAntennas.map(a => a.frequency))].sort((a, b) => a - b);
337
+
338
+ // Create unique frequency entries with original frequency
339
+ const freqMap = new Map<number, number>();
340
+ sameNameAntennas.forEach(a => {
341
+ // Keep the first original frequency for each band
342
+ if (!freqMap.has(a.frequency)) {
343
+ freqMap.set(a.frequency, a.originalFrequency);
344
+ }
345
+ });
346
+
347
+ const frequencies = Array.from(freqMap.entries())
348
+ .map(([band, orig]) => ({ band, originalFrequency: orig }))
349
+ .sort((a, b) => a.band - b.band);
315
350
  uniqueFrequencies = frequencies;
316
351
 
317
352
  // Auto-select frequency if there's only one option
318
353
  if (frequencies.length === 1) {
319
- selectedFrequency = frequencies[0];
354
+ selectedFrequency = frequencies[0].band;
320
355
  }
321
356
  } else {
322
357
  uniqueFrequencies = [];
@@ -348,10 +383,14 @@
348
383
  }
349
384
  });
350
385
 
351
- // Format frequency for display (700, 800 -> "700 MHz", "800 MHz")
386
+ // Format frequency for display (band [original] -> "700 [712]")
352
387
  function formatFrequency(freq: number): string {
353
388
  return `${freq} MHz`;
354
389
  }
390
+
391
+ function formatFrequencyWithOriginal(info: FrequencyInfo): string {
392
+ return `${info.band} [${info.originalFrequency}]`;
393
+ }
355
394
  </script>
356
395
 
357
396
  <div class="card h-100">
@@ -396,24 +435,47 @@
396
435
  {/if}
397
436
  </div>
398
437
 
438
+ <!-- Frequency Error Message -->
439
+ {#if frequencyError}
440
+ <div class="alert alert-danger py-2 mb-3" role="alert">
441
+ <i class="bi bi-exclamation-triangle-fill me-2"></i>
442
+ <small>{frequencyError}</small>
443
+ </div>
444
+ {/if}
445
+
399
446
  <!-- Frequency Band Filter -->
400
447
  {#if uniqueFrequencies.length > 1}
401
448
  <div class="mb-3">
402
449
  <div class="form-label">
403
450
  <strong>Frequency Band:</strong>
451
+ {#if requestedFrequency && !frequencyError}
452
+ <span class="badge bg-info ms-2">Requested: {requestedFrequency} MHz</span>
453
+ {/if}
404
454
  </div>
405
455
  <div class="d-flex flex-wrap gap-1">
406
- {#each uniqueFrequencies as freq}
456
+ {#each uniqueFrequencies as freqInfo}
457
+ {@const isRequested = requestedFrequency === freqInfo.band}
458
+ {@const isSelected = selectedFrequency === freqInfo.band}
407
459
  <button
408
460
  type="button"
409
- class="btn btn-sm {selectedFrequency === freq ? 'btn-primary' : 'btn-outline-secondary'}"
410
- onclick={() => handleFrequencyChange(freq)}
461
+ class="btn btn-sm {isSelected ? 'btn-primary' : isRequested ? 'btn-outline-info border-2' : 'btn-outline-secondary'}"
462
+ class:fw-bold={isRequested}
463
+ onclick={() => handleFrequencyChange(freqInfo.band)}
411
464
  >
412
- {formatFrequency(freq)}
465
+ {formatFrequencyWithOriginal(freqInfo)}
466
+ {#if isRequested && !isSelected}
467
+ <i class="bi bi-arrow-left-short"></i>
468
+ {/if}
413
469
  </button>
414
470
  {/each}
415
471
  </div>
416
472
  </div>
473
+ {:else if uniqueFrequencies.length === 1 && requestedFrequency && uniqueFrequencies[0].band !== requestedFrequency}
474
+ <!-- Single frequency but doesn't match requested -->
475
+ <div class="alert alert-warning py-2 mb-3" role="alert">
476
+ <i class="bi bi-exclamation-triangle-fill me-2"></i>
477
+ <small>Requested {requestedFrequency} MHz, but only {uniqueFrequencies[0].band} MHz available</small>
478
+ </div>
417
479
  {/if}
418
480
 
419
481
  <!-- Electrical Tilt Control -->
@@ -10,6 +10,8 @@ interface Props {
10
10
  onElectricalTiltChange?: (index: number) => void;
11
11
  onMechanicalTiltChange?: (tilt: number) => void;
12
12
  showStatus?: boolean;
13
+ /** Requested frequency from external mode - will be highlighted */
14
+ requestedFrequency?: number | null;
13
15
  }
14
16
  declare const AntennaControls: import("svelte").Component<Props, {}, "">;
15
17
  type AntennaControls = ReturnType<typeof AntennaControls>;
@@ -1,11 +1,8 @@
1
1
  <svelte:options runes={true} />
2
2
 
3
3
  <script lang="ts">
4
- import JsonImporter from './JsonImporter.svelte';
5
4
  import MSIConverter from './MSIConverter.svelte';
6
- import DbNotification from './DbNotification.svelte';
7
5
  import DatabaseViewer from './DatabaseViewer.svelte';
8
- import { exportAntennas, clearAllAntennas } from '../utils/db-utils';
9
6
  import type { BandDefinition } from '../types';
10
7
 
11
8
  interface Props {
@@ -18,42 +15,12 @@
18
15
 
19
16
  let { show, onClose, onDataRefresh, customBands }: Props = $props();
20
17
 
21
- let activeTab = $state<'view' | 'import' | 'msi' | 'export' | 'manage'>('view');
22
- let showClearConfirm = $state(false);
18
+ let activeTab = $state<'view' | 'manage'>('view');
23
19
 
24
20
  // Handle data refresh after import operations
25
21
  function handleDataRefresh() {
26
22
  onDataRefresh?.();
27
23
  }
28
-
29
- // Export data as JSON
30
- async function handleExport() {
31
- try {
32
- await exportAntennas();
33
- } catch (error) {
34
- console.error('Export failed:', error);
35
- }
36
- }
37
-
38
- // Clear all data with confirmation
39
- async function handleClearData() {
40
- if (!showClearConfirm) {
41
- showClearConfirm = true;
42
- return;
43
- }
44
-
45
- try {
46
- await clearAllAntennas();
47
- showClearConfirm = false;
48
- handleDataRefresh();
49
- } catch (error) {
50
- console.error('Clear data failed:', error);
51
- }
52
- }
53
-
54
- function cancelClear() {
55
- showClearConfirm = false;
56
- }
57
24
  </script>
58
25
 
59
26
  <!-- Modal -->
@@ -91,33 +58,12 @@
91
58
  >
92
59
  <i class="bi bi-table me-2"></i>View Data
93
60
  </button>
94
- <button
95
- class="nav-link {activeTab === 'import' ? 'active' : ''}"
96
- type="button"
97
- onclick={() => activeTab = 'import'}
98
- >
99
- <i class="bi bi-upload me-2"></i>Import JSON
100
- </button>
101
- <button
102
- class="nav-link {activeTab === 'msi' ? 'active' : ''}"
103
- type="button"
104
- onclick={() => activeTab = 'msi'}
105
- >
106
- <i class="bi bi-file-earmark-text me-2"></i>Import MSI
107
- </button>
108
- <button
109
- class="nav-link {activeTab === 'export' ? 'active' : ''}"
110
- type="button"
111
- onclick={() => activeTab = 'export'}
112
- >
113
- <i class="bi bi-download me-2"></i>Export Data
114
- </button>
115
61
  <button
116
62
  class="nav-link {activeTab === 'manage' ? 'active' : ''}"
117
63
  type="button"
118
64
  onclick={() => activeTab = 'manage'}
119
65
  >
120
- <i class="bi bi-trash me-2"></i>Manage Data
66
+ <i class="bi bi-database-gear me-2"></i>Manage Database
121
67
  </button>
122
68
  </div>
123
69
  </div>
@@ -132,118 +78,10 @@
132
78
  </div>
133
79
  {/if}
134
80
 
135
- <!-- Import JSON Tab -->
136
- {#if activeTab === 'import'}
137
- <div class="tab-pane fade show active">
138
- <div class="row">
139
- <div class="col-12">
140
- <h5 class="mb-3">Import Antenna Data from JSON</h5>
141
- <JsonImporter onDataRefresh={handleDataRefresh} />
142
- </div>
143
- </div>
144
- </div>
145
- {/if}
146
-
147
- <!-- Import MSI Tab -->
148
- {#if activeTab === 'msi'}
149
- <div class="tab-pane fade show active">
150
- <div class="row">
151
- <div class="col-12">
152
- <h5 class="mb-3">Import MSI Pattern Files</h5>
153
- <MSIConverter onDataRefresh={handleDataRefresh} {customBands} />
154
- </div>
155
- </div>
156
- </div>
157
- {/if}
158
-
159
- <!-- Export Data Tab -->
160
- {#if activeTab === 'export'}
161
- <div class="tab-pane fade show active">
162
- <div class="row">
163
- <div class="col-12">
164
- <h5 class="mb-3">Export Antenna Data</h5>
165
-
166
- <div class="card border-success">
167
- <div class="card-body text-center">
168
- <i class="bi bi-download text-success" style="font-size: 3rem;"></i>
169
- <h6 class="mt-3 mb-3">Download Data Backup</h6>
170
- <p class="text-muted mb-4">
171
- Export all antenna patterns and metadata as a JSON file.
172
- </p>
173
- <button
174
- type="button"
175
- class="btn btn-success btn-lg"
176
- onclick={handleExport}
177
- >
178
- <i class="bi bi-download me-2"></i>
179
- Export JSON File
180
- </button>
181
- </div>
182
- </div>
183
- </div>
184
- </div>
185
- </div>
186
- {/if}
187
-
188
- <!-- Manage Data Tab -->
81
+ <!-- Manage Database Tab -->
189
82
  {#if activeTab === 'manage'}
190
83
  <div class="tab-pane fade show active">
191
- <div class="row">
192
- <div class="col-12">
193
- <h5 class="mb-3">Manage Database</h5>
194
-
195
- <!-- Database Status -->
196
- <div class="card border-info mb-4">
197
- <div class="card-body">
198
- <h6 class="card-title">
199
- <i class="bi bi-info-circle me-2"></i>Database Status
200
- </h6>
201
- <DbNotification />
202
- </div>
203
- </div>
204
-
205
- <!-- Clear Data Section -->
206
- <div class="card border-danger">
207
- <div class="card-body">
208
- <p class="text-muted mb-3">
209
- Permanently delete all antenna data from the database.
210
- </p>
211
-
212
- {#if !showClearConfirm}
213
- <button
214
- type="button"
215
- class="btn btn-outline-danger"
216
- onclick={handleClearData}
217
- >
218
- <i class="bi bi-trash me-2"></i>
219
- Clear All Data
220
- </button>
221
- {:else}
222
- <div class="alert alert-danger" role="alert">
223
- <strong>Are you sure?</strong> This will permanently delete all antenna data.
224
- </div>
225
- <div class="d-flex gap-2">
226
- <button
227
- type="button"
228
- class="btn btn-danger"
229
- onclick={handleClearData}
230
- >
231
- <i class="bi bi-trash me-2"></i>
232
- Yes, Delete All Data
233
- </button>
234
- <button
235
- type="button"
236
- class="btn btn-secondary"
237
- onclick={cancelClear}
238
- >
239
- Cancel
240
- </button>
241
- </div>
242
- {/if}
243
- </div>
244
- </div>
245
- </div>
246
- </div>
84
+ <MSIConverter onDataRefresh={handleDataRefresh} {customBands} />
247
85
  </div>
248
86
  {/if}
249
87
 
@@ -296,12 +134,4 @@
296
134
  border-bottom-color: #0d6efd;
297
135
  background: none;
298
136
  }
299
-
300
- .card {
301
- transition: box-shadow 0.15s ease-in-out;
302
- }
303
-
304
- .card:hover {
305
- box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
306
- }
307
137
  </style>
@@ -1,7 +1,7 @@
1
1
  <svelte:options runes={true} />
2
2
 
3
3
  <script lang="ts">
4
- import { onMount } from 'svelte';
4
+ import { onMount, untrack } from 'svelte';
5
5
  import { loadAntennas } from '../utils/db-utils';
6
6
  import type { Antenna, ExternalAntennaInput, ViewMode, PatternType, ChartEngineType, PatternDisplayMode, BandDefinition } from '../types';
7
7
  import AntennaControls from './AntennaControls.svelte';
@@ -110,12 +110,13 @@
110
110
  if (externalAntenna1) {
111
111
  const found = findAntennaBySpec(antennas, externalAntenna1);
112
112
  if (found) {
113
- selectedAntenna = found;
114
- // Update available tilts
115
- updateAvailableTilts(found);
116
- // Set tilts from external spec
117
- ant1ElectricalTilt = findTiltIndex(availableElectricalTilts, externalAntenna1.electricalTilt ?? 0);
118
- ant1MechanicalTilt = externalAntenna1.mechanicalTilt ?? 0;
113
+ // Use untrack to prevent infinite loop from state updates
114
+ untrack(() => {
115
+ selectedAntenna = found;
116
+ updateAvailableTilts(found);
117
+ ant1ElectricalTilt = findTiltIndex(availableElectricalTilts, externalAntenna1.electricalTilt ?? 0);
118
+ ant1MechanicalTilt = externalAntenna1.mechanicalTilt ?? 0;
119
+ });
119
120
  } else {
120
121
  console.warn('[AntennaTools] External antenna 1 not found in database:', externalAntenna1.name);
121
122
  }
@@ -125,13 +126,14 @@
125
126
  if (externalAntenna2) {
126
127
  const found2 = findAntennaBySpec(antennas, externalAntenna2);
127
128
  if (found2) {
128
- selectedAntenna2 = found2;
129
- ant2ElectricalTilt = findTiltIndex(getAvailableTiltsForAntenna(found2), externalAntenna2.electricalTilt ?? 0);
130
- ant2MechanicalTilt = externalAntenna2.mechanicalTilt ?? 0;
131
- // Auto-switch to compare mode if antenna 2 is provided
132
- if (viewMode === 'single') {
133
- viewMode = 'compare';
134
- }
129
+ untrack(() => {
130
+ selectedAntenna2 = found2;
131
+ ant2ElectricalTilt = findTiltIndex(getAvailableTiltsForAntenna(found2), externalAntenna2.electricalTilt ?? 0);
132
+ ant2MechanicalTilt = externalAntenna2.mechanicalTilt ?? 0;
133
+ if (viewMode === 'single') {
134
+ viewMode = 'compare';
135
+ }
136
+ });
135
137
  }
136
138
  }
137
139
  }
@@ -455,40 +457,22 @@
455
457
  <div class="row">
456
458
  <!-- Left Column - Antenna 1 Controls -->
457
459
  <div class="col-md-3">
458
- {#if !isExternalMode}
459
- <AntennaControls
460
- antennas={antennas}
461
- selectedAntenna={selectedAntenna}
462
- antennaNumber={1}
463
- electricalTiltIndex={ant1ElectricalTilt}
464
- mechanicalTilt={ant1MechanicalTilt}
465
- colorTheme="primary"
466
- onAntennaChange={handleAntenna1Change}
467
- onElectricalTiltChange={handleTilt1Change}
468
- onMechanicalTiltChange={handleMechTilt1Change}
469
- />
470
- {:else}
471
- <!-- External mode: Show antenna info card -->
472
- <div class="card border-primary">
473
- <div class="card-header bg-primary text-white">
474
- <i class="bi bi-broadcast me-2"></i>
475
- {antenna1Label || 'Antenna 1'}
476
- </div>
477
- <div class="card-body">
478
- {#if selectedAntenna}
479
- <h6 class="card-title">{selectedAntenna.name}</h6>
480
- <div class="small text-muted">
481
- <div><strong>Band:</strong> {selectedAntenna.frequency}</div>
482
- <div><strong>E-Tilt:</strong> {availableElectricalTilts[ant1ElectricalTilt] || 0}°</div>
483
- <div><strong>M-Tilt:</strong> {ant1MechanicalTilt}°</div>
484
- {#if selectedAntenna.gain_dBd}
485
- <div><strong>Gain:</strong> {selectedAntenna.gain_dBd} dBd</div>
486
- {/if}
487
- </div>
488
- {:else}
489
- <p class="text-muted mb-0">Antenna not found in database</p>
490
- {/if}
491
- </div>
460
+ <!-- Show full controls in both modes - external mode just pre-selects the antenna -->
461
+ <AntennaControls
462
+ antennas={antennas}
463
+ selectedAntenna={selectedAntenna}
464
+ antennaNumber={1}
465
+ electricalTiltIndex={ant1ElectricalTilt}
466
+ mechanicalTilt={ant1MechanicalTilt}
467
+ colorTheme="primary"
468
+ onAntennaChange={handleAntenna1Change}
469
+ onElectricalTiltChange={handleTilt1Change}
470
+ onMechanicalTiltChange={handleMechTilt1Change}
471
+ requestedFrequency={isExternalMode ? externalAntenna1?.frequency : null}
472
+ />
473
+ {#if isExternalMode && antenna1Label}
474
+ <div class="badge bg-primary w-100 mt-2 py-2">
475
+ <i class="bi bi-link-45deg me-1"></i>{antenna1Label}
492
476
  </div>
493
477
  {/if}
494
478
  </div>
@@ -531,40 +515,22 @@
531
515
  <!-- Right Column - Antenna 2 Controls (only in compare mode) -->
532
516
  <div class="col-md-3">
533
517
  {#if viewMode === 'compare'}
534
- {#if !isExternalMode}
535
- <AntennaControls
536
- antennas={antennas}
537
- selectedAntenna={selectedAntenna2}
538
- antennaNumber={2}
539
- electricalTiltIndex={ant2ElectricalTilt}
540
- mechanicalTilt={ant2MechanicalTilt}
541
- colorTheme="warning"
542
- onAntennaChange={handleAntenna2Change}
543
- onElectricalTiltChange={handleTilt2Change}
544
- onMechanicalTiltChange={handleMechTilt2Change}
545
- />
546
- {:else}
547
- <!-- External mode: Show antenna 2 info card -->
548
- <div class="card border-warning">
549
- <div class="card-header bg-warning text-dark">
550
- <i class="bi bi-broadcast me-2"></i>
551
- {antenna2Label || 'Antenna 2'}
552
- </div>
553
- <div class="card-body">
554
- {#if selectedAntenna2}
555
- <h6 class="card-title">{selectedAntenna2.name}</h6>
556
- <div class="small text-muted">
557
- <div><strong>Band:</strong> {selectedAntenna2.frequency}</div>
558
- <div><strong>E-Tilt:</strong> {getAvailableTiltsForAntenna(selectedAntenna2)[ant2ElectricalTilt] || 0}°</div>
559
- <div><strong>M-Tilt:</strong> {ant2MechanicalTilt}°</div>
560
- {#if selectedAntenna2.gain_dBd}
561
- <div><strong>Gain:</strong> {selectedAntenna2.gain_dBd} dBd</div>
562
- {/if}
563
- </div>
564
- {:else}
565
- <p class="text-muted mb-0">Antenna not found in database</p>
566
- {/if}
567
- </div>
518
+ <!-- Show full controls in both modes -->
519
+ <AntennaControls
520
+ antennas={antennas}
521
+ selectedAntenna={selectedAntenna2}
522
+ antennaNumber={2}
523
+ electricalTiltIndex={ant2ElectricalTilt}
524
+ mechanicalTilt={ant2MechanicalTilt}
525
+ colorTheme="warning"
526
+ onAntennaChange={handleAntenna2Change}
527
+ onElectricalTiltChange={handleTilt2Change}
528
+ onMechanicalTiltChange={handleMechTilt2Change}
529
+ requestedFrequency={isExternalMode ? externalAntenna2?.frequency : null}
530
+ />
531
+ {#if isExternalMode && antenna2Label}
532
+ <div class="badge bg-warning text-dark w-100 mt-2 py-2">
533
+ <i class="bi bi-link-45deg me-1"></i>{antenna2Label}
568
534
  </div>
569
535
  {/if}
570
536
  {:else}
@@ -134,8 +134,8 @@
134
134
  />
135
135
  </div>
136
136
  <div class="col-md-5">
137
- <label class="form-label">Filter by Band</label>
138
- <div class="btn-group w-100 flex-wrap" role="group">
137
+ <span class="form-label d-block">Filter by Band</span>
138
+ <div class="btn-group w-100 flex-wrap" role="group" aria-label="Filter by frequency band">
139
139
  <button
140
140
  type="button"
141
141
  class="btn btn-sm {selectedBand === null ? 'btn-primary' : 'btn-outline-primary'}"
@@ -187,8 +187,7 @@
187
187
  <thead class="table-light sticky-top">
188
188
  <tr>
189
189
  <th>Model Name</th>
190
- <th class="text-center" style="width: 80px;">Band</th>
191
- <th class="text-center" style="width: 100px;">Orig. Freq</th>
190
+ <th class="text-center" style="width: 120px;">Band [Orig]</th>
192
191
  <th class="text-center" style="width: 70px;">Tilt</th>
193
192
  <th class="text-center" style="width: 80px;">Gain</th>
194
193
  <th class="text-center" style="width: 60px;">H-Pts</th>
@@ -206,9 +205,7 @@
206
205
  </td>
207
206
  <td class="text-center">
208
207
  <span class="badge bg-primary">{antenna.frequency}</span>
209
- </td>
210
- <td class="text-center text-muted">
211
- {antenna.originalFrequency} MHz
208
+ <span class="text-muted small">[{antenna.originalFrequency}]</span>
212
209
  </td>
213
210
  <td class="text-center">
214
211
  {antenna.tilt}°
@@ -225,7 +222,7 @@
225
222
  </tr>
226
223
  {:else}
227
224
  <tr>
228
- <td colspan="7" class="text-center text-muted py-4">
225
+ <td colspan="6" class="text-center text-muted py-4">
229
226
  No antennas match your search criteria
230
227
  </td>
231
228
  </tr>