@camstack/addon-admin-ui 0.1.1 → 0.1.3

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-DjELGD4R.css +1 -0
  2. package/dist/assets/index-w55PwKyu.js +598 -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 +5 -1
  8. package/src/App.tsx +0 -71
  9. package/src/components/addons/AddonCard.tsx +0 -339
  10. package/src/components/addons/AddonUploadZone.tsx +0 -307
  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 -119
  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 -171
  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 -113
  68. package/src/components/metrics/IntegrationUsage.tsx +0 -90
  69. package/src/components/metrics/PipelineStatus.tsx +0 -105
  70. package/src/components/metrics/ProcessResources.tsx +0 -139
  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 -238
  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 -224
  98. package/src/pages/Integrations.tsx +0 -330
  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 -525
  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 -210
  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,269 +0,0 @@
1
- import { useState, useMemo } from 'react'
2
- import { useQuery } from '@tanstack/react-query'
3
- import { Film, Calendar, ChevronLeft, ChevronRight } from 'lucide-react'
4
- import { useTranslation } from 'react-i18next'
5
- import { useBackendClient } from '../hooks/useBackendClient'
6
- import { TimelineBar, type RecordingSegment, type TimelineEvent } from '../components/timeline/TimelineBar'
7
-
8
- // ---------------------------------------------------------------------------
9
- // Class → colour mapping
10
- // ---------------------------------------------------------------------------
11
- const CLASS_COLORS: Record<string, string> = {
12
- person: '#f59e0b',
13
- vehicle: '#3b82f6',
14
- car: '#3b82f6',
15
- face: '#a855f7',
16
- plate: '#ec4899',
17
- animal: '#10b981',
18
- motion: '#ef4444',
19
- unknown: '#6b7280',
20
- }
21
-
22
- function colorForClass(cls: string): string {
23
- return CLASS_COLORS[cls.toLowerCase()] ?? CLASS_COLORS['unknown']!
24
- }
25
-
26
- // ---------------------------------------------------------------------------
27
- // Date helpers
28
- // ---------------------------------------------------------------------------
29
- function startOfDay(date: Date): number {
30
- const d = new Date(date)
31
- d.setHours(0, 0, 0, 0)
32
- return d.getTime()
33
- }
34
-
35
- function formatDateInput(ms: number): string {
36
- return new Date(ms).toISOString().slice(0, 10)
37
- }
38
-
39
- function parseDateInput(value: string): number {
40
- return new Date(value + 'T00:00:00').getTime()
41
- }
42
-
43
- // ---------------------------------------------------------------------------
44
- // Placeholder segments (shown when no real API data exists)
45
- // ---------------------------------------------------------------------------
46
- function buildPlaceholderSegments(dayStartMs: number): readonly RecordingSegment[] {
47
- return [
48
- { startMs: dayStartMs + 0 * 3_600_000, endMs: dayStartMs + 4 * 3_600_000 },
49
- { startMs: dayStartMs + 6 * 3_600_000, endMs: dayStartMs + 10 * 3_600_000 },
50
- { startMs: dayStartMs + 12 * 3_600_000, endMs: dayStartMs + 16 * 3_600_000 },
51
- { startMs: dayStartMs + 18 * 3_600_000, endMs: dayStartMs + 23 * 3_600_000 },
52
- ]
53
- }
54
-
55
- function buildPlaceholderEvents(dayStartMs: number): readonly TimelineEvent[] {
56
- const classes = ['person', 'vehicle', 'face', 'motion']
57
- return Array.from({ length: 24 }, (_, i) => ({
58
- id: `placeholder-${i}`,
59
- timestampMs: dayStartMs + Math.floor(Math.random() * 24 * 3_600_000),
60
- label: classes[i % classes.length]!,
61
- color: colorForClass(classes[i % classes.length]!),
62
- })).sort((a, b) => a.timestampMs - b.timestampMs)
63
- }
64
-
65
- // ---------------------------------------------------------------------------
66
- // Component
67
- // ---------------------------------------------------------------------------
68
- export function TimelinePage() {
69
- const { t } = useTranslation()
70
- const client = useBackendClient()
71
-
72
- const today = startOfDay(new Date())
73
- const [selectedDayMs, setSelectedDayMs] = useState(today)
74
- const [selectedCameraId, setSelectedCameraId] = useState<string | null>(null)
75
- const [playheadMs, setPlayheadMs] = useState(today + 12 * 3_600_000)
76
-
77
- const dayStartMs = selectedDayMs
78
- const dayEndMs = selectedDayMs + 24 * 3_600_000
79
-
80
- // Fetch devices to populate camera tabs
81
- const { data: devices, isLoading: devicesLoading } = useQuery({
82
- queryKey: ['devices'],
83
- queryFn: () => client.listDevices(),
84
- staleTime: 30_000,
85
- })
86
-
87
- const deviceList = (devices ?? []) as unknown as Array<Record<string, unknown>>
88
-
89
- const cameras = useMemo(() => {
90
- const list = deviceList.filter((d) => {
91
- const type = String(d.type ?? '').toLowerCase()
92
- const caps = (d.capabilities ?? []) as string[]
93
- return (
94
- type === 'camera' ||
95
- type.includes('camera') ||
96
- caps.some((c) => c.toLowerCase().includes('video') || c.toLowerCase().includes('stream'))
97
- )
98
- })
99
- return list.map((d) => ({ id: String(d.id), name: String(d.name ?? d.id) }))
100
- }, [deviceList])
101
-
102
- // Auto-select first camera
103
- const activeCameraId = selectedCameraId ?? cameras[0]?.id ?? null
104
-
105
- // Fetch real events for the selected camera on the selected day
106
- const { data: rawEvents } = useQuery({
107
- queryKey: ['events', activeCameraId, dayStartMs],
108
- queryFn: (): Promise<unknown> =>
109
- activeCameraId ? client.getEvents(activeCameraId, { limit: 200 }) : Promise.resolve(null),
110
- enabled: !!activeCameraId,
111
- staleTime: 60_000,
112
- })
113
-
114
- const timelineEvents: readonly TimelineEvent[] = useMemo(() => {
115
- const raw = rawEvents as unknown
116
- // getEvents returns { events: [...], total: n } or an array depending on router version
117
- const evtsArr: unknown[] =
118
- Array.isArray(raw)
119
- ? (raw as unknown[])
120
- : raw != null && typeof raw === 'object' && Array.isArray((raw as Record<string, unknown>)['events'])
121
- ? ((raw as Record<string, unknown>)['events'] as unknown[])
122
- : []
123
- const evts = evtsArr as Array<Record<string, unknown>>
124
- if (evts.length === 0) {
125
- return activeCameraId ? buildPlaceholderEvents(dayStartMs) : []
126
- }
127
- return evts.map((ev, idx) => {
128
- const cls = String((ev.class ?? ev.type ?? ev.label ?? 'unknown')).toLowerCase()
129
- const ts = typeof ev.timestamp === 'number' ? ev.timestamp : dayStartMs
130
- return {
131
- id: String(ev.id ?? idx),
132
- timestampMs: ts,
133
- label: cls,
134
- color: colorForClass(cls),
135
- }
136
- })
137
- }, [rawEvents, activeCameraId, dayStartMs])
138
-
139
- // Use placeholder segments (no real segment API exposed yet)
140
- const segments: readonly RecordingSegment[] = useMemo(
141
- () => (activeCameraId ? buildPlaceholderSegments(dayStartMs) : []),
142
- [activeCameraId, dayStartMs],
143
- )
144
-
145
- function navigateDay(delta: number) {
146
- setSelectedDayMs((prev) => prev + delta * 24 * 3_600_000)
147
- setPlayheadMs((prev) => prev + delta * 24 * 3_600_000)
148
- }
149
-
150
- return (
151
- <div className="flex flex-col h-full p-6 gap-5 overflow-hidden">
152
- {/* ── Header ── */}
153
- <div className="flex items-center justify-between gap-4 shrink-0">
154
- <h1 className="text-lg font-semibold text-foreground">{t('nav.timeline', 'Timeline')}</h1>
155
-
156
- {/* Date navigation */}
157
- <div className="flex items-center gap-2">
158
- <button
159
- onClick={() => navigateDay(-1)}
160
- className="p-1 rounded hover:bg-surface-hover text-foreground-subtle hover:text-foreground transition-colors"
161
- >
162
- <ChevronLeft className="h-4 w-4" />
163
- </button>
164
- <div className="relative flex items-center">
165
- <Calendar className="absolute left-2 h-3.5 w-3.5 text-foreground-subtle pointer-events-none" />
166
- <input
167
- type="date"
168
- value={formatDateInput(selectedDayMs)}
169
- onChange={(e) => {
170
- if (e.target.value) {
171
- const ms = parseDateInput(e.target.value)
172
- setSelectedDayMs(ms)
173
- setPlayheadMs(ms + 12 * 3_600_000)
174
- }
175
- }}
176
- className="rounded-lg border border-border bg-surface pl-7 pr-3 py-1.5 text-xs text-foreground focus:outline-none focus:border-primary/50"
177
- />
178
- </div>
179
- <button
180
- onClick={() => navigateDay(1)}
181
- className="p-1 rounded hover:bg-surface-hover text-foreground-subtle hover:text-foreground transition-colors"
182
- disabled={selectedDayMs >= today}
183
- >
184
- <ChevronRight className="h-4 w-4" />
185
- </button>
186
- </div>
187
- </div>
188
-
189
- {/* ── Camera selector tabs ── */}
190
- <div className="flex gap-1.5 shrink-0 overflow-x-auto pb-1">
191
- {devicesLoading && (
192
- <div className="h-8 w-48 rounded-lg bg-surface animate-pulse" />
193
- )}
194
- {!devicesLoading && cameras.length === 0 && (
195
- <span className="text-xs text-foreground-subtle">No cameras found</span>
196
- )}
197
- {cameras.map((cam) => {
198
- const active = cam.id === activeCameraId
199
- return (
200
- <button
201
- key={cam.id}
202
- onClick={() => setSelectedCameraId(cam.id)}
203
- className={`shrink-0 rounded-lg px-3 py-1.5 text-[12px] font-medium transition-all border ${
204
- active
205
- ? 'bg-primary/12 text-primary border-primary/30'
206
- : 'bg-surface text-foreground-subtle border-border hover:bg-surface-hover hover:text-foreground'
207
- }`}
208
- >
209
- {cam.name}
210
- </button>
211
- )
212
- })}
213
- </div>
214
-
215
- {/* ── Video player placeholder ── */}
216
- <div className="relative rounded-xl border border-border bg-black shrink-0 overflow-hidden" style={{ aspectRatio: '16/9', maxHeight: '45vh' }}>
217
- <div className="absolute inset-0 flex flex-col items-center justify-center gap-3 text-foreground-subtle">
218
- <Film className="h-12 w-12 opacity-20" />
219
- <p className="text-sm opacity-50">
220
- {activeCameraId
221
- ? `${cameras.find((c) => c.id === activeCameraId)?.name ?? activeCameraId} — seek to play`
222
- : 'Select a camera'}
223
- </p>
224
- </div>
225
-
226
- {/* Playhead timestamp overlay */}
227
- <div className="absolute top-3 left-3 rounded bg-black/60 px-2 py-1 text-[11px] text-white font-mono">
228
- {new Date(playheadMs).toLocaleTimeString()}
229
- </div>
230
- </div>
231
-
232
- {/* ── Timeline scrub bar ── */}
233
- <div className="rounded-xl border border-border bg-surface px-4 pt-3 pb-4 shrink-0">
234
- {/* Legend */}
235
- <div className="flex items-center gap-4 mb-3">
236
- <span className="text-[10px] font-semibold text-foreground-subtle uppercase tracking-wider">Timeline</span>
237
- <div className="flex items-center gap-1.5">
238
- <span className="inline-block h-2 w-4 rounded-sm bg-success/60" />
239
- <span className="text-[10px] text-foreground-subtle">Recording</span>
240
- </div>
241
- {Array.from(new Set(timelineEvents.map((e) => e.label))).slice(0, 5).map((cls) => (
242
- <div key={cls} className="flex items-center gap-1.5">
243
- <span
244
- className="inline-block h-2.5 w-2.5 rounded-full"
245
- style={{ backgroundColor: colorForClass(cls) }}
246
- />
247
- <span className="text-[10px] text-foreground-subtle capitalize">{cls}</span>
248
- </div>
249
- ))}
250
- </div>
251
-
252
- {activeCameraId ? (
253
- <TimelineBar
254
- startMs={dayStartMs}
255
- endMs={dayEndMs}
256
- segments={segments}
257
- events={timelineEvents}
258
- playheadMs={playheadMs}
259
- onSeek={setPlayheadMs}
260
- />
261
- ) : (
262
- <div className="h-8 rounded bg-surface-hover flex items-center justify-center text-xs text-foreground-subtle">
263
- Select a camera to view timeline
264
- </div>
265
- )}
266
- </div>
267
- </div>
268
- )
269
- }