Enhance training route and admin functionality. Added new admin route for training, updated type definitions to include admin checks, and modified training page to conditionally display admin link based on user permissions. Improved data handling for training videos and user roles.
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
|
||||
type TrainingVideo = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
created: string;
|
||||
videoUrl: string;
|
||||
};
|
||||
|
||||
type AdminFormState = {
|
||||
error?: string;
|
||||
} | null;
|
||||
|
||||
let { data, form }: { data: { videos: TrainingVideo[] }; form: AdminFormState } = $props();
|
||||
|
||||
const videos = $derived(data.videos);
|
||||
|
||||
function formatDate(created: string) {
|
||||
if (!created) return '';
|
||||
const d = new Date(created);
|
||||
return d.toLocaleDateString(undefined, {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: d.getFullYear() !== new Date().getFullYear() ? 'numeric' : undefined
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Training admin — Base</title>
|
||||
</svelte:head>
|
||||
|
||||
<main class="flex min-h-0 flex-1 flex-col items-center p-4 sm:p-6">
|
||||
<div class="flex w-full max-w-5xl flex-col gap-4 sm:gap-6">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<a href="/training">
|
||||
<Button variant="ghost" size="sm">← Training</Button>
|
||||
</a>
|
||||
<h1 class="text-xl font-semibold">Training admin</h1>
|
||||
</div>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header class="pb-2">
|
||||
<Card.Title>Videos</Card.Title>
|
||||
<Card.Description>Manage training videos in the Media collection.</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content class="space-y-3">
|
||||
{#if form?.error}
|
||||
<p class="text-destructive text-sm">{form.error}</p>
|
||||
{/if}
|
||||
|
||||
{#if videos.length === 0}
|
||||
<p class="text-muted-foreground text-sm">No videos found.</p>
|
||||
{:else}
|
||||
<div class="space-y-3">
|
||||
{#each videos as video (video.id)}
|
||||
<div class="flex items-start justify-between gap-3 rounded-md border bg-card/40 p-3">
|
||||
<div class="min-w-0 space-y-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<p class="font-medium truncate">{video.title}</p>
|
||||
<span class="text-muted-foreground text-xs">{formatDate(video.created)}</span>
|
||||
</div>
|
||||
{#if video.description}
|
||||
<p class="text-muted-foreground text-xs line-clamp-2">
|
||||
{video.description}
|
||||
</p>
|
||||
{/if}
|
||||
{#if video.videoUrl}
|
||||
<p class="text-muted-foreground text-xs truncate">
|
||||
{video.videoUrl}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
<form method="POST" class="flex flex-col items-end gap-2">
|
||||
<input type="hidden" name="id" value={video.id} />
|
||||
<Button
|
||||
type="submit"
|
||||
name="intent"
|
||||
value="delete"
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
<a href={`/training/${video.id}`}>
|
||||
<Button variant="outline" size="sm">Open</Button>
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user