diff --git a/CHANGELOG.md b/CHANGELOG.md index 401f0c3..5118a21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `useAdminActivity`, `useAdminLogs`, `useWsQuery`), each with a barrel `index.ts`. All call sites have been updated to the new specific paths. No runtime behaviour change. +- Frontend `types/index.ts` god-file split into topic-scoped modules + (`call.ts`, `config.ts`, `ws.ts`, `auth.ts`, `api.ts`, `admin.ts`, + `ui.ts`). The original `index.ts` is now a barrel that re-exports + everything, so all existing `@/types` imports keep working unchanged. + New code can also import from a specific module (e.g. `@/types/admin`). ### Fixed diff --git a/frontend/src/types/admin.ts b/frontend/src/types/admin.ts new file mode 100644 index 0000000..f0b9734 --- /dev/null +++ b/frontend/src/types/admin.ts @@ -0,0 +1,281 @@ +// Admin DTOs (mirrors of ADM/ADMRES payloads and admin REST envelopes). + +export interface AdminUser { + id: number; + username: string; + role: "admin" | "listener"; + disabled: number; // 0 or 1 + systemsJson: string | null; + expiration: number | null; // unix timestamp + limit: number | null; // concurrent connection limit + createdAt: number; + updatedAt: number; +} + +export interface AdminSystem { + id: number; + systemId: number; + label: string; + autoPopulateTalkgroups: number; + blacklistsJson: string | null; + led: string | null; + order: number; +} + +export interface AdminTalkgroup { + id: number; + systemId: number; + talkgroupId: number; + label: string | null; + name: string | null; + frequency: number | null; + led: string | null; + groupId: number | null; + tagId: number | null; + order: number; +} + +export interface AdminUnit { + id: number; + systemId: number; + unitId: number; + label: string | null; + order: number; +} + +export interface AdminGroup { + id: number; + label: string; +} + +export interface AdminTag { + id: number; + label: string; +} + +export interface AdminApiKey { + id: number; + fingerprint: string; + ident: string | null; + disabled: number; + systemsJson: string | null; + callRateLimit: number | null; + order: number; +} + +export interface AdminApiKeyCreateResponse extends AdminApiKey { + createdKey: string; +} + +export interface AdminDownstream { + id: number; + url: string; + hasApiKey: boolean; + systemsJson: string | null; + disabled: number; + order: number; +} + +export interface AdminDownstreamCreate { + url: string; + apiKey: string; + systemsJson: string | null; + disabled: number; + order: number; +} + +export interface AdminDownstreamUpdate { + id: number; + url: string; + apiKey: string; + systemsJson: string | null; + disabled: number; + order: number; +} + +export interface AdminWebhook { + id: number; + url: string; + type: string; + secret: string | null; + systemsJson: string | null; + disabled: number; + order: number; +} + +export interface AdminSetting { + key: string; + value: string; +} + +export interface Capabilities { + ffmpeg: boolean; + fdkAac: boolean; + whisper: boolean; +} + +export interface ConfigResponse { + settings: AdminSetting[]; + capabilities: Capabilities; +} + +export interface AdminLog { + dateTime: number; + level: string; + message: string; + attrs?: Record; +} + +// User create/update payload +export interface CreateUserPayload { + username: string; + password: string; + role: "admin" | "listener"; + disabled?: number; + systemsJson?: string | null; + expiration?: number | null; + limit?: number | null; +} + +export interface UpdateUserPayload { + username?: string; + password?: string; + role?: "admin" | "listener"; + disabled?: number; + systemsJson?: string | null; + expiration?: number | null; + limit?: number | null; +} + +export interface AdminDirMonitor { + id: number; + directory: string; + type: string; + mask: string | null; + extension: string | null; + frequency: number | null; + delay: number | null; + deleteAfter: number; + usePolling: number; + disabled: number; + systemId: number | null; + talkgroupId: number | null; + order: number; +} + +// --- RadioReference enrichment types --- + +export interface RRTalkgroupCandidate { + row: number; + talkgroupId: number; + label?: string; + name?: string; + group?: string; + tag?: string; + led?: string; + order?: number; +} + +export interface RRPreviewRow extends RRTalkgroupCandidate { + matched: boolean; + wouldUpdate: boolean; + wouldUpdateFields: string[]; + skipReason?: string; +} + +export interface RRRowError { + row: number; + reason: string; +} + +export interface RRPreviewResponse { + processed: number; + matched: number; + wouldUpdate: number; + skipped: number; + errors: number; + rowErrors: RRRowError[]; + rows: RRPreviewRow[]; +} + +export interface RRApplyRequest { + systemId: number; + candidates: RRTalkgroupCandidate[]; + mergeMode: string; + selectedFields: string[]; +} + +export interface RRApplyResponse { + processed: number; + matched: number; + updated: number; + skipped: number; + errors: number; + rowErrors: RRRowError[]; +} + +// --- Shared Links (admin) --- + +export interface SharedLinkAdmin { + id: number; + callId: number; + token: string; + createdAt: number; + sharedBy: string; + dateTime: number; + duration: number; + systemLabel: string; + talkgroupLabel: string; + talkgroupName: string; + expiresAt: number | null; +} + +// --- Server filesystem types --- + +export interface ServerDirectoryEntry { + name: string; + path: string; +} + +export interface ServerDirectoryListResponse { + path: string; + parent: string | null; + directories: ServerDirectoryEntry[]; +} + +// --- Transcription types --- + +export interface TranscriptionStatus { + enabled: boolean; + url: string; + model: string; + language: string; + diarize: boolean; + liveDisplay: boolean; + connected: boolean; +} + +export interface WhisperModel { + id: string; + object: string; + path: string; + created: number; + owned_by: string; +} + +export interface TranscriptionModelsResponse { + object: string; + models: WhisperModel[]; +} + +export interface TranscriptionStats { + total: number; + recent24h: number; + avgDurationMs: number; + minDurationMs: number; + maxDurationMs: number; + queueDepth: number; + poolEnabled: boolean; + byLanguage: { language: string; count: number }[]; + byModel: { model: string; count: number }[]; +} diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts new file mode 100644 index 0000000..fe375c6 --- /dev/null +++ b/frontend/src/types/api.ts @@ -0,0 +1,7 @@ +// REST request/response envelopes (non-admin). + +// Setup status from GET /api/setup/status +export interface SetupStatus { + needsSetup: boolean; + publicAccess: boolean; +} diff --git a/frontend/src/types/auth.ts b/frontend/src/types/auth.ts new file mode 100644 index 0000000..6a40dbf --- /dev/null +++ b/frontend/src/types/auth.ts @@ -0,0 +1,25 @@ +// Authentication request/response shapes. + +export interface LoginResponse { + token: string; + user: { + id: number; + username: string; + role: string; + }; + passwordNeedChange: boolean; +} + +export interface RefreshResponse { + token: string; + user: { + id: number; + username: string; + role: string; + }; +} + +export interface ChangePasswordRequest { + currentPassword: string; + newPassword: string; +} diff --git a/frontend/src/types/call.ts b/frontend/src/types/call.ts new file mode 100644 index 0000000..09a8ed3 --- /dev/null +++ b/frontend/src/types/call.ts @@ -0,0 +1,40 @@ +// Call data from WS CAL event or search results. + +export interface Call { + id: number; + audioName: string; + audioType: string; + dateTime: number; // unix timestamp + systemId: number; // radio system ID + system: number; // DB system ID + talkgroupId: number; // radio TG ID + talkgroup: number; // DB TG ID + frequency?: number; // Hz + duration?: number; // ms + source?: number; // unit ID + sources?: string; // JSON array + frequencies?: string; // JSON array + patches?: string; // JSON array + site?: string; // receiver site name + channel?: string; // channel identifier + decoder?: string; // decoder type (e.g. "P25 Phase 1") + errorCount?: number; // P25 error count + spikeCount?: number; // P25 spike count + talkerAlias?: string; // DMR/P25 talker alias + systemLabel?: string; // populated from config + talkgroupLabel?: string; // populated from config + talkgroupName?: string; // populated from config + talkgroupTag?: string; // populated from config + talkgroupGroup?: string; // populated from config + talkgroupLedColor?: string; // CSS color for LED + transcript?: string; + transcriptSegments?: TranscriptionSegment[]; + audioUrl?: string; // object URL for audio playback +} + +export interface TranscriptionSegment { + speaker?: string; + start: number; + end: number; + text: string; +} diff --git a/frontend/src/types/config.ts b/frontend/src/types/config.ts new file mode 100644 index 0000000..ec22c21 --- /dev/null +++ b/frontend/src/types/config.ts @@ -0,0 +1,34 @@ +// Scanner / system / talkgroup configuration delivered via WS CFG/VER events. + +export interface SystemConfig { + id: number; + systemId: number; + label: string; + ledColor: string; + talkgroups: TalkgroupConfig[]; +} + +export interface TalkgroupConfig { + id: number; + talkgroupId: number; + label: string; + name: string; + tag: string; + group: string; + ledColor: string; // CSS color string + frequency?: number; +} + +export interface ScannerConfig { + systems: SystemConfig[]; + branding?: string; + email?: string; + version?: string; + time12hFormat: boolean; + showListenersCount: boolean; + playbackGoesLive: boolean; + shareableLinks: boolean; + keypadBeeps: string; + transcriptionEnabled: boolean; + liveTranscriptDisplay: boolean; +} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index d518b33..d04aedc 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -1,413 +1,10 @@ -// Call data from WS CAL event or search results -export interface Call { - id: number; - audioName: string; - audioType: string; - dateTime: number; // unix timestamp - systemId: number; // radio system ID - system: number; // DB system ID - talkgroupId: number; // radio TG ID - talkgroup: number; // DB TG ID - frequency?: number; // Hz - duration?: number; // ms - source?: number; // unit ID - sources?: string; // JSON array - frequencies?: string; // JSON array - patches?: string; // JSON array - site?: string; // receiver site name - channel?: string; // channel identifier - decoder?: string; // decoder type (e.g. "P25 Phase 1") - errorCount?: number; // P25 error count - spikeCount?: number; // P25 spike count - talkerAlias?: string; // DMR/P25 talker alias - systemLabel?: string; // populated from config - talkgroupLabel?: string; // populated from config - talkgroupName?: string; // populated from config - talkgroupTag?: string; // populated from config - talkgroupGroup?: string; // populated from config - talkgroupLedColor?: string; // CSS color for LED - transcript?: string; - transcriptSegments?: TranscriptionSegment[]; - audioUrl?: string; // object URL for audio playback -} - -export interface TranscriptionSegment { - speaker?: string; - start: number; - end: number; - text: string; -} - -// System/talkgroup config from CFG event -export interface SystemConfig { - id: number; - systemId: number; - label: string; - ledColor: string; - talkgroups: TalkgroupConfig[]; -} - -export interface TalkgroupConfig { - id: number; - talkgroupId: number; - label: string; - name: string; - tag: string; - group: string; - ledColor: string; // CSS color string - frequency?: number; -} - -// Scanner configuration from CFG/VER events -export interface ScannerConfig { - systems: SystemConfig[]; - branding?: string; - email?: string; - version?: string; - time12hFormat: boolean; - showListenersCount: boolean; - playbackGoesLive: boolean; - shareableLinks: boolean; - keypadBeeps: string; - transcriptionEnabled: boolean; - liveTranscriptDisplay: boolean; -} - -// WS message: JSON array [command, payload?, flags?] -export type WsCommand = - | "CAL" - | "CFG" - | "XPR" - | "LCL" - | "LSC" - | "LFM" - | "MAX" - | "VER" - | "TRN" - | "ADM_EVT" - | "ADM_REQ" - | "ADM_RES"; - -// Setup status from GET /api/setup/status -export interface SetupStatus { - needsSetup: boolean; - publicAccess: boolean; -} - -// Auth login response -export interface LoginResponse { - token: string; - user: { - id: number; - username: string; - role: string; - }; - passwordNeedChange: boolean; -} - -// Auth refresh response -export interface RefreshResponse { - token: string; - user: { - id: number; - username: string; - role: string; - }; -} - -// For avoid timer tracking -export interface AvoidEntry { - talkgroupId: number; - expiresAt: number; // unix ms timestamp, 0 = permanent -} - -// Connection status for WS -export type ConnectionStatus = "connecting" | "connected" | "disconnected"; - -// ─── Admin resource types ─── - -export interface AdminUser { - id: number; - username: string; - role: "admin" | "listener"; - disabled: number; // 0 or 1 - systemsJson: string | null; - expiration: number | null; // unix timestamp - limit: number | null; // concurrent connection limit - createdAt: number; - updatedAt: number; -} - -export interface AdminSystem { - id: number; - systemId: number; - label: string; - autoPopulateTalkgroups: number; - blacklistsJson: string | null; - led: string | null; - order: number; -} - -export interface AdminTalkgroup { - id: number; - systemId: number; - talkgroupId: number; - label: string | null; - name: string | null; - frequency: number | null; - led: string | null; - groupId: number | null; - tagId: number | null; - order: number; -} - -export interface AdminUnit { - id: number; - systemId: number; - unitId: number; - label: string | null; - order: number; -} - -export interface AdminGroup { - id: number; - label: string; -} - -export interface AdminTag { - id: number; - label: string; -} - -export interface AdminApiKey { - id: number; - fingerprint: string; - ident: string | null; - disabled: number; - systemsJson: string | null; - callRateLimit: number | null; - order: number; -} - -export interface AdminApiKeyCreateResponse extends AdminApiKey { - createdKey: string; -} - -export interface AdminDownstream { - id: number; - url: string; - hasApiKey: boolean; - systemsJson: string | null; - disabled: number; - order: number; -} - -export interface AdminDownstreamCreate { - url: string; - apiKey: string; - systemsJson: string | null; - disabled: number; - order: number; -} - -export interface AdminDownstreamUpdate { - id: number; - url: string; - apiKey: string; - systemsJson: string | null; - disabled: number; - order: number; -} - -export interface AdminWebhook { - id: number; - url: string; - type: string; - secret: string | null; - systemsJson: string | null; - disabled: number; - order: number; -} - -export interface AdminSetting { - key: string; - value: string; -} - -export interface Capabilities { - ffmpeg: boolean; - fdkAac: boolean; - whisper: boolean; -} - -export interface ConfigResponse { - settings: AdminSetting[]; - capabilities: Capabilities; -} - -export interface AdminLog { - dateTime: number; - level: string; - message: string; - attrs?: Record; -} - -// Password change request -export interface ChangePasswordRequest { - currentPassword: string; - newPassword: string; -} - -// User create/update payload -export interface CreateUserPayload { - username: string; - password: string; - role: "admin" | "listener"; - disabled?: number; - systemsJson?: string | null; - expiration?: number | null; - limit?: number | null; -} - -export interface UpdateUserPayload { - username?: string; - password?: string; - role?: "admin" | "listener"; - disabled?: number; - systemsJson?: string | null; - expiration?: number | null; - limit?: number | null; -} - -export interface AdminDirMonitor { - id: number; - directory: string; - type: string; - mask: string | null; - extension: string | null; - frequency: number | null; - delay: number | null; - deleteAfter: number; - usePolling: number; - disabled: number; - systemId: number | null; - talkgroupId: number | null; - order: number; -} - -// --- RadioReference enrichment types --- - -export interface RRTalkgroupCandidate { - row: number; - talkgroupId: number; - label?: string; - name?: string; - group?: string; - tag?: string; - led?: string; - order?: number; -} - -export interface RRPreviewRow extends RRTalkgroupCandidate { - matched: boolean; - wouldUpdate: boolean; - wouldUpdateFields: string[]; - skipReason?: string; -} - -export interface RRRowError { - row: number; - reason: string; -} - -export interface RRPreviewResponse { - processed: number; - matched: number; - wouldUpdate: number; - skipped: number; - errors: number; - rowErrors: RRRowError[]; - rows: RRPreviewRow[]; -} - -export interface RRApplyRequest { - systemId: number; - candidates: RRTalkgroupCandidate[]; - mergeMode: string; - selectedFields: string[]; -} - -export interface RRApplyResponse { - processed: number; - matched: number; - updated: number; - skipped: number; - errors: number; - rowErrors: RRRowError[]; -} - -// --- Shared Links (admin) --- - -export interface SharedLinkAdmin { - id: number; - callId: number; - token: string; - createdAt: number; - sharedBy: string; - dateTime: number; - duration: number; - systemLabel: string; - talkgroupLabel: string; - talkgroupName: string; - expiresAt: number | null; -} - -// --- Server filesystem types --- - -export interface ServerDirectoryEntry { - name: string; - path: string; -} - -export interface ServerDirectoryListResponse { - path: string; - parent: string | null; - directories: ServerDirectoryEntry[]; -} - -// --- Transcription types --- - -export interface TranscriptionStatus { - enabled: boolean; - url: string; - model: string; - language: string; - diarize: boolean; - liveDisplay: boolean; - connected: boolean; -} - -export interface WhisperModel { - id: string; - object: string; - path: string; - created: number; - owned_by: string; -} - -export interface TranscriptionModelsResponse { - object: string; - models: WhisperModel[]; -} - -export interface TranscriptionStats { - total: number; - recent24h: number; - avgDurationMs: number; - minDurationMs: number; - maxDurationMs: number; - queueDepth: number; - poolEnabled: boolean; - byLanguage: { language: string; count: number }[]; - byModel: { model: string; count: number }[]; -} +// Barrel re-export for the types tree. Existing `@/types` imports keep +// working; new code can also import from a specific module +// (`@/types/call`, `@/types/admin`, etc.). +export * from "./admin"; +export * from "./api"; +export * from "./auth"; +export * from "./call"; +export * from "./config"; +export * from "./ui"; +export * from "./ws"; diff --git a/frontend/src/types/ui.ts b/frontend/src/types/ui.ts new file mode 100644 index 0000000..b68bbd4 --- /dev/null +++ b/frontend/src/types/ui.ts @@ -0,0 +1,7 @@ +// Purely client-side view-state types. + +// Avoid timer tracking +export interface AvoidEntry { + talkgroupId: number; + expiresAt: number; // unix ms timestamp, 0 = permanent +} diff --git a/frontend/src/types/ws.ts b/frontend/src/types/ws.ts new file mode 100644 index 0000000..95ce172 --- /dev/null +++ b/frontend/src/types/ws.ts @@ -0,0 +1,19 @@ +// WS framing types. + +// WS message: JSON array [command, payload?, flags?] +export type WsCommand = + | "CAL" + | "CFG" + | "XPR" + | "LCL" + | "LSC" + | "LFM" + | "MAX" + | "VER" + | "TRN" + | "ADM_EVT" + | "ADM_REQ" + | "ADM_RES"; + +// Connection status for WS +export type ConnectionStatus = "connecting" | "connected" | "disconnected";