feat(idaa): implement jitsi report streaming and conference lifecycle improvements
- Refactor Jitsi reports to use SvelteKit streaming with a skeleton loader. - Add conference lifecycle event listeners (left, close) to video conference page. - Implement manual Novi data re-sync and improve initialization robustness. - Fix skeleton visibility by using standard Tailwind colors.
This commit is contained in:
@@ -20,86 +20,101 @@
|
||||
<div class="p-4 space-y-4">
|
||||
<h1 class="h1">Jitsi Meeting Reports</h1>
|
||||
|
||||
{#if data.meetings && data.meetings.length > 0}
|
||||
<div class="space-y-2">
|
||||
{#each data.meetings as meeting (meeting.meeting_id)}
|
||||
<div class="card card-hover">
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<header class="card-header p-2 cursor-pointer" onclick={() => toggle_accordion(meeting.meeting_id)}>
|
||||
<div class="flex justify-between items-center w-full">
|
||||
<div class="flex-1">
|
||||
<!-- NOTE: Normally I would the "h3" class, but Novi classes make things look odd. -->
|
||||
<h3 class="text-base">{meeting.room_name}</h3>
|
||||
<p class="text-sm text-gray-500">{new Date(meeting.start_time).toLocaleString()}</p>
|
||||
{#await data.streamed.meetings}
|
||||
<div class="space-y-4 animate-pulse">
|
||||
<div class="h-12 w-full bg-gray-200 dark:bg-gray-700 rounded"></div>
|
||||
<div class="h-12 w-full bg-gray-200 dark:bg-gray-700 rounded"></div>
|
||||
<div class="h-12 w-full bg-gray-200 dark:bg-gray-700 rounded"></div>
|
||||
<div class="h-12 w-full bg-gray-200 dark:bg-gray-700 rounded"></div>
|
||||
</div>
|
||||
{:then meetings}
|
||||
{#if meetings && meetings.length > 0}
|
||||
<div class="space-y-2">
|
||||
{#each meetings as meeting (meeting.meeting_id)}
|
||||
<div class="card card-hover">
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<header class="card-header p-2 cursor-pointer" onclick={() => toggle_accordion(meeting.meeting_id)}>
|
||||
<div class="flex justify-between items-center w-full">
|
||||
<div class="flex-1">
|
||||
<!-- NOTE: Normally I would the "h3" class, but Novi classes make things look odd. -->
|
||||
<h3 class="text-base">{meeting.room_name}</h3>
|
||||
<p class="text-sm text-gray-500">{new Date(meeting.start_time).toLocaleString()}</p>
|
||||
</div>
|
||||
<div class="flex-none flex items-center space-x-4 text-sm mr-4">
|
||||
<span>Duration: {meeting.final_duration}</span>
|
||||
<span>Participants: {meeting.final_participant_count}</span>
|
||||
</div>
|
||||
<div class="flex-none">
|
||||
<span class="transition-transform duration-200" class:rotate-180={open_accordions[meeting.meeting_id]}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-none flex items-center space-x-4 text-sm mr-4">
|
||||
<span>Duration: {meeting.final_duration}</span>
|
||||
<span>Participants: {meeting.final_participant_count}</span>
|
||||
</div>
|
||||
<div class="flex-none">
|
||||
<span class="transition-transform duration-200" class:rotate-180={open_accordions[meeting.meeting_id]}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{#if open_accordions[meeting.meeting_id]}
|
||||
<div class="p-4 border-t border-gray-200 dark:border-gray-700 grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<!-- NOTE: Normally I would the "h4" class, but Novi classes make things look odd. -->
|
||||
<h4 class="text-base">Event Timeline</h4>
|
||||
{#if meeting.events && meeting.events.length > 0}
|
||||
<ul class="list-disc list-inside space-y-2 mt-2">
|
||||
{#each meeting.events as event}
|
||||
<li>
|
||||
<span class="font-mono text-xs">[{new Date(event.timestamp).toLocaleTimeString()}]</span>
|
||||
<span class="font-semibold">{ae_util.to_title_case(event.action.replace('jitsi_meeting_', ''))}</span>
|
||||
{#if event.details.full_name}
|
||||
- by {event.details.full_name}
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
<p class="text-gray-500 italic mt-2">No discrete events recorded.</p>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
<!-- NOTE: Normally I would the "h4" class, but Novi classes make things look odd. -->
|
||||
<h4 class="text-base">Final Participants</h4>
|
||||
{#if meeting.final_participants && meeting.final_participants.length > 0}
|
||||
<div class="table-container mt-2">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Role</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each meeting.final_participants as participant}
|
||||
</header>
|
||||
{#if open_accordions[meeting.meeting_id]}
|
||||
<div class="p-4 border-t border-gray-200 dark:border-gray-700 grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<!-- NOTE: Normally I would the "h4" class, but Novi classes make things look odd. -->
|
||||
<h4 class="text-base">Event Timeline</h4>
|
||||
{#if meeting.events && meeting.events.length > 0}
|
||||
<ul class="list-disc list-inside space-y-2 mt-2">
|
||||
{#each meeting.events as event}
|
||||
<li>
|
||||
<span class="font-mono text-xs">[{new Date(event.timestamp).toLocaleTimeString()}]</span>
|
||||
<span class="font-semibold">{ae_util.to_title_case(event.action.replace('jitsi_meeting_', ''))}</span>
|
||||
{#if event.details.full_name}
|
||||
- by {event.details.full_name}
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{:else}
|
||||
<p class="text-gray-500 italic mt-2">No discrete events recorded.</p>
|
||||
{/if}
|
||||
</div>
|
||||
<div>
|
||||
<!-- NOTE: Normally I would the "h4" class, but Novi classes make things look odd. -->
|
||||
<h4 class="text-base">Final Participants</h4>
|
||||
{#if meeting.final_participants && meeting.final_participants.length > 0}
|
||||
<div class="table-container mt-2">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{participant.displayName}</td>
|
||||
<td>{ae_util.to_title_case(participant.role)}</td>
|
||||
<th>Name</th>
|
||||
<th>Role</th>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-gray-500 italic mt-2">No participant data available.</p>
|
||||
{/if}
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each meeting.final_participants as participant}
|
||||
<tr>
|
||||
<td>{participant.displayName}</td>
|
||||
<td>{ae_util.to_title_case(participant.role)}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{:else}
|
||||
<p class="text-gray-500 italic mt-2">No participant data available.</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="card p-4 text-center">
|
||||
<h3 class="h3">No Meeting Reports Found</h3>
|
||||
<p>There are no Jitsi activity logs to display.</p>
|
||||
</div>
|
||||
{/if}
|
||||
{:catch error}
|
||||
<div class="card p-4 bg-red-100 text-red-900 border-l-4 border-red-500">
|
||||
<h3 class="h3 font-bold">Error Loading Reports</h3>
|
||||
<p>An error occurred while fetching the meeting reports:</p>
|
||||
<pre class="mt-2 text-xs overflow-auto">{error.message}</pre>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="card p-4 text-center">
|
||||
<h3 class="h3">No Meeting Reports Found</h3>
|
||||
<p>There are no Jitsi activity logs to display.</p>
|
||||
</div>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
|
||||
@@ -12,17 +12,21 @@ export const load: PageLoad = async ({ fetch }) => {
|
||||
if (!api_cfg || !account_id) {
|
||||
console.error('API config or Account ID not available for loading Jitsi reports.');
|
||||
return {
|
||||
meetings: []
|
||||
streamed: {
|
||||
meetings: Promise.resolve([])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const meetings = await load_jitsi_report({
|
||||
const meetings_promise = load_jitsi_report({
|
||||
api_cfg,
|
||||
account_id,
|
||||
log_lvl: 1
|
||||
});
|
||||
|
||||
return {
|
||||
meetings
|
||||
streamed: {
|
||||
meetings: meetings_promise
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user