Improve live recordings UI, upload flow, and default Flux theme.
Refresh list status over Reverb with polling fallback, clarify queued vs processing, streamline upload empty states, and seed an admin login.
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
const BADGE_COLORS = {
|
||||
done: 'teal',
|
||||
processing: 'amber',
|
||||
pending: 'amber',
|
||||
pending: 'zinc',
|
||||
failed: 'red',
|
||||
cancelled: 'zinc',
|
||||
};
|
||||
@@ -55,12 +55,6 @@ export function formatTimestamp(value) {
|
||||
+ ':' + pad(date.getMinutes());
|
||||
}
|
||||
|
||||
function formatWordCount(count) {
|
||||
const n = Number(count) || 0;
|
||||
|
||||
return n > 0 ? n.toLocaleString() : '—';
|
||||
}
|
||||
|
||||
function subscribeToRecording(recordingId, handler) {
|
||||
if (!window.Echo) {
|
||||
return () => {};
|
||||
@@ -76,14 +70,6 @@ function subscribeToRecording(recordingId, handler) {
|
||||
};
|
||||
}
|
||||
|
||||
function subscribeToRecordings(recordingIds, handler) {
|
||||
const leaveFns = recordingIds.map((id) => subscribeToRecording(id, handler));
|
||||
|
||||
return () => {
|
||||
leaveFns.forEach((leave) => leave());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Show-page Alpine component: Echo push + local elapsed tick + optional status hydrate.
|
||||
*/
|
||||
@@ -208,34 +194,19 @@ export function transcriptionMonitor({ statusUrl, initial }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Index-page Alpine component: patch row status from Reverb events.
|
||||
* Index-page Alpine component: inline audio player only.
|
||||
* Status/progress refresh via Livewire Echo + wire:poll.
|
||||
*/
|
||||
export function recordingsIndex({ recordings, pendingCount }) {
|
||||
const byId = {};
|
||||
|
||||
for (const row of recordings) {
|
||||
byId[row.id] = row;
|
||||
}
|
||||
|
||||
export function recordingsIndex() {
|
||||
return {
|
||||
rows: byId,
|
||||
pendingCount: Number(pendingCount) || 0,
|
||||
leaveChannel: null,
|
||||
playingId: null,
|
||||
isPlaying: false,
|
||||
|
||||
start() {
|
||||
this.leaveChannel = subscribeToRecordings(Object.keys(this.rows), (event) => {
|
||||
this.applyPayload(event);
|
||||
});
|
||||
//
|
||||
},
|
||||
|
||||
destroy() {
|
||||
if (this.leaveChannel) {
|
||||
this.leaveChannel();
|
||||
this.leaveChannel = null;
|
||||
}
|
||||
|
||||
const player = this.$refs.player;
|
||||
|
||||
if (player) {
|
||||
@@ -282,41 +253,5 @@ export function recordingsIndex({ recordings, pendingCount }) {
|
||||
this.isPlaying = false;
|
||||
});
|
||||
},
|
||||
|
||||
applyPayload(payload) {
|
||||
const id = payload.id;
|
||||
|
||||
if (!this.rows[id]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const previousStatus = this.rows[id].status;
|
||||
const next = {
|
||||
...this.rows[id],
|
||||
status: payload.status,
|
||||
status_label: payload.status_label,
|
||||
progress: payload.progress,
|
||||
percent: payload.percent,
|
||||
is_active: payload.is_active,
|
||||
word_count: payload.word_count ?? this.rows[id].word_count,
|
||||
word_count_display: formatWordCount(payload.word_count ?? this.rows[id].word_count),
|
||||
badge_color: badgeColorFor(payload.status),
|
||||
};
|
||||
|
||||
this.rows[id] = next;
|
||||
|
||||
const wasQueueable = ['pending', 'failed', 'cancelled'].includes(previousStatus);
|
||||
const isQueueable = ['pending', 'failed', 'cancelled'].includes(payload.status);
|
||||
|
||||
if (wasQueueable && !isQueueable) {
|
||||
this.pendingCount = Math.max(0, this.pendingCount - 1);
|
||||
} else if (!wasQueueable && isQueueable) {
|
||||
this.pendingCount += 1;
|
||||
}
|
||||
},
|
||||
|
||||
row(id) {
|
||||
return this.rows[id] || {};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user