Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
281 changes: 281 additions & 0 deletions frontend/src/types/admin.ts
Original file line number Diff line number Diff line change
@@ -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<string, 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 }[];
}
7 changes: 7 additions & 0 deletions frontend/src/types/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// REST request/response envelopes (non-admin).

// Setup status from GET /api/setup/status
export interface SetupStatus {
needsSetup: boolean;
publicAccess: boolean;
}
25 changes: 25 additions & 0 deletions frontend/src/types/auth.ts
Original file line number Diff line number Diff line change
@@ -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;
}
40 changes: 40 additions & 0 deletions frontend/src/types/call.ts
Original file line number Diff line number Diff line change
@@ -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;
}
Loading
Loading