@smartnet360/svelte-components 0.0.11 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps/antenna-pattern/components/AntennaControls.svelte +424 -0
- package/dist/apps/antenna-pattern/components/AntennaControls.svelte.d.ts +16 -0
- package/dist/apps/antenna-pattern/components/AntennaDataDropdown.svelte +62 -0
- package/dist/apps/antenna-pattern/components/AntennaDataDropdown.svelte.d.ts +18 -0
- package/dist/apps/antenna-pattern/components/AntennaDiagrams.svelte +339 -0
- package/dist/apps/antenna-pattern/components/AntennaDiagrams.svelte.d.ts +3 -0
- package/dist/apps/antenna-pattern/components/AntennaSettingsModal.svelte +299 -0
- package/dist/apps/antenna-pattern/components/AntennaSettingsModal.svelte.d.ts +24 -0
- package/dist/apps/antenna-pattern/components/DbNotification.svelte +67 -0
- package/dist/apps/antenna-pattern/components/DbNotification.svelte.d.ts +18 -0
- package/dist/apps/antenna-pattern/components/JsonImporter.svelte +116 -0
- package/dist/apps/antenna-pattern/components/JsonImporter.svelte.d.ts +18 -0
- package/dist/apps/antenna-pattern/components/MSIConverter.svelte +209 -0
- package/dist/apps/antenna-pattern/components/MSIConverter.svelte.d.ts +18 -0
- package/dist/apps/antenna-pattern/components/PlotlyRadarChart.svelte +252 -0
- package/dist/apps/antenna-pattern/components/PlotlyRadarChart.svelte.d.ts +22 -0
- package/dist/apps/antenna-pattern/db.d.ts +24 -0
- package/dist/apps/antenna-pattern/db.js +15 -0
- package/dist/apps/antenna-pattern/helpers/plotly-utils.d.ts +54 -0
- package/dist/apps/antenna-pattern/helpers/plotly-utils.js +324 -0
- package/dist/apps/antenna-pattern/index.d.ts +15 -0
- package/dist/apps/antenna-pattern/index.js +19 -0
- package/dist/apps/antenna-pattern/stores/antennas.d.ts +5 -0
- package/dist/apps/antenna-pattern/stores/antennas.js +14 -0
- package/dist/apps/antenna-pattern/stores/db-status.d.ts +28 -0
- package/dist/apps/antenna-pattern/stores/db-status.js +34 -0
- package/dist/apps/antenna-pattern/utils/db-utils.d.ts +9 -0
- package/dist/apps/antenna-pattern/utils/db-utils.js +180 -0
- package/dist/apps/antenna-pattern/utils/init-db.d.ts +2 -0
- package/dist/apps/antenna-pattern/utils/init-db.js +95 -0
- package/dist/apps/antenna-pattern/utils/msi-parser.d.ts +3 -0
- package/dist/apps/antenna-pattern/utils/msi-parser.js +197 -0
- package/dist/apps/antenna-pattern/utils/plotly-chart-utils.d.ts +101 -0
- package/dist/apps/antenna-pattern/utils/plotly-chart-utils.js +152 -0
- package/dist/apps/index.d.ts +1 -0
- package/dist/apps/index.js +6 -0
- package/dist/{Charts → core/Charts}/ChartComponent.svelte +131 -39
- package/dist/{Charts → core/Charts}/charts.model.d.ts +1 -1
- package/dist/{Desktop → core/Desktop}/GridRenderer.svelte +1 -1
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +6 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -2
- package/package.json +6 -2
- /package/dist/{Charts → core/Charts}/ChartCard.svelte +0 -0
- /package/dist/{Charts → core/Charts}/ChartCard.svelte.d.ts +0 -0
- /package/dist/{Charts → core/Charts}/ChartComponent.svelte.d.ts +0 -0
- /package/dist/{Charts → core/Charts}/adapt.d.ts +0 -0
- /package/dist/{Charts → core/Charts}/adapt.js +0 -0
- /package/dist/{Charts → core/Charts}/charts.model.js +0 -0
- /package/dist/{Charts → core/Charts}/data-utils.d.ts +0 -0
- /package/dist/{Charts → core/Charts}/data-utils.js +0 -0
- /package/dist/{Charts → core/Charts}/index.d.ts +0 -0
- /package/dist/{Charts → core/Charts}/index.js +0 -0
- /package/dist/{Charts → core/Charts}/plotly.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Desktop.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/Desktop.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/Half.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/Half.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/Quarter.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/Quarter.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/ResizeHandle.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/ResizeHandle.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/index.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/index.js +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/resizeStore.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/resizeStore.js +0 -0
- /package/dist/{Desktop → core/Desktop}/GridRenderer.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/ComponentPalette.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/ComponentPalette.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/ConfigurationPanel.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/ConfigurationPanel.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/GridSelector.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/GridSelector.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPicker.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPicker.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPreview.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPreview.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/index.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/index.js +0 -0
- /package/dist/{Desktop → core/Desktop}/GridViewer.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridViewer.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/gridLayouts.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/gridLayouts.js +0 -0
- /package/dist/{Desktop → core/Desktop}/index.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/index.js +0 -0
- /package/dist/{Desktop → core/Desktop}/launchHelpers.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/launchHelpers.js +0 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import { fade, fly } from 'svelte/transition';
|
3
|
+
import { dataOperationStatus } from '../stores/db-status';
|
4
|
+
import { onDestroy } from 'svelte';
|
5
|
+
|
6
|
+
let visible = false;
|
7
|
+
let message = '';
|
8
|
+
let type: 'success' | 'error' | 'info' = 'info';
|
9
|
+
let timer: number;
|
10
|
+
|
11
|
+
const unsubscribe = dataOperationStatus.subscribe(status => {
|
12
|
+
if (!status.operation) return;
|
13
|
+
|
14
|
+
// Clear any existing timer
|
15
|
+
if (timer) clearTimeout(timer);
|
16
|
+
|
17
|
+
if (status.inProgress) {
|
18
|
+
visible = true;
|
19
|
+
message = status.message || `${status.operation} in progress...`;
|
20
|
+
type = 'info';
|
21
|
+
} else if (status.success) {
|
22
|
+
visible = true;
|
23
|
+
message = status.message || `${status.operation} completed successfully`;
|
24
|
+
type = 'success';
|
25
|
+
|
26
|
+
// Auto-hide success messages after 4 seconds
|
27
|
+
timer = setTimeout(() => {
|
28
|
+
visible = false;
|
29
|
+
}, 4000);
|
30
|
+
} else if (status.error) {
|
31
|
+
visible = true;
|
32
|
+
message = status.message || `${status.operation} failed: ${status.error}`;
|
33
|
+
type = 'error';
|
34
|
+
|
35
|
+
// Auto-hide error messages after 6 seconds
|
36
|
+
timer = setTimeout(() => {
|
37
|
+
visible = false;
|
38
|
+
}, 6000);
|
39
|
+
}
|
40
|
+
});
|
41
|
+
|
42
|
+
onDestroy(() => {
|
43
|
+
unsubscribe();
|
44
|
+
if (timer) clearTimeout(timer);
|
45
|
+
});
|
46
|
+
|
47
|
+
function dismiss() {
|
48
|
+
visible = false;
|
49
|
+
if (timer) clearTimeout(timer);
|
50
|
+
}
|
51
|
+
</script>
|
52
|
+
|
53
|
+
{#if visible}
|
54
|
+
<div class="toast-container position-fixed bottom-0 end-0 p-3" transition:fly={{ y: 32, duration: 200 }}>
|
55
|
+
<div
|
56
|
+
class={`toast show align-items-center text-bg-${type === 'error' ? 'danger' : type === 'success' ? 'success' : 'info'} border-0`}
|
57
|
+
role="alert"
|
58
|
+
aria-live="assertive"
|
59
|
+
aria-atomic="true"
|
60
|
+
>
|
61
|
+
<div class="d-flex">
|
62
|
+
<div class="toast-body">{message}</div>
|
63
|
+
<button type="button" class="btn-close btn-close-white me-2 m-auto" aria-label="Close" on:click={dismiss}></button>
|
64
|
+
</div>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
{/if}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
3
|
+
$$bindings?: Bindings;
|
4
|
+
} & Exports;
|
5
|
+
(internal: unknown, props: {
|
6
|
+
$$events?: Events;
|
7
|
+
$$slots?: Slots;
|
8
|
+
}): Exports & {
|
9
|
+
$set?: any;
|
10
|
+
$on?: any;
|
11
|
+
};
|
12
|
+
z_$$bindings?: Bindings;
|
13
|
+
}
|
14
|
+
declare const DbNotification: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
15
|
+
[evt: string]: CustomEvent<any>;
|
16
|
+
}, {}, {}, string>;
|
17
|
+
type DbNotification = InstanceType<typeof DbNotification>;
|
18
|
+
export default DbNotification;
|
@@ -0,0 +1,116 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import { importFromJson, hasImportedData } from '../utils/db-utils';
|
3
|
+
import type { Antenna } from '../db';
|
4
|
+
let fileInput: HTMLInputElement | null = null;
|
5
|
+
|
6
|
+
let isLoading = false;
|
7
|
+
let message = '';
|
8
|
+
let error = '';
|
9
|
+
let antennaCount = 0;
|
10
|
+
|
11
|
+
async function handleFileUpload(event: Event) {
|
12
|
+
const input = event.target as HTMLInputElement;
|
13
|
+
fileInput = input;
|
14
|
+
const files = input.files;
|
15
|
+
|
16
|
+
if (!files || files.length === 0) return;
|
17
|
+
|
18
|
+
const file = files[0];
|
19
|
+
if (file.type !== 'application/json') {
|
20
|
+
error = 'Please select a JSON file.';
|
21
|
+
return;
|
22
|
+
}
|
23
|
+
|
24
|
+
try {
|
25
|
+
isLoading = true;
|
26
|
+
error = '';
|
27
|
+
message = 'Importing data...';
|
28
|
+
|
29
|
+
const antennas = await importFromJson(file);
|
30
|
+
antennaCount = antennas.length;
|
31
|
+
|
32
|
+
message = `Successfully imported ${antennaCount} antennas into the database.`;
|
33
|
+
|
34
|
+
// Reset the file input
|
35
|
+
input.value = '';
|
36
|
+
} catch (e) {
|
37
|
+
if (e instanceof Error) {
|
38
|
+
error = `Error importing data: ${e.message}`;
|
39
|
+
} else {
|
40
|
+
error = 'An unknown error occurred during import.';
|
41
|
+
}
|
42
|
+
} finally {
|
43
|
+
isLoading = false;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
</script>
|
47
|
+
|
48
|
+
<div class="card border-0 shadow-sm">
|
49
|
+
<div class="card-body ">
|
50
|
+
|
51
|
+
{#if hasImportedData()}
|
52
|
+
<div class="alert alert-warning border-0 bg-warning-subtle text-warning-emphasis d-flex align-items-start gap-2" role="alert">
|
53
|
+
<i class="bi bi-exclamation-triangle"></i>
|
54
|
+
<div>
|
55
|
+
<strong>Existing data detected.</strong> The new file will overwrite your current antenna records.
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
{/if}
|
59
|
+
|
60
|
+
<div class="mb-4">
|
61
|
+
<label class="form-label fw-semibold" for="jsonFileInput">JSON file</label>
|
62
|
+
<input
|
63
|
+
id="jsonFileInput"
|
64
|
+
class="form-control form-control-lg"
|
65
|
+
type="file"
|
66
|
+
accept=".json"
|
67
|
+
on:change={handleFileUpload}
|
68
|
+
disabled={isLoading}
|
69
|
+
bind:this={fileInput}
|
70
|
+
/>
|
71
|
+
<div class="form-text">Choose a `.json` export created by this tool or generated from MSI conversions.</div>
|
72
|
+
</div>
|
73
|
+
|
74
|
+
<div class="d-flex align-items-center gap-3 mb-4">
|
75
|
+
<button class="btn btn-primary" type="button" on:click={() => fileInput && fileInput.click()} disabled={isLoading}>
|
76
|
+
<i class="bi bi-folder2-open me-2"></i>
|
77
|
+
Browse files
|
78
|
+
</button>
|
79
|
+
{#if isLoading}
|
80
|
+
<div class="d-flex align-items-center gap-2 text-muted">
|
81
|
+
<div class="spinner-border spinner-border-sm" role="status">
|
82
|
+
<span class="visually-hidden">Loading...</span>
|
83
|
+
</div>
|
84
|
+
<span>Importing data…</span>
|
85
|
+
</div>
|
86
|
+
{/if}
|
87
|
+
</div>
|
88
|
+
|
89
|
+
{#if message && !error}
|
90
|
+
<div class="alert alert-info border-0 bg-info-subtle text-info-emphasis d-flex align-items-start gap-2" role="alert">
|
91
|
+
<i class="bi bi-info-circle"></i>
|
92
|
+
<div>{message}</div>
|
93
|
+
</div>
|
94
|
+
{/if}
|
95
|
+
|
96
|
+
{#if error}
|
97
|
+
<div class="alert alert-danger border-0 bg-danger-subtle text-danger-emphasis d-flex align-items-start gap-2" role="alert">
|
98
|
+
<i class="bi bi-exclamation-octagon"></i>
|
99
|
+
<div>{error}</div>
|
100
|
+
</div>
|
101
|
+
{/if}
|
102
|
+
|
103
|
+
{#if antennaCount > 0 && !error}
|
104
|
+
<div class="alert alert-success border-0 bg-success-subtle text-success-emphasis" role="alert">
|
105
|
+
<div class="d-flex flex-column flex-sm-row gap-2 align-items-sm-center">
|
106
|
+
<div class="d-flex align-items-center gap-2">
|
107
|
+
<i class="bi bi-check-circle-fill"></i>
|
108
|
+
<span class="fw-semibold">Import complete</span>
|
109
|
+
</div>
|
110
|
+
<span class="badge text-bg-success">{antennaCount}</span>
|
111
|
+
<span class="text-muted">antenna{antennaCount === 1 ? '' : 's'} ready for viewing</span>
|
112
|
+
</div>
|
113
|
+
</div>
|
114
|
+
{/if}
|
115
|
+
</div>
|
116
|
+
</div>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
3
|
+
$$bindings?: Bindings;
|
4
|
+
} & Exports;
|
5
|
+
(internal: unknown, props: {
|
6
|
+
$$events?: Events;
|
7
|
+
$$slots?: Slots;
|
8
|
+
}): Exports & {
|
9
|
+
$set?: any;
|
10
|
+
$on?: any;
|
11
|
+
};
|
12
|
+
z_$$bindings?: Bindings;
|
13
|
+
}
|
14
|
+
declare const JsonImporter: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
15
|
+
[evt: string]: CustomEvent<any>;
|
16
|
+
}, {}, {}, string>;
|
17
|
+
type JsonImporter = InstanceType<typeof JsonImporter>;
|
18
|
+
export default JsonImporter;
|
@@ -0,0 +1,209 @@
|
|
1
|
+
<script lang="ts">
|
2
|
+
import { parseFolder } from '../utils/msi-parser';
|
3
|
+
import { exportToJson, saveAntennas } from '../utils/db-utils';
|
4
|
+
import type { Antenna } from '../db';
|
5
|
+
import { updateDbStatus } from '../stores/db-status';
|
6
|
+
import { goto } from '$app/navigation';
|
7
|
+
import { base } from '$app/paths';
|
8
|
+
|
9
|
+
let antennas: Antenna[] = [];
|
10
|
+
let isLoading = false;
|
11
|
+
let error = '';
|
12
|
+
let message = '';
|
13
|
+
|
14
|
+
let recursiveScan = true;
|
15
|
+
|
16
|
+
async function selectFolder() {
|
17
|
+
try {
|
18
|
+
// Check if File System Access API is supported
|
19
|
+
if (!('showDirectoryPicker' in window)) {
|
20
|
+
error = 'Your browser does not support the File System Access API. Please use a Chromium-based browser.';
|
21
|
+
return;
|
22
|
+
}
|
23
|
+
|
24
|
+
isLoading = true;
|
25
|
+
error = '';
|
26
|
+
message = 'Selecting folder...';
|
27
|
+
|
28
|
+
// Ask user to select a folder
|
29
|
+
const directoryPicker = window.showDirectoryPicker as () => Promise<FileSystemDirectoryHandle>;
|
30
|
+
const directoryHandle = await directoryPicker();
|
31
|
+
message = recursiveScan ?
|
32
|
+
'Reading and parsing files recursively (including all subfolders)...' :
|
33
|
+
'Reading and parsing files (top-level folder only)...';
|
34
|
+
|
35
|
+
// Parse all MSI/PNT files in the folder, with recursive option
|
36
|
+
antennas = await parseFolder(directoryHandle, recursiveScan);
|
37
|
+
|
38
|
+
if (antennas.length === 0) {
|
39
|
+
message = 'No MSI or PNT files found in the selected folder.';
|
40
|
+
} else {
|
41
|
+
message = `Successfully parsed ${antennas.length} antenna files${recursiveScan ? ' from all folders' : ''}.`;
|
42
|
+
}
|
43
|
+
} catch (e) {
|
44
|
+
if (e instanceof Error) {
|
45
|
+
if (e.name === 'AbortError') {
|
46
|
+
// User cancelled the folder picker
|
47
|
+
error = '';
|
48
|
+
message = 'Folder selection cancelled.';
|
49
|
+
} else {
|
50
|
+
error = `Error: ${e.message}`;
|
51
|
+
}
|
52
|
+
} else {
|
53
|
+
error = 'An unknown error occurred.';
|
54
|
+
}
|
55
|
+
} finally {
|
56
|
+
isLoading = false;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
async function saveToDatabase() {
|
61
|
+
if (antennas.length === 0) {
|
62
|
+
error = 'No antenna data to save.';
|
63
|
+
return;
|
64
|
+
}
|
65
|
+
|
66
|
+
try {
|
67
|
+
isLoading = true;
|
68
|
+
message = 'Saving to database...';
|
69
|
+
await saveAntennas(antennas);
|
70
|
+
message = `Successfully imported ${antennas.length} antennas to database.`;
|
71
|
+
|
72
|
+
// Navigate to view page after a short delay
|
73
|
+
setTimeout(() => goto(`${base}/apps/antenna-patterns/view`), 1500);
|
74
|
+
} catch (e) {
|
75
|
+
error = e instanceof Error ? e.message : 'Failed to save antennas to database';
|
76
|
+
} finally {
|
77
|
+
isLoading = false;
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
function downloadJson() {
|
82
|
+
if (antennas.length === 0) {
|
83
|
+
error = 'No antenna data to download.';
|
84
|
+
return;
|
85
|
+
}
|
86
|
+
|
87
|
+
exportToJson(antennas);
|
88
|
+
message = 'JSON file downloaded.';
|
89
|
+
}
|
90
|
+
</script>
|
91
|
+
|
92
|
+
<div class="card border-0 shadow-sm">
|
93
|
+
<div class="card-body">
|
94
|
+
<header class="d-flex flex-column flex-lg-row align-items-lg-center justify-content-between gap-3 mb-4">
|
95
|
+
<div>
|
96
|
+
<h1 class="h3 mb-2">Convert MSI / PNT to JSON</h1>
|
97
|
+
<p class="text-muted mb-0">Scan a folder of antenna definitions, review the results, then import them to the local database.</p>
|
98
|
+
</div>
|
99
|
+
<span class="badge text-bg-success">Local processing</span>
|
100
|
+
</header>
|
101
|
+
|
102
|
+
<div class="d-flex flex-column gap-4">
|
103
|
+
<div class="text-center">
|
104
|
+
<button class="btn btn-primary btn-lg px-4" on:click={selectFolder} disabled={isLoading}>
|
105
|
+
{#if isLoading}
|
106
|
+
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
107
|
+
Processing…
|
108
|
+
{:else}
|
109
|
+
<i class="bi bi-folder-plus me-2"></i>
|
110
|
+
Choose folder with MSI / PNT files
|
111
|
+
{/if}
|
112
|
+
</button>
|
113
|
+
</div>
|
114
|
+
|
115
|
+
<div class="d-flex justify-content-center">
|
116
|
+
<div class="form-check form-switch">
|
117
|
+
<input class="form-check-input" type="checkbox" bind:checked={recursiveScan} id="recursiveScan" />
|
118
|
+
<label class="form-check-label" for="recursiveScan">
|
119
|
+
<i class="bi bi-arrow-down-circle me-1"></i>
|
120
|
+
Include sub-folders during scan
|
121
|
+
</label>
|
122
|
+
</div>
|
123
|
+
</div>
|
124
|
+
|
125
|
+
{#if antennas.length > 0}
|
126
|
+
<div class="row g-3">
|
127
|
+
<div class="col-md-6">
|
128
|
+
<button class="btn btn-success w-100" on:click={saveToDatabase} disabled={isLoading}>
|
129
|
+
{#if isLoading}
|
130
|
+
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
131
|
+
Saving…
|
132
|
+
{:else}
|
133
|
+
<i class="bi bi-database-add me-2"></i>
|
134
|
+
Import to database
|
135
|
+
{/if}
|
136
|
+
</button>
|
137
|
+
</div>
|
138
|
+
<div class="col-md-6">
|
139
|
+
<button class="btn btn-outline-secondary w-100" on:click={downloadJson}>
|
140
|
+
<i class="bi bi-download me-2"></i>
|
141
|
+
Download as JSON
|
142
|
+
</button>
|
143
|
+
</div>
|
144
|
+
</div>
|
145
|
+
{/if}
|
146
|
+
|
147
|
+
{#if message && !error}
|
148
|
+
<div class="alert alert-info border-0 bg-info-subtle text-info-emphasis" role="alert">
|
149
|
+
<i class="bi bi-info-circle me-2"></i>
|
150
|
+
{message}
|
151
|
+
</div>
|
152
|
+
{/if}
|
153
|
+
|
154
|
+
{#if error}
|
155
|
+
<div class="alert alert-danger border-0 bg-danger-subtle text-danger-emphasis" role="alert">
|
156
|
+
<i class="bi bi-exclamation-octagon me-2"></i>
|
157
|
+
{error}
|
158
|
+
</div>
|
159
|
+
{/if}
|
160
|
+
|
161
|
+
{#if antennas.length > 0}
|
162
|
+
<div class="card border border-light-subtle">
|
163
|
+
<div class="card-body">
|
164
|
+
<div class="d-flex flex-column flex-md-row align-items-md-center justify-content-between gap-3 mb-3">
|
165
|
+
<div class="d-flex align-items-center gap-3">
|
166
|
+
<span class="badge text-bg-success fs-6">{antennas.length}</span>
|
167
|
+
<h2 class="h5 mb-0">Files parsed successfully</h2>
|
168
|
+
</div>
|
169
|
+
<button
|
170
|
+
class="btn btn-link text-decoration-none p-0"
|
171
|
+
type="button"
|
172
|
+
data-bs-toggle="collapse"
|
173
|
+
data-bs-target="#parsedAntennaList"
|
174
|
+
aria-expanded="false"
|
175
|
+
>
|
176
|
+
<i class="bi bi-list-ul me-2"></i>
|
177
|
+
View details
|
178
|
+
</button>
|
179
|
+
</div>
|
180
|
+
<div class="collapse" id="parsedAntennaList">
|
181
|
+
<div class="list-group overflow-auto" style="max-height: 320px;">
|
182
|
+
{#each antennas as antenna, index}
|
183
|
+
<div class="list-group-item border-0 border-bottom">
|
184
|
+
<div class="d-flex justify-content-between align-items-start">
|
185
|
+
<div>
|
186
|
+
<h3 class="h6 mb-1">{antenna.name}</h3>
|
187
|
+
<div class="d-flex flex-wrap gap-3 text-muted small">
|
188
|
+
<span><i class="bi bi-broadcast me-1"></i>{antenna.frequency} MHz</span>
|
189
|
+
<span><i class="bi bi-arrow-clockwise me-1"></i>Tilt {antenna.tilt}°</span>
|
190
|
+
</div>
|
191
|
+
{#if antenna.sourcePath}
|
192
|
+
<div class="text-muted small mt-1">
|
193
|
+
<i class="bi bi-folder me-1"></i>
|
194
|
+
{antenna.sourcePath}
|
195
|
+
</div>
|
196
|
+
{/if}
|
197
|
+
</div>
|
198
|
+
<span class="badge text-bg-light">#{index + 1}</span>
|
199
|
+
</div>
|
200
|
+
</div>
|
201
|
+
{/each}
|
202
|
+
</div>
|
203
|
+
</div>
|
204
|
+
</div>
|
205
|
+
</div>
|
206
|
+
{/if}
|
207
|
+
</div>
|
208
|
+
</div>
|
209
|
+
</div>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
3
|
+
$$bindings?: Bindings;
|
4
|
+
} & Exports;
|
5
|
+
(internal: unknown, props: {
|
6
|
+
$$events?: Events;
|
7
|
+
$$slots?: Slots;
|
8
|
+
}): Exports & {
|
9
|
+
$set?: any;
|
10
|
+
$on?: any;
|
11
|
+
};
|
12
|
+
z_$$bindings?: Bindings;
|
13
|
+
}
|
14
|
+
declare const MsiConverter: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
15
|
+
[evt: string]: CustomEvent<any>;
|
16
|
+
}, {}, {}, string>;
|
17
|
+
type MsiConverter = InstanceType<typeof MsiConverter>;
|
18
|
+
export default MsiConverter;
|