@camstack/addon-admin-ui 0.1.2 → 0.1.4

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 (127) hide show
  1. package/dist/assets/index-BoVZEQ1j.js +598 -0
  2. package/dist/assets/index-DwSc8ann.css +1 -0
  3. package/{index.html → dist/index.html} +3 -1
  4. package/dist/server/addon.d.ts +11 -0
  5. package/dist/server/addon.js +50 -0
  6. package/dist/server/addon.js.map +1 -0
  7. package/package.json +4 -1
  8. package/src/App.tsx +0 -71
  9. package/src/components/addons/AddonCard.tsx +0 -355
  10. package/src/components/addons/AddonUploadZone.tsx +0 -69
  11. package/src/components/addons/CapabilityBadge.tsx +0 -55
  12. package/src/components/addons/CapabilityMap.tsx +0 -133
  13. package/src/components/addons/UpdatesList.tsx +0 -108
  14. package/src/components/agents/AgentCard.tsx +0 -281
  15. package/src/components/agents/AgentLogs.tsx +0 -231
  16. package/src/components/agents/ProcessList.tsx +0 -127
  17. package/src/components/agents/ProcessTree.tsx +0 -369
  18. package/src/components/agents/TaskList.tsx +0 -68
  19. package/src/components/cameras/CameraCard.tsx +0 -60
  20. package/src/components/cameras/LiveEventsPanel.tsx +0 -91
  21. package/src/components/cameras/ProviderSection.tsx +0 -50
  22. package/src/components/cameras/StreamArea.tsx +0 -107
  23. package/src/components/cameras/tabs/AddonsTab.tsx +0 -113
  24. package/src/components/cameras/tabs/CameraEventsTab.tsx +0 -129
  25. package/src/components/cameras/tabs/PipelineTab.tsx +0 -118
  26. package/src/components/cameras/tabs/StreamsTab.tsx +0 -114
  27. package/src/components/dashboard/BlockPicker.tsx +0 -54
  28. package/src/components/dashboard/BlockWrapper.tsx +0 -97
  29. package/src/components/dashboard/DashboardGrid.tsx +0 -160
  30. package/src/components/dashboard/block-registry.ts +0 -15
  31. package/src/components/dashboard/blocks/PipelineStagesBlock.tsx +0 -39
  32. package/src/components/dashboard/blocks/StorageBlock.tsx +0 -66
  33. package/src/components/dashboard/blocks/SystemStatusBlock.tsx +0 -67
  34. package/src/components/dashboard/blocks/index.ts +0 -32
  35. package/src/components/device/DeviceHeader.tsx +0 -116
  36. package/src/components/device/FloatingPanel.tsx +0 -132
  37. package/src/components/device/FloatingPanelManager.tsx +0 -167
  38. package/src/components/device/PanelContent.tsx +0 -196
  39. package/src/components/device/QuickConfigWizard.tsx +0 -507
  40. package/src/components/device/tabs/DetectionConfigTab.tsx +0 -96
  41. package/src/components/device/tabs/EventsTab.tsx +0 -19
  42. package/src/components/device/tabs/LogsTab.tsx +0 -22
  43. package/src/components/device/tabs/OverviewTab.tsx +0 -104
  44. package/src/components/device/tabs/ProviderSettingsTab.tsx +0 -34
  45. package/src/components/device/tabs/RecordingTab.tsx +0 -47
  46. package/src/components/device/tabs/ReplTab.tsx +0 -153
  47. package/src/components/device/tabs/TrackTrailTab.tsx +0 -49
  48. package/src/components/device/tabs/ZonesTab.tsx +0 -98
  49. package/src/components/device/zone-editor/ZoneCanvas.tsx +0 -354
  50. package/src/components/device/zone-editor/ZoneForm.tsx +0 -128
  51. package/src/components/device/zone-editor/ZoneList.tsx +0 -150
  52. package/src/components/form-builder/FormBuilder.tsx +0 -135
  53. package/src/components/form-builder/FormField.tsx +0 -732
  54. package/src/components/form-builder/ModelSelector.tsx +0 -239
  55. package/src/components/integrations/AddDeviceDialog.tsx +0 -205
  56. package/src/components/integrations/CompactDeviceCard.tsx +0 -35
  57. package/src/components/integrations/DeviceCard.tsx +0 -29
  58. package/src/components/integrations/DeviceDiscoveryStep.tsx +0 -105
  59. package/src/components/integrations/DeviceGrid.tsx +0 -79
  60. package/src/components/integrations/DeviceGroupHeader.tsx +0 -17
  61. package/src/components/integrations/DiscoveredDeviceCard.tsx +0 -26
  62. package/src/components/integrations/IntegrationCard.tsx +0 -40
  63. package/src/components/integrations/IntegrationWizard.tsx +0 -172
  64. package/src/components/integrations/ProviderConfigForm.tsx +0 -89
  65. package/src/components/integrations/ProviderPicker.tsx +0 -91
  66. package/src/components/integrations/SnapshotPopover.tsx +0 -68
  67. package/src/components/metrics/AgentLoad.tsx +0 -105
  68. package/src/components/metrics/IntegrationUsage.tsx +0 -73
  69. package/src/components/metrics/PipelineStatus.tsx +0 -74
  70. package/src/components/metrics/ProcessResources.tsx +0 -123
  71. package/src/components/pipeline/PhaseSettings.tsx +0 -131
  72. package/src/components/shared/CapabilityBadges.tsx +0 -30
  73. package/src/components/shared/ProviderIcon.tsx +0 -42
  74. package/src/components/shared/StatusBadge.tsx +0 -23
  75. package/src/components/shared/WebRtcPlayer.tsx +0 -211
  76. package/src/components/timeline/EventMarker.tsx +0 -32
  77. package/src/components/timeline/TimelineBar.tsx +0 -131
  78. package/src/components/ui/ConfirmDialog.tsx +0 -115
  79. package/src/components/ui/ToastContainer.tsx +0 -92
  80. package/src/contexts/auth-context.tsx +0 -91
  81. package/src/hooks/useBackendClient.ts +0 -6
  82. package/src/hooks/useTheme.ts +0 -1
  83. package/src/i18n/en.json +0 -164
  84. package/src/i18n/index.ts +0 -29
  85. package/src/i18n/it.json +0 -164
  86. package/src/index.css +0 -63
  87. package/src/layouts/AddonPageLoader.tsx +0 -120
  88. package/src/layouts/AppLayout.tsx +0 -254
  89. package/src/layouts/ProtectedRoute.tsx +0 -25
  90. package/src/lib/addon-page-context.ts +0 -29
  91. package/src/lib/backend.ts +0 -16
  92. package/src/main.tsx +0 -21
  93. package/src/pages/AccessDenied.tsx +0 -22
  94. package/src/pages/Cameras.tsx +0 -127
  95. package/src/pages/Dashboard.tsx +0 -6
  96. package/src/pages/DeviceDetail.tsx +0 -175
  97. package/src/pages/IntegrationDetail.tsx +0 -222
  98. package/src/pages/Integrations.tsx +0 -333
  99. package/src/pages/Login.tsx +0 -106
  100. package/src/pages/Metrics.tsx +0 -18
  101. package/src/pages/PipelineConfig.tsx +0 -282
  102. package/src/pages/Showroom.tsx +0 -351
  103. package/src/pages/Timeline.tsx +0 -269
  104. package/src/pages/system/Addons.tsx +0 -396
  105. package/src/pages/system/Agents.tsx +0 -362
  106. package/src/pages/system/Logs.tsx +0 -131
  107. package/src/pages/system/Models.tsx +0 -102
  108. package/src/pages/system/Processes.tsx +0 -129
  109. package/src/pages/system/Repl.tsx +0 -148
  110. package/src/pages/system/Settings.tsx +0 -168
  111. package/src/pages/system/Users.tsx +0 -174
  112. package/src/server/addon.ts +0 -54
  113. package/src/types/config-ui.ts +0 -28
  114. package/src/types/dashboard.ts +0 -39
  115. package/tsconfig.json +0 -29
  116. package/tsconfig.server.json +0 -16
  117. package/tsup.config.ts +0 -20
  118. package/vite.config.ts +0 -68
  119. /package/{public → dist}/brand/logo-dark.svg +0 -0
  120. /package/{public → dist}/brand/logo-horizontal-dark.svg +0 -0
  121. /package/{public → dist}/brand/logo-horizontal-light.svg +0 -0
  122. /package/{public → dist}/brand/logo-light.svg +0 -0
  123. /package/{public → dist}/brand/logo-wide-dark.svg +0 -0
  124. /package/{public → dist}/brand/logo-wide-light.svg +0 -0
  125. /package/{public → dist}/favicon.svg +0 -0
  126. /package/{public → dist}/vendor/react-jsx-runtime.mjs +0 -0
  127. /package/{public → dist}/vendor/react.mjs +0 -0
@@ -1,507 +0,0 @@
1
- import { useState } from 'react'
2
- import { X, ChevronLeft, ChevronRight, Check, Zap } from 'lucide-react'
3
-
4
- interface QuickConfigWizardProps {
5
- open: boolean
6
- onClose: () => void
7
- deviceId: string
8
- }
9
-
10
- // ----- Step 1: Detection Classes -----
11
- interface DetectionClass {
12
- id: string
13
- label: string
14
- enabled: boolean
15
- score: number
16
- }
17
-
18
- const DEFAULT_DETECTION_CLASSES: DetectionClass[] = [
19
- { id: 'person', label: 'Person', enabled: true, score: 50 },
20
- { id: 'vehicle', label: 'Vehicle', enabled: true, score: 50 },
21
- { id: 'animal', label: 'Animal', enabled: false, score: 50 },
22
- { id: 'package', label: 'Package', enabled: false, score: 50 },
23
- ]
24
-
25
- // ----- Step 2: Pipeline Stages -----
26
- interface PipelineStage {
27
- id: string
28
- label: string
29
- enabled: boolean
30
- }
31
-
32
- const DEFAULT_PIPELINE_STAGES: PipelineStage[] = [
33
- { id: 'class-filter', label: 'Class Filter', enabled: true },
34
- { id: 'tracker', label: 'Tracker', enabled: true },
35
- { id: 'sub-detection', label: 'Sub-Detection', enabled: false },
36
- { id: 'recognition', label: 'Recognition', enabled: false },
37
- { id: 'zone-analysis', label: 'Zone Analysis', enabled: false },
38
- { id: 'event-generation', label: 'Event Generation', enabled: true },
39
- { id: 'object-snapshot', label: 'Object Snapshot', enabled: true },
40
- ]
41
-
42
- // ----- Step 3: Recording -----
43
- interface RecordingConfig {
44
- enabled: boolean
45
- retentionDays: number
46
- format: 'mp4' | 'ts'
47
- }
48
-
49
- // ----- Step 4: Extensions -----
50
- interface Extension {
51
- id: string
52
- label: string
53
- enabled: boolean
54
- }
55
-
56
- const DEFAULT_EXTENSIONS: Extension[] = [
57
- { id: 'face-recognition', label: 'Face Recognition', enabled: false },
58
- { id: 'plate-ocr', label: 'Plate OCR', enabled: false },
59
- { id: 'audio-classifier', label: 'Audio Classifier', enabled: false },
60
- { id: 'trail-capture', label: 'Trail Capture', enabled: false },
61
- ]
62
-
63
- // ----- Wizard config state -----
64
- interface WizardConfig {
65
- detectionClasses: DetectionClass[]
66
- pipelineStages: PipelineStage[]
67
- recording: RecordingConfig
68
- extensions: Extension[]
69
- }
70
-
71
- const STEPS = [
72
- 'Detection Classes',
73
- 'Pipeline Stages',
74
- 'Recording',
75
- 'Extensions',
76
- 'Summary',
77
- ] as const
78
-
79
- type Step = 0 | 1 | 2 | 3 | 4
80
-
81
- // ----- Sub-components -----
82
-
83
- function StepIndicator({ current, total }: { current: Step; total: number }) {
84
- return (
85
- <div className="flex items-center justify-center gap-2 mb-6">
86
- {Array.from({ length: total }).map((_, i) => (
87
- <div
88
- key={i}
89
- className={`rounded-full transition-all ${
90
- i === current
91
- ? 'h-2 w-6 bg-primary'
92
- : i < current
93
- ? 'h-2 w-2 bg-primary/50'
94
- : 'h-2 w-2 bg-border'
95
- }`}
96
- />
97
- ))}
98
- </div>
99
- )
100
- }
101
-
102
- function ToggleSwitch({
103
- checked,
104
- onChange,
105
- }: {
106
- checked: boolean
107
- onChange: (v: boolean) => void
108
- }) {
109
- return (
110
- <button
111
- type="button"
112
- role="switch"
113
- aria-checked={checked}
114
- onClick={() => onChange(!checked)}
115
- className={`relative inline-flex h-5 w-9 flex-shrink-0 rounded-full border-2 border-transparent transition-colors focus:outline-none ${
116
- checked ? 'bg-primary' : 'bg-border'
117
- }`}
118
- >
119
- <span
120
- className={`pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow transition-transform ${
121
- checked ? 'translate-x-4' : 'translate-x-0'
122
- }`}
123
- />
124
- </button>
125
- )
126
- }
127
-
128
- // Step 1
129
- function Step1DetectionClasses({
130
- classes,
131
- onChange,
132
- }: {
133
- classes: DetectionClass[]
134
- onChange: (updated: DetectionClass[]) => void
135
- }) {
136
- function toggleClass(id: string) {
137
- onChange(classes.map((c) => (c.id === id ? { ...c, enabled: !c.enabled } : c)))
138
- }
139
-
140
- function setScore(id: string, score: number) {
141
- onChange(classes.map((c) => (c.id === id ? { ...c, score } : c)))
142
- }
143
-
144
- return (
145
- <div className="space-y-4">
146
- <p className="text-xs text-foreground-subtle">
147
- Enable detection classes and set minimum confidence scores.
148
- </p>
149
- {classes.map((cls) => (
150
- <div key={cls.id} className="rounded-lg border border-border bg-background p-3 space-y-2">
151
- <div className="flex items-center justify-between">
152
- <span className="text-sm font-medium text-foreground">{cls.label}</span>
153
- <ToggleSwitch checked={cls.enabled} onChange={() => toggleClass(cls.id)} />
154
- </div>
155
- {cls.enabled && (
156
- <div className="flex items-center gap-3">
157
- <span className="text-[11px] text-foreground-subtle w-16 flex-shrink-0">
158
- Min score
159
- </span>
160
- <input
161
- type="range"
162
- min={0}
163
- max={100}
164
- value={cls.score}
165
- onChange={(e) => setScore(cls.id, Number(e.target.value))}
166
- className="flex-1 accent-primary"
167
- />
168
- <span className="text-[11px] text-foreground w-8 text-right">{cls.score}%</span>
169
- </div>
170
- )}
171
- </div>
172
- ))}
173
- </div>
174
- )
175
- }
176
-
177
- // Step 2
178
- function Step2PipelineStages({
179
- stages,
180
- onChange,
181
- }: {
182
- stages: PipelineStage[]
183
- onChange: (updated: PipelineStage[]) => void
184
- }) {
185
- function toggleStage(id: string) {
186
- onChange(stages.map((s) => (s.id === id ? { ...s, enabled: !s.enabled } : s)))
187
- }
188
-
189
- return (
190
- <div className="space-y-3">
191
- <p className="text-xs text-foreground-subtle">
192
- Toggle processing stages for the detection pipeline.
193
- </p>
194
- {stages.map((stage) => (
195
- <div
196
- key={stage.id}
197
- className="flex items-center justify-between rounded-lg border border-border bg-background px-3 py-2.5"
198
- >
199
- <span className="text-sm text-foreground">{stage.label}</span>
200
- <ToggleSwitch checked={stage.enabled} onChange={() => toggleStage(stage.id)} />
201
- </div>
202
- ))}
203
- </div>
204
- )
205
- }
206
-
207
- // Step 3
208
- function Step3Recording({
209
- config,
210
- onChange,
211
- }: {
212
- config: RecordingConfig
213
- onChange: (updated: RecordingConfig) => void
214
- }) {
215
- return (
216
- <div className="space-y-4">
217
- <p className="text-xs text-foreground-subtle">Configure recording behavior for this device.</p>
218
-
219
- <div className="flex items-center justify-between rounded-lg border border-border bg-background px-3 py-2.5">
220
- <span className="text-sm text-foreground">Enable Recording</span>
221
- <ToggleSwitch
222
- checked={config.enabled}
223
- onChange={(v) => onChange({ ...config, enabled: v })}
224
- />
225
- </div>
226
-
227
- {config.enabled && (
228
- <>
229
- <div className="rounded-lg border border-border bg-background p-3 space-y-2">
230
- <div className="flex items-center justify-between">
231
- <span className="text-sm font-medium text-foreground">Retention</span>
232
- <span className="text-sm text-primary font-medium">{config.retentionDays} days</span>
233
- </div>
234
- <input
235
- type="range"
236
- min={1}
237
- max={90}
238
- value={config.retentionDays}
239
- onChange={(e) => onChange({ ...config, retentionDays: Number(e.target.value) })}
240
- className="w-full accent-primary"
241
- />
242
- <div className="flex justify-between text-[10px] text-foreground-subtle">
243
- <span>1 day</span>
244
- <span>90 days</span>
245
- </div>
246
- </div>
247
-
248
- <div className="rounded-lg border border-border bg-background p-3 space-y-2">
249
- <span className="text-sm font-medium text-foreground block">Format</span>
250
- <div className="flex gap-2">
251
- {(['mp4', 'ts'] as const).map((fmt) => (
252
- <button
253
- key={fmt}
254
- type="button"
255
- onClick={() => onChange({ ...config, format: fmt })}
256
- className={`flex-1 rounded-lg border py-2 text-xs font-medium transition-colors ${
257
- config.format === fmt
258
- ? 'border-primary bg-primary/10 text-primary'
259
- : 'border-border text-foreground-subtle hover:text-foreground'
260
- }`}
261
- >
262
- .{fmt}
263
- </button>
264
- ))}
265
- </div>
266
- </div>
267
- </>
268
- )}
269
- </div>
270
- )
271
- }
272
-
273
- // Step 4
274
- function Step4Extensions({
275
- extensions,
276
- onChange,
277
- }: {
278
- extensions: Extension[]
279
- onChange: (updated: Extension[]) => void
280
- }) {
281
- function toggleExtension(id: string) {
282
- onChange(extensions.map((e) => (e.id === id ? { ...e, enabled: !e.enabled } : e)))
283
- }
284
-
285
- return (
286
- <div className="space-y-3">
287
- <p className="text-xs text-foreground-subtle">Enable optional processing add-ons.</p>
288
- {extensions.map((ext) => (
289
- <div
290
- key={ext.id}
291
- className="flex items-center justify-between rounded-lg border border-border bg-background px-3 py-2.5"
292
- >
293
- <span className="text-sm text-foreground">{ext.label}</span>
294
- <ToggleSwitch checked={ext.enabled} onChange={() => toggleExtension(ext.id)} />
295
- </div>
296
- ))}
297
- </div>
298
- )
299
- }
300
-
301
- // Step 5 Summary
302
- function Step5Summary({ config }: { config: WizardConfig }) {
303
- const enabledClasses = config.detectionClasses.filter((c) => c.enabled)
304
- const enabledStages = config.pipelineStages.filter((s) => s.enabled)
305
- const enabledExtensions = config.extensions.filter((e) => e.enabled)
306
-
307
- return (
308
- <div className="space-y-4">
309
- <p className="text-xs text-foreground-subtle">Review your configuration before applying.</p>
310
-
311
- {/* Detection classes */}
312
- <div className="rounded-lg border border-border bg-background p-3 space-y-1">
313
- <span className="text-[11px] font-semibold text-foreground uppercase tracking-wider">
314
- Detection Classes
315
- </span>
316
- {enabledClasses.length === 0 ? (
317
- <p className="text-xs text-foreground-subtle">None enabled</p>
318
- ) : (
319
- enabledClasses.map((c) => (
320
- <div key={c.id} className="flex justify-between text-xs">
321
- <span className="text-foreground">{c.label}</span>
322
- <span className="text-foreground-subtle">{c.score}% min score</span>
323
- </div>
324
- ))
325
- )}
326
- </div>
327
-
328
- {/* Pipeline stages */}
329
- <div className="rounded-lg border border-border bg-background p-3 space-y-1">
330
- <span className="text-[11px] font-semibold text-foreground uppercase tracking-wider">
331
- Pipeline Stages
332
- </span>
333
- {enabledStages.length === 0 ? (
334
- <p className="text-xs text-foreground-subtle">None enabled</p>
335
- ) : (
336
- <div className="flex flex-wrap gap-1 mt-1">
337
- {enabledStages.map((s) => (
338
- <span
339
- key={s.id}
340
- className="inline-flex items-center rounded-full bg-primary/10 px-2 py-0.5 text-[10px] font-medium text-primary"
341
- >
342
- {s.label}
343
- </span>
344
- ))}
345
- </div>
346
- )}
347
- </div>
348
-
349
- {/* Recording */}
350
- <div className="rounded-lg border border-border bg-background p-3 space-y-1">
351
- <span className="text-[11px] font-semibold text-foreground uppercase tracking-wider">
352
- Recording
353
- </span>
354
- {config.recording.enabled ? (
355
- <div className="text-xs text-foreground space-y-0.5">
356
- <div className="flex justify-between">
357
- <span>Status</span>
358
- <span className="text-success">Enabled</span>
359
- </div>
360
- <div className="flex justify-between">
361
- <span>Retention</span>
362
- <span>{config.recording.retentionDays} days</span>
363
- </div>
364
- <div className="flex justify-between">
365
- <span>Format</span>
366
- <span>.{config.recording.format}</span>
367
- </div>
368
- </div>
369
- ) : (
370
- <p className="text-xs text-foreground-subtle">Disabled</p>
371
- )}
372
- </div>
373
-
374
- {/* Extensions */}
375
- <div className="rounded-lg border border-border bg-background p-3 space-y-1">
376
- <span className="text-[11px] font-semibold text-foreground uppercase tracking-wider">
377
- Extensions
378
- </span>
379
- {enabledExtensions.length === 0 ? (
380
- <p className="text-xs text-foreground-subtle">None enabled</p>
381
- ) : (
382
- <div className="flex flex-wrap gap-1 mt-1">
383
- {enabledExtensions.map((e) => (
384
- <span
385
- key={e.id}
386
- className="inline-flex items-center rounded-full bg-success/10 px-2 py-0.5 text-[10px] font-medium text-success"
387
- >
388
- {e.label}
389
- </span>
390
- ))}
391
- </div>
392
- )}
393
- </div>
394
- </div>
395
- )
396
- }
397
-
398
- // ----- Main Wizard -----
399
-
400
- export function QuickConfigWizard({ open, onClose, deviceId }: QuickConfigWizardProps) {
401
- const [step, setStep] = useState<Step>(0)
402
- const [config, setConfig] = useState<WizardConfig>({
403
- detectionClasses: DEFAULT_DETECTION_CLASSES,
404
- pipelineStages: DEFAULT_PIPELINE_STAGES,
405
- recording: { enabled: false, retentionDays: 14, format: 'mp4' },
406
- extensions: DEFAULT_EXTENSIONS,
407
- })
408
-
409
- if (!open) return null
410
-
411
- function handleApply() {
412
- console.log('[QuickConfigWizard] Applying config for device', deviceId, config)
413
- onClose()
414
- }
415
-
416
- function handleClose() {
417
- setStep(0)
418
- onClose()
419
- }
420
-
421
- return (
422
- <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
423
- <div className="w-full max-w-lg rounded-xl border border-border bg-surface p-6 shadow-2xl">
424
- {/* Header */}
425
- <div className="flex items-center justify-between mb-2">
426
- <div className="flex items-center gap-2">
427
- <Zap className="h-4 w-4 text-primary" />
428
- <h2 className="text-sm font-semibold text-foreground">Quick Config</h2>
429
- </div>
430
- <button
431
- onClick={handleClose}
432
- className="text-foreground-subtle hover:text-foreground transition-colors p-1 rounded"
433
- >
434
- <X className="h-4 w-4" />
435
- </button>
436
- </div>
437
-
438
- {/* Step title */}
439
- <p className="text-xs text-foreground-subtle mb-4">
440
- Step {step + 1} of {STEPS.length} — {STEPS[step]}
441
- </p>
442
-
443
- {/* Step indicator dots */}
444
- <StepIndicator current={step} total={STEPS.length} />
445
-
446
- {/* Step content */}
447
- <div className="min-h-[300px] max-h-[420px] overflow-y-auto">
448
- {step === 0 && (
449
- <Step1DetectionClasses
450
- classes={config.detectionClasses}
451
- onChange={(detectionClasses) => setConfig((prev) => ({ ...prev, detectionClasses }))}
452
- />
453
- )}
454
- {step === 1 && (
455
- <Step2PipelineStages
456
- stages={config.pipelineStages}
457
- onChange={(pipelineStages) => setConfig((prev) => ({ ...prev, pipelineStages }))}
458
- />
459
- )}
460
- {step === 2 && (
461
- <Step3Recording
462
- config={config.recording}
463
- onChange={(recording) => setConfig((prev) => ({ ...prev, recording }))}
464
- />
465
- )}
466
- {step === 3 && (
467
- <Step4Extensions
468
- extensions={config.extensions}
469
- onChange={(extensions) => setConfig((prev) => ({ ...prev, extensions }))}
470
- />
471
- )}
472
- {step === 4 && <Step5Summary config={config} />}
473
- </div>
474
-
475
- {/* Navigation */}
476
- <div className="flex items-center justify-between mt-6 pt-4 border-t border-border">
477
- <button
478
- onClick={() => setStep((s) => (s - 1) as Step)}
479
- disabled={step === 0}
480
- className="inline-flex items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-xs font-medium text-foreground-subtle hover:text-foreground hover:bg-surface-hover disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
481
- >
482
- <ChevronLeft className="h-3.5 w-3.5" />
483
- Back
484
- </button>
485
-
486
- {step < 4 ? (
487
- <button
488
- onClick={() => setStep((s) => (s + 1) as Step)}
489
- className="inline-flex items-center gap-1.5 rounded-lg bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 transition-colors"
490
- >
491
- Next
492
- <ChevronRight className="h-3.5 w-3.5" />
493
- </button>
494
- ) : (
495
- <button
496
- onClick={handleApply}
497
- className="inline-flex items-center gap-1.5 rounded-lg bg-success px-4 py-1.5 text-xs font-medium text-white hover:bg-success/90 transition-colors"
498
- >
499
- <Check className="h-3.5 w-3.5" />
500
- Apply
501
- </button>
502
- )}
503
- </div>
504
- </div>
505
- </div>
506
- )
507
- }
@@ -1,96 +0,0 @@
1
- import { useState } from 'react'
2
-
3
- interface ClassConfig {
4
- id: string
5
- label: string
6
- enabled: boolean
7
- score: number
8
- }
9
-
10
- const DEFAULT_CLASSES: ClassConfig[] = [
11
- { id: 'person', label: 'Person', enabled: true, score: 0.5 },
12
- { id: 'vehicle', label: 'Vehicle', enabled: true, score: 0.5 },
13
- { id: 'animal', label: 'Animal', enabled: false, score: 0.5 },
14
- { id: 'package', label: 'Package', enabled: false, score: 0.5 },
15
- ]
16
-
17
- const DEFAULT_STAGES = [
18
- { id: 'motion_filter', label: 'Motion Filter' },
19
- { id: 'object_detection', label: 'Object Detection' },
20
- { id: 'zone_filter', label: 'Zone Filter' },
21
- { id: 'tracking', label: 'Tracking' },
22
- { id: 'event_aggregation', label: 'Event Aggregation' },
23
- ]
24
-
25
- export function DetectionConfigTab() {
26
- const [classes] = useState<ClassConfig[]>(DEFAULT_CLASSES)
27
- const [stages] = useState(
28
- DEFAULT_STAGES.map((s) => ({ ...s, enabled: true }))
29
- )
30
-
31
- return (
32
- <div className="space-y-6">
33
- {/* Primary Detection Classes */}
34
- <div className="rounded-lg border border-border bg-surface overflow-hidden">
35
- <div className="border-b border-border px-4 py-2.5">
36
- <h2 className="text-xs font-semibold text-foreground uppercase tracking-wider">Detection Classes</h2>
37
- <p className="text-[11px] text-foreground-subtle mt-0.5">Read-only — configuration editing coming soon</p>
38
- </div>
39
- <div className="divide-y divide-border">
40
- {classes.map((cls) => (
41
- <div key={cls.id} className="flex items-center gap-4 px-4 py-3">
42
- <input
43
- type="checkbox"
44
- checked={cls.enabled}
45
- readOnly
46
- className="h-3.5 w-3.5 rounded border-border text-primary accent-primary cursor-not-allowed opacity-70"
47
- />
48
- <span className="w-20 text-xs font-medium text-foreground">{cls.label}</span>
49
- <div className="flex flex-1 items-center gap-3">
50
- <input
51
- type="range"
52
- min={0}
53
- max={1}
54
- step={0.01}
55
- value={cls.score}
56
- readOnly
57
- className="flex-1 accent-primary cursor-not-allowed opacity-70"
58
- />
59
- <span className="w-10 text-right text-[11px] font-mono text-foreground-subtle">
60
- {cls.score.toFixed(2)}
61
- </span>
62
- </div>
63
- <span className="text-[10px] text-foreground-subtle w-12">min score</span>
64
- </div>
65
- ))}
66
- </div>
67
- </div>
68
-
69
- {/* Pipeline Stages */}
70
- <div className="rounded-lg border border-border bg-surface overflow-hidden">
71
- <div className="border-b border-border px-4 py-2.5">
72
- <h2 className="text-xs font-semibold text-foreground uppercase tracking-wider">Pipeline Stages</h2>
73
- <p className="text-[11px] text-foreground-subtle mt-0.5">Read-only — stage control coming soon</p>
74
- </div>
75
- <div className="divide-y divide-border">
76
- {stages.map((stage) => (
77
- <div key={stage.id} className="flex items-center justify-between px-4 py-3">
78
- <span className="text-xs text-foreground">{stage.label}</span>
79
- <div
80
- className={`relative inline-flex h-5 w-9 flex-shrink-0 cursor-not-allowed rounded-full border-2 border-transparent transition-colors opacity-70 ${
81
- stage.enabled ? 'bg-primary' : 'bg-foreground-subtle/30'
82
- }`}
83
- >
84
- <span
85
- className={`inline-block h-4 w-4 transform rounded-full bg-white shadow ring-0 transition-transform ${
86
- stage.enabled ? 'translate-x-4' : 'translate-x-0'
87
- }`}
88
- />
89
- </div>
90
- </div>
91
- ))}
92
- </div>
93
- </div>
94
- </div>
95
- )
96
- }
@@ -1,19 +0,0 @@
1
- import { Bell } from 'lucide-react'
2
-
3
- export function EventsTab() {
4
- return (
5
- <div className="rounded-lg border border-border bg-surface overflow-hidden">
6
- <div className="border-b border-border px-4 py-2.5 flex items-center justify-between">
7
- <h2 className="text-xs font-semibold text-foreground uppercase tracking-wider">Event Timeline</h2>
8
- <span className="inline-flex items-center gap-1 rounded-full bg-foreground-subtle/10 px-2 py-0.5 text-[10px] text-foreground-subtle">
9
- 0 events
10
- </span>
11
- </div>
12
- <div className="flex flex-col items-center justify-center py-16 text-foreground-subtle">
13
- <Bell className="h-8 w-8 mb-3 opacity-30" />
14
- <p className="text-sm">No events</p>
15
- <p className="text-xs mt-1 opacity-70">Device events will appear here as they occur</p>
16
- </div>
17
- </div>
18
- )
19
- }
@@ -1,22 +0,0 @@
1
- import { ScrollText } from 'lucide-react'
2
-
3
- export function LogsTab() {
4
- return (
5
- <div className="rounded-lg border border-border bg-surface overflow-hidden">
6
- <div className="border-b border-border px-4 py-2.5 flex items-center justify-between">
7
- <div>
8
- <h2 className="text-xs font-semibold text-foreground uppercase tracking-wider">Device Logs</h2>
9
- <p className="text-[11px] text-foreground-subtle mt-0.5">Scoped log stream for this device</p>
10
- </div>
11
- <span className="inline-flex items-center gap-1.5 rounded-full bg-info/10 px-2.5 py-0.5 text-[10px] font-medium text-info">
12
- Coming with backend integration
13
- </span>
14
- </div>
15
- <div className="flex flex-col items-center justify-center py-16 text-foreground-subtle">
16
- <ScrollText className="h-8 w-8 mb-3 opacity-30" />
17
- <p className="text-sm font-medium text-foreground">Device logs</p>
18
- <p className="text-xs mt-1 opacity-70">Device logs — coming with backend integration</p>
19
- </div>
20
- </div>
21
- )
22
- }