@smartnet360/svelte-components 0.0.125 → 0.0.127

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 (48) hide show
  1. package/dist/core/Auth/auth.svelte.js +47 -2
  2. package/dist/map-v3/demo/DemoMap.svelte +36 -0
  3. package/dist/map-v3/demo/demo-custom-cells.d.ts +21 -0
  4. package/dist/map-v3/demo/demo-custom-cells.js +48 -0
  5. package/dist/map-v3/features/cells/custom/components/CustomCellFilterControl.svelte +220 -0
  6. package/dist/map-v3/features/cells/custom/components/CustomCellFilterControl.svelte.d.ts +15 -0
  7. package/dist/map-v3/features/cells/custom/components/CustomCellSetManager.svelte +306 -0
  8. package/dist/map-v3/features/cells/custom/components/CustomCellSetManager.svelte.d.ts +10 -0
  9. package/dist/map-v3/features/cells/custom/components/index.d.ts +5 -0
  10. package/dist/map-v3/features/cells/custom/components/index.js +5 -0
  11. package/dist/map-v3/features/cells/custom/index.d.ts +32 -0
  12. package/dist/map-v3/features/cells/custom/index.js +35 -0
  13. package/dist/map-v3/features/cells/custom/layers/CustomCellsLayer.svelte +262 -0
  14. package/dist/map-v3/features/cells/custom/layers/CustomCellsLayer.svelte.d.ts +10 -0
  15. package/dist/map-v3/features/cells/custom/layers/index.d.ts +4 -0
  16. package/dist/map-v3/features/cells/custom/layers/index.js +4 -0
  17. package/dist/map-v3/features/cells/custom/logic/csv-parser.d.ts +20 -0
  18. package/dist/map-v3/features/cells/custom/logic/csv-parser.js +164 -0
  19. package/dist/map-v3/features/cells/custom/logic/index.d.ts +5 -0
  20. package/dist/map-v3/features/cells/custom/logic/index.js +5 -0
  21. package/dist/map-v3/features/cells/custom/logic/tree-adapter.d.ts +24 -0
  22. package/dist/map-v3/features/cells/custom/logic/tree-adapter.js +67 -0
  23. package/dist/map-v3/features/cells/custom/stores/custom-cell-sets.svelte.d.ts +78 -0
  24. package/dist/map-v3/features/cells/custom/stores/custom-cell-sets.svelte.js +242 -0
  25. package/dist/map-v3/features/cells/custom/stores/index.d.ts +4 -0
  26. package/dist/map-v3/features/cells/custom/stores/index.js +4 -0
  27. package/dist/map-v3/features/cells/custom/types.d.ts +83 -0
  28. package/dist/map-v3/features/cells/custom/types.js +23 -0
  29. package/dist/map-v3/features/sites/custom/components/CustomSiteFilterControl.svelte +203 -0
  30. package/dist/map-v3/features/sites/custom/components/CustomSiteFilterControl.svelte.d.ts +15 -0
  31. package/dist/map-v3/features/sites/custom/components/CustomSiteSetManager.svelte +261 -0
  32. package/dist/map-v3/features/sites/custom/components/CustomSiteSetManager.svelte.d.ts +10 -0
  33. package/dist/map-v3/features/sites/custom/index.d.ts +13 -0
  34. package/dist/map-v3/features/sites/custom/index.js +16 -0
  35. package/dist/map-v3/features/sites/custom/layers/CustomSitesLayer.svelte +201 -0
  36. package/dist/map-v3/features/sites/custom/layers/CustomSitesLayer.svelte.d.ts +8 -0
  37. package/dist/map-v3/features/sites/custom/logic/csv-parser.d.ts +12 -0
  38. package/dist/map-v3/features/sites/custom/logic/csv-parser.js +182 -0
  39. package/dist/map-v3/features/sites/custom/logic/tree-adapter.d.ts +16 -0
  40. package/dist/map-v3/features/sites/custom/logic/tree-adapter.js +59 -0
  41. package/dist/map-v3/features/sites/custom/stores/custom-site-sets.svelte.d.ts +78 -0
  42. package/dist/map-v3/features/sites/custom/stores/custom-site-sets.svelte.js +248 -0
  43. package/dist/map-v3/features/sites/custom/types.d.ts +74 -0
  44. package/dist/map-v3/features/sites/custom/types.js +8 -0
  45. package/dist/map-v3/index.d.ts +2 -0
  46. package/dist/map-v3/index.js +4 -0
  47. package/dist/map-v3/shared/controls/MapControl.svelte +27 -3
  48. package/package.json +1 -1
@@ -1,5 +1,32 @@
1
1
  // Auth state management using Svelte 5 runes
2
2
  import { browser } from '$app/environment';
3
+ // Demo mode - enabled via PUBLIC_DEMO_MODE env var (GitHub Pages)
4
+ const DEMO_MODE = import.meta.env.PUBLIC_DEMO_MODE === 'true';
5
+ const DEMO_USERNAME = 'demo';
6
+ const DEMO_PASSWORD = 'demo123';
7
+ // Demo user session (client-side only, no server needed)
8
+ const DEMO_SESSION = {
9
+ user: {
10
+ id: 'demo-user',
11
+ username: 'demo',
12
+ displayName: 'Demo User',
13
+ email: 'demo@example.com',
14
+ groups: ['demo'],
15
+ loginTime: Date.now()
16
+ },
17
+ permissions: [
18
+ { featureId: 'map-view', canView: true, canEdit: false, canAdmin: false },
19
+ { featureId: 'coverage-view', canView: true, canEdit: false, canAdmin: false },
20
+ { featureId: 'charts-view', canView: true, canEdit: false, canAdmin: false },
21
+ { featureId: 'desktop-view', canView: true, canEdit: false, canAdmin: false },
22
+ { featureId: 'table-view', canView: true, canEdit: false, canAdmin: false },
23
+ { featureId: 'antenna-view', canView: true, canEdit: false, canAdmin: false },
24
+ { featureId: 'site-check', canView: true, canEdit: false, canAdmin: false },
25
+ { featureId: 'settings-view', canView: true, canEdit: false, canAdmin: false }
26
+ ],
27
+ token: 'demo-token',
28
+ expiresAt: Date.now() + (24 * 60 * 60 * 1000) // 24 hours for demo
29
+ };
3
30
  // Default configuration
4
31
  const defaultConfig = {
5
32
  apiEndpoint: '/api/auth',
@@ -80,6 +107,24 @@ export function createAuthState(config = {}) {
80
107
  isLoading = true;
81
108
  error = null;
82
109
  try {
110
+ // Demo mode: handle authentication client-side (no server needed)
111
+ if (DEMO_MODE) {
112
+ if (username === DEMO_USERNAME && password === DEMO_PASSWORD) {
113
+ // Create fresh demo session with updated timestamp
114
+ const demoSession = {
115
+ ...DEMO_SESSION,
116
+ user: { ...DEMO_SESSION.user, loginTime: Date.now() },
117
+ expiresAt: Date.now() + (24 * 60 * 60 * 1000)
118
+ };
119
+ saveSession(demoSession);
120
+ return true;
121
+ }
122
+ else {
123
+ error = 'Demo mode: Use username "demo" and password "demo123"';
124
+ return false;
125
+ }
126
+ }
127
+ // Normal mode: call server API
83
128
  const response = await fetch(`${cfg.apiEndpoint}/login`, {
84
129
  method: 'POST',
85
130
  headers: { 'Content-Type': 'application/json' },
@@ -121,8 +166,8 @@ export function createAuthState(config = {}) {
121
166
  async function logout() {
122
167
  isLoading = true;
123
168
  try {
124
- // Call logout endpoint if we have a token
125
- if (session?.token) {
169
+ // Skip server call in demo mode (no server available)
170
+ if (!DEMO_MODE && session?.token) {
126
171
  await fetch(`${cfg.apiEndpoint}/logout`, {
127
172
  method: 'POST',
128
173
  headers: {
@@ -20,6 +20,18 @@
20
20
  import SiteFilterControl from '../features/sites/components/SiteFilterControl.svelte';
21
21
  import { createSiteRegistry } from '../features/sites/stores/site.registry.svelte';
22
22
  import FeatureSelectionControl from '../features/selection/components/FeatureSelectionControl.svelte';
23
+ // Custom Cells Feature
24
+ import {
25
+ CustomCellsLayer,
26
+ CustomCellSetManager,
27
+ createCustomCellSetsStore
28
+ } from '../features/cells/custom';
29
+ // Custom Sites Feature
30
+ import {
31
+ CustomSitesLayer,
32
+ CustomSiteSetManager,
33
+ CustomSiteSetsStore
34
+ } from '../features/sites/custom';
23
35
  import { demoCells } from './demo-cells';
24
36
  import { demoRepeaters } from './demo-repeaters';
25
37
 
@@ -42,6 +54,12 @@
42
54
  const repeaterRegistry = createRepeaterRegistry('demo-map');
43
55
  const repeaterDisplay = new RepeaterDisplayStore();
44
56
 
57
+ // Custom Cells Store
58
+ const customCellSets = createCustomCellSetsStore(cellData, 'demo-map');
59
+
60
+ // Custom Sites Store
61
+ const customSiteSets = new CustomSiteSetsStore();
62
+
45
63
  onMount(() => {
46
64
  // Load dummy data
47
65
  // Need to cast or map if types slightly differ, but they should match
@@ -81,6 +99,18 @@
81
99
  displayStore={repeaterDisplay}
82
100
  />
83
101
 
102
+ <!-- Custom Cells Manager (includes filter controls for each set) -->
103
+ <CustomCellSetManager
104
+ position="top-left"
105
+ setsStore={customCellSets}
106
+ />
107
+
108
+ <!-- Custom Sites Manager (includes filter controls for each set) -->
109
+ <CustomSiteSetManager
110
+ position="top-left"
111
+ setsStore={customSiteSets}
112
+ />
113
+
84
114
  <FeatureSettingsControl
85
115
  position="top-right"
86
116
  cellDisplayStore={cellDisplay}
@@ -99,6 +129,12 @@
99
129
  <RepeatersLayer dataStore={repeaterData} registry={repeaterRegistry} displayStore={repeaterDisplay} />
100
130
  <RepeaterLabelsLayer dataStore={repeaterData} registry={repeaterRegistry} displayStore={repeaterDisplay} />
101
131
 
132
+ <!-- Custom Cells Layer (renders on top of regular cells) -->
133
+ <CustomCellsLayer setsStore={customCellSets} />
134
+
135
+ <!-- Custom Sites Layer (renders custom point markers) -->
136
+ <CustomSitesLayer setsStore={customSiteSets} />
137
+
102
138
  <FeatureSelectionControl
103
139
  position="bottom-left"
104
140
  cellDataStore={cellData}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Demo Custom Cells Data
3
+ *
4
+ * Sample CSV content for testing custom cells feature.
5
+ * Uses txIds from the demo cells data.
6
+ *
7
+ * txId format: SSSS S BB where:
8
+ * - SSSS = site number (1000+)
9
+ * - S = sector (1, 2, or 3)
10
+ * - BB = band index (41-51)
11
+ * 41=GSM900, 42=GSM1800, 43=LTE700, 44=LTE800, 45=LTE900
12
+ * 46=LTE1800, 47=LTE2100, 48=LTE2600, 49=5G700, 50=5G2100, 51=5G3500
13
+ */
14
+ /**
15
+ * Sample CSV content with various groups and size factors
16
+ */
17
+ export declare const demoCustomCellsCsv = "txId,customGroup,sizeFactor,congestion,trafficGB\n1000141,High Traffic,3,critical,250\n1000241,High Traffic,2.5,high,180\n1000341,High Traffic,2,medium,120\n1001145,High Traffic,3.5,critical,300\n1001245,High Traffic,2,medium,100\n1002147,Medium Traffic,1.5,low,60\n1002247,Medium Traffic,1.2,low,45\n1002347,Medium Traffic,1,minimal,30\n1003142,Medium Traffic,1.5,low,55\n1003242,Medium Traffic,1.3,low,40\n1004151,Low Traffic,0.8,minimal,15\n1004251,Low Traffic,0.7,minimal,10\n1004351,Low Traffic,0.6,minimal,8\n1005149,Problem Cells,2,error,0\n1005249,Problem Cells,2,error,0\n1006144,Problem Cells,1.8,warning,5\n";
18
+ /**
19
+ * Another sample CSV with different groupings
20
+ */
21
+ export declare const demoCustomCellsCsv2 = "txId,customGroup,sizeFactor,priority\n1010141,Expansion Zone A,2,high\n1010241,Expansion Zone A,2,high\n1010341,Expansion Zone A,2,high\n1011145,Expansion Zone A,1.5,medium\n1012147,Expansion Zone B,1.8,high\n1012247,Expansion Zone B,1.8,high\n1013142,Existing Coverage,1,low\n1013242,Existing Coverage,1,low\n1013342,Existing Coverage,1,low\n";
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Demo Custom Cells Data
3
+ *
4
+ * Sample CSV content for testing custom cells feature.
5
+ * Uses txIds from the demo cells data.
6
+ *
7
+ * txId format: SSSS S BB where:
8
+ * - SSSS = site number (1000+)
9
+ * - S = sector (1, 2, or 3)
10
+ * - BB = band index (41-51)
11
+ * 41=GSM900, 42=GSM1800, 43=LTE700, 44=LTE800, 45=LTE900
12
+ * 46=LTE1800, 47=LTE2100, 48=LTE2600, 49=5G700, 50=5G2100, 51=5G3500
13
+ */
14
+ /**
15
+ * Sample CSV content with various groups and size factors
16
+ */
17
+ export const demoCustomCellsCsv = `txId,customGroup,sizeFactor,congestion,trafficGB
18
+ 1000141,High Traffic,3,critical,250
19
+ 1000241,High Traffic,2.5,high,180
20
+ 1000341,High Traffic,2,medium,120
21
+ 1001145,High Traffic,3.5,critical,300
22
+ 1001245,High Traffic,2,medium,100
23
+ 1002147,Medium Traffic,1.5,low,60
24
+ 1002247,Medium Traffic,1.2,low,45
25
+ 1002347,Medium Traffic,1,minimal,30
26
+ 1003142,Medium Traffic,1.5,low,55
27
+ 1003242,Medium Traffic,1.3,low,40
28
+ 1004151,Low Traffic,0.8,minimal,15
29
+ 1004251,Low Traffic,0.7,minimal,10
30
+ 1004351,Low Traffic,0.6,minimal,8
31
+ 1005149,Problem Cells,2,error,0
32
+ 1005249,Problem Cells,2,error,0
33
+ 1006144,Problem Cells,1.8,warning,5
34
+ `;
35
+ /**
36
+ * Another sample CSV with different groupings
37
+ */
38
+ export const demoCustomCellsCsv2 = `txId,customGroup,sizeFactor,priority
39
+ 1010141,Expansion Zone A,2,high
40
+ 1010241,Expansion Zone A,2,high
41
+ 1010341,Expansion Zone A,2,high
42
+ 1011145,Expansion Zone A,1.5,medium
43
+ 1012147,Expansion Zone B,1.8,high
44
+ 1012247,Expansion Zone B,1.8,high
45
+ 1013142,Existing Coverage,1,low
46
+ 1013242,Existing Coverage,1,low
47
+ 1013342,Existing Coverage,1,low
48
+ `;
@@ -0,0 +1,220 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Custom Cell Filter Control
4
+ *
5
+ * TreeView with color pickers for a single custom cell set.
6
+ * Shows groups with their cell counts and allows color customization.
7
+ */
8
+ import { untrack, onDestroy } from 'svelte';
9
+ import { MapControl } from '../../../../shared';
10
+ import { createTreeStore, TreeView } from '../../../../../core/TreeView';
11
+ import type { CustomCellSetsStore } from '../stores/custom-cell-sets.svelte';
12
+ import type { CustomCellSet } from '../types';
13
+ import { buildCustomCellTree } from '../logic/tree-adapter';
14
+
15
+ interface Props {
16
+ /** The custom cell sets store */
17
+ setsStore: CustomCellSetsStore;
18
+ /** The specific set to display */
19
+ set: CustomCellSet;
20
+ /** Control position on map */
21
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
22
+ /** Callback when set is removed */
23
+ onremove?: (setId: string) => void;
24
+ }
25
+
26
+ let {
27
+ setsStore,
28
+ set,
29
+ position = 'top-left',
30
+ onremove
31
+ }: Props = $props();
32
+
33
+ // Reference to MapControl for explicit cleanup
34
+ let mapControlRef: MapControl | undefined;
35
+
36
+ // Track if this component should be destroyed (used for delayed removal)
37
+ let isDestroying = $state(false);
38
+
39
+ onDestroy(() => {
40
+ console.log(`[CustomCellFilterControl] onDestroy called for set: ${set.id}`);
41
+ });
42
+
43
+ // Build tree from set
44
+ let treeStore = $derived.by(() => {
45
+ const _version = setsStore.version;
46
+ const _set = set;
47
+
48
+ return untrack(() => {
49
+ const nodes = buildCustomCellTree(_set);
50
+ return createTreeStore({
51
+ nodes,
52
+ namespace: `custom-cells:${_set.id}`,
53
+ persistState: true,
54
+ defaultExpandAll: true
55
+ });
56
+ });
57
+ });
58
+
59
+ // Sync tree selection -> store visibility
60
+ $effect(() => {
61
+ const val = treeStore;
62
+ let changes = 0;
63
+
64
+ val.state.nodes.forEach((nodeState) => {
65
+ // Skip root node
66
+ if (nodeState.node.id === `root-${set.id}`) return;
67
+ // Skip folder nodes
68
+ if (nodeState.node.children && nodeState.node.children.length > 0) return;
69
+
70
+ const groupId = nodeState.node.metadata?.groupId;
71
+ if (!groupId) return;
72
+
73
+ const isVisible = val.state.checkedPaths.has(nodeState.path);
74
+ const currentlyVisible = set.visibleGroups.has(groupId);
75
+
76
+ if (isVisible !== currentlyVisible) {
77
+ setsStore.toggleGroupVisibility(set.id, groupId);
78
+ changes++;
79
+ }
80
+ });
81
+
82
+ if (changes > 0) {
83
+ console.log(`[CustomCellFilterControl] Synced ${changes} visibility changes`);
84
+ }
85
+ });
86
+
87
+ function handleColorChange(groupId: string, event: Event) {
88
+ const input = event.target as HTMLInputElement;
89
+ setsStore.setGroupColor(set.id, groupId, input.value);
90
+ }
91
+
92
+ function handleRemove() {
93
+ if (onremove) {
94
+ onremove(set.id);
95
+ }
96
+ }
97
+
98
+ function handleToggleVisibility() {
99
+ setsStore.toggleSetVisibility(set.id);
100
+ }
101
+ </script>
102
+
103
+ <MapControl {position} title={set.name} icon="layers" controlWidth="280px">
104
+ {#snippet actions()}
105
+ <button
106
+ class="btn btn-sm btn-outline-secondary border-0 p-1 px-2"
107
+ title={set.visible ? 'Hide Layer' : 'Show Layer'}
108
+ onclick={handleToggleVisibility}
109
+ >
110
+ <i class="bi bi-eye{set.visible ? '-fill' : '-slash'}"></i>
111
+ </button>
112
+ <button
113
+ class="btn btn-sm btn-outline-danger border-0 p-1 px-2"
114
+ title="Remove Set"
115
+ onclick={handleRemove}
116
+ >
117
+ <i class="bi bi-trash"></i>
118
+ </button>
119
+ {/snippet}
120
+
121
+ <div class="custom-cell-filter-control">
122
+ <!-- Set Info -->
123
+ <div class="set-info mb-2 px-1">
124
+ <small class="text-muted">
125
+ {set.cells.length} cells in {set.groups.length} groups
126
+ {#if set.unmatchedTxIds.length > 0}
127
+ <span class="text-warning ms-1" title="Some cells not found">
128
+ ({set.unmatchedTxIds.length} unmatched)
129
+ </span>
130
+ {/if}
131
+ </small>
132
+ </div>
133
+
134
+ <!-- Size Slider -->
135
+ <div class="size-control mb-2 px-1">
136
+ <label for="baseSize-{set.id}" class="form-label small mb-1">
137
+ Base Size: {set.baseSize}px
138
+ </label>
139
+ <input
140
+ type="range"
141
+ class="form-range"
142
+ id="baseSize-{set.id}"
143
+ min="10"
144
+ max="150"
145
+ step="5"
146
+ value={set.baseSize}
147
+ oninput={(e) => setsStore.updateSetSettings(set.id, {
148
+ baseSize: parseInt((e.target as HTMLInputElement).value)
149
+ })}
150
+ />
151
+ </div>
152
+
153
+ <!-- Opacity Slider -->
154
+ <div class="opacity-control mb-2 px-1">
155
+ <label for="opacity-{set.id}" class="form-label small mb-1">
156
+ Opacity: {Math.round(set.opacity * 100)}%
157
+ </label>
158
+ <input
159
+ type="range"
160
+ class="form-range"
161
+ id="opacity-{set.id}"
162
+ min="0.1"
163
+ max="1"
164
+ step="0.1"
165
+ value={set.opacity}
166
+ oninput={(e) => setsStore.updateSetSettings(set.id, {
167
+ opacity: parseFloat((e.target as HTMLInputElement).value)
168
+ })}
169
+ />
170
+ </div>
171
+
172
+ <!-- Tree View -->
173
+ <div class="custom-cell-tree">
174
+ {#if set.cells.length === 0}
175
+ <div class="text-muted p-3 text-center small">
176
+ No cells in this set.
177
+ </div>
178
+ {:else}
179
+ <TreeView showControls={false} store={treeStore} height="200px">
180
+ {#snippet children({ node, state })}
181
+ <!-- Color Picker (Only for group leaves) -->
182
+ {#if (!node.children || node.children.length === 0) && node.metadata?.groupId !== '__root__'}
183
+ <div
184
+ class="d-flex align-items-center"
185
+ role="group"
186
+ onclick={(e) => e.stopPropagation()}
187
+ onkeydown={(e) => e.stopPropagation()}
188
+ >
189
+ <input
190
+ type="color"
191
+ class="form-control form-control-color form-control-sm border-0 p-0"
192
+ style="width: 16px; height: 16px; min-height: 0;"
193
+ value={node.metadata?.color}
194
+ oninput={(e) => handleColorChange(node.metadata?.groupId || '', e)}
195
+ title="Change color"
196
+ />
197
+ </div>
198
+ {/if}
199
+ {/snippet}
200
+ </TreeView>
201
+ {/if}
202
+ </div>
203
+ </div>
204
+ </MapControl>
205
+
206
+ <style>
207
+ .custom-cell-filter-control {
208
+ font-size: 0.875rem;
209
+ }
210
+
211
+ .set-info {
212
+ border-bottom: 1px solid var(--bs-border-color, #dee2e6);
213
+ padding-bottom: 0.5rem;
214
+ }
215
+
216
+ .custom-cell-tree {
217
+ max-height: 250px;
218
+ overflow-y: auto;
219
+ }
220
+ </style>
@@ -0,0 +1,15 @@
1
+ import type { CustomCellSetsStore } from '../stores/custom-cell-sets.svelte';
2
+ import type { CustomCellSet } from '../types';
3
+ interface Props {
4
+ /** The custom cell sets store */
5
+ setsStore: CustomCellSetsStore;
6
+ /** The specific set to display */
7
+ set: CustomCellSet;
8
+ /** Control position on map */
9
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
10
+ /** Callback when set is removed */
11
+ onremove?: (setId: string) => void;
12
+ }
13
+ declare const CustomCellFilterControl: import("svelte").Component<Props, {}, "">;
14
+ type CustomCellFilterControl = ReturnType<typeof CustomCellFilterControl>;
15
+ export default CustomCellFilterControl;