feat(frontend): show transcription + keyword search + channel/date filters + speaker leaderboard
- cards now render transcription snippet (expandable); no-transcript placeholder - toolbar: search box (transcription+username ILIKE), speaker filter (existing), channel filter, start/end date range, RESET - new DECK/LEADERBOARD tabs: leaderboard = per-speaker clips, est duration, transcribed words (useRecordingsSummary → recordings.summary); click a row jumps the deck to that speaker's clips - hooks: filter-driven SWR cache key (fields serialized), representative key matcher for delete/WS-sync across all filter views; useRecordingsSummary hook - recordingsApi.list generalized to RecordingListParams (q/startDate/endDate)
This commit is contained in:
1 parent
22c09ecd28
commit
e12b1302ff
5 files changed
+609
-273
No files matched your search
@@ -1,20 +1,34 @@
|
||||
import { orpc } from "@/lib/orpc/client";
|
||||
import type { PaginatedRecordings } from "@/lib/types";
|
||||
import type { PaginatedRecordings, SpeakerSummary } from "@/lib/types";
|
||||
|
||||
export interface RecordingListParams {
|
||||
limit?: number;
|
||||
channelId?: string;
|
||||
userId?: string;
|
||||
cursor?: string;
|
||||
/** keyword search against transcription + username */
|
||||
q?: string;
|
||||
/** created_at lower bound (epoch ms) */
|
||||
startDate?: number;
|
||||
/** created_at upper bound (epoch ms) */
|
||||
endDate?: number;
|
||||
}
|
||||
|
||||
export const recordingsApi = {
|
||||
list: (
|
||||
limit?: number,
|
||||
channelId?: string,
|
||||
userId?: string,
|
||||
cursor?: string,
|
||||
) =>
|
||||
list: (params: RecordingListParams = {}) =>
|
||||
orpc.recordings.list({
|
||||
limit,
|
||||
channelId,
|
||||
userId,
|
||||
cursor,
|
||||
limit: params.limit,
|
||||
channelId: params.channelId,
|
||||
userId: params.userId,
|
||||
cursor: params.cursor,
|
||||
q: params.q,
|
||||
startDate: params.startDate,
|
||||
endDate: params.endDate,
|
||||
}) as unknown as Promise<PaginatedRecordings>,
|
||||
|
||||
delete: (id: string) =>
|
||||
orpc.recordings.delete({ id }) as unknown as Promise<{ ok: boolean }>,
|
||||
|
||||
summary: () =>
|
||||
orpc.recordings.summary({}) as unknown as Promise<SpeakerSummary[]>,
|
||||
};
|
||||
@@ -13,6 +13,7 @@ export interface VoiceRecording {
|
||||
upload_error?: string | null;
|
||||
created_at: number;
|
||||
uploaded_at?: number | null;
|
||||
transcription?: string | null;
|
||||
}
|
||||
|
||||
export interface PaginatedRecordings {
|
||||
@@ -20,3 +21,15 @@ export interface PaginatedRecordings {
|
||||
nextCursor: string | null;
|
||||
hasMore: boolean;
|
||||
}
|
||||
|
||||
/** recordings.summary — per-speaker leaderboard. */
|
||||
export interface SpeakerSummary {
|
||||
user_id: string;
|
||||
username: string;
|
||||
avatar_url: string | null;
|
||||
clips: number;
|
||||
est_duration_s: number;
|
||||
words: number;
|
||||
transcribed: number;
|
||||
last_at: number;
|
||||
}
|
||||
Reference in new issue
Block a user