Refactor training page and admin access control. Updated user role checks to include 'training_admin' for permissions, removed unnecessary admin checks from the training page, and added video deletion functionality with appropriate error handling. Improved UI for video management actions.
This commit is contained in:
@@ -24,6 +24,6 @@ export function mapUserRecord(r: Record<string, unknown>): UserFromRecord {
|
||||
tasgrid: bool(r, 'TasGrid', 'tasgrid'),
|
||||
stackq: bool(r, 'Stackq', 'stackq'),
|
||||
hrm: bool(r, 'HRM', 'hrm', 'Hrm'),
|
||||
training_admin: bool(r, 'training_admin', 'TrainingAdmin', 'trainingAdmin')
|
||||
training_admin: bool(r, 'training_admin', 'TrainingAdmin', 'trainingAdmin', 'Admin')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,12 +46,10 @@ export const load: PageServerLoad = async ({ locals }) => {
|
||||
});
|
||||
|
||||
const canUpload = Boolean(user?.training_admin);
|
||||
const isAdmin = Boolean((user as any)?.Admin);
|
||||
|
||||
return {
|
||||
videos,
|
||||
canUpload,
|
||||
isAdmin
|
||||
canUpload
|
||||
};
|
||||
};
|
||||
|
||||
@@ -109,6 +107,42 @@ export const actions: Actions = {
|
||||
});
|
||||
}
|
||||
|
||||
throw redirect(303, '/training');
|
||||
},
|
||||
delete: async ({ request, locals }) => {
|
||||
const user = locals.user;
|
||||
|
||||
if (!user) {
|
||||
throw redirect(303, '/login');
|
||||
}
|
||||
|
||||
if (!user.training_admin) {
|
||||
return fail(403, {
|
||||
error: 'You do not have permission to delete training videos.'
|
||||
});
|
||||
}
|
||||
|
||||
const formData = await request.formData();
|
||||
const id = String(formData.get('id') ?? '').trim();
|
||||
|
||||
if (!id) {
|
||||
return fail(400, {
|
||||
error: 'Missing video id.'
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
await locals.pb.collection(VIDEO_COLLECTION).delete(id);
|
||||
} catch (e) {
|
||||
const message =
|
||||
e && typeof e === 'object' && 'message' in e
|
||||
? String((e as { message: string }).message)
|
||||
: 'Could not delete video.';
|
||||
return fail(500, {
|
||||
error: message
|
||||
});
|
||||
}
|
||||
|
||||
throw redirect(303, '/training');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -20,12 +20,11 @@
|
||||
};
|
||||
} | null;
|
||||
|
||||
let { data, form }: { data: { videos: TrainingVideo[]; canUpload: boolean; isAdmin: boolean }; form: UploadFormState } =
|
||||
let { data, form }: { data: { videos: TrainingVideo[]; canUpload: boolean }; form: UploadFormState } =
|
||||
$props();
|
||||
|
||||
const videos = $derived(data.videos);
|
||||
const canUpload = $derived(data.canUpload);
|
||||
const isAdmin = $derived(data.isAdmin);
|
||||
|
||||
function formatDate(created: string) {
|
||||
if (!created) return '';
|
||||
@@ -88,11 +87,6 @@
|
||||
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<h1 class="text-2xl font-semibold">Training</h1>
|
||||
{#if isAdmin}
|
||||
<a href="/training/admin">
|
||||
<Button variant="outline" size="sm">Admin</Button>
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if canUpload}
|
||||
@@ -162,9 +156,26 @@
|
||||
<Card.Title class="truncate">{video.title}</Card.Title>
|
||||
<Card.Description class="text-xs">{formatDate(video.created)}</Card.Description>
|
||||
</div>
|
||||
<a href={`/training/${video.id}`} aria-label="Open training video">
|
||||
<Button variant="outline" size="sm">Open</Button>
|
||||
</a>
|
||||
<div class="flex items-center gap-2">
|
||||
<a href={`/training/${video.id}`} aria-label="Open training video">
|
||||
<Button variant="outline" size="sm">Open</Button>
|
||||
</a>
|
||||
{#if canUpload}
|
||||
<form method="POST">
|
||||
<input type="hidden" name="id" value={video.id} />
|
||||
<Button
|
||||
type="submit"
|
||||
name="intent"
|
||||
value="delete"
|
||||
variant="destructive"
|
||||
size="icon"
|
||||
aria-label="Delete training video"
|
||||
>
|
||||
✕
|
||||
</Button>
|
||||
</form>
|
||||
{/if}
|
||||
</div>
|
||||
</Card.Header>
|
||||
<Card.Content class="flex flex-1 flex-col gap-3">
|
||||
{#if video.videoUrl}
|
||||
|
||||
@@ -21,7 +21,15 @@ export const load: PageServerLoad = async ({ locals }) => {
|
||||
throw redirect(303, '/login');
|
||||
}
|
||||
|
||||
if (!(user as any)?.Admin) {
|
||||
console.log('training/admin load user', user);
|
||||
|
||||
const isAdmin = Boolean(
|
||||
(user as any)?.Admin ||
|
||||
(user as any)?.admin ||
|
||||
(user as any)?.training_admin
|
||||
);
|
||||
|
||||
if (!isAdmin) {
|
||||
throw error(403, 'You do not have access to training admin.');
|
||||
}
|
||||
|
||||
@@ -65,7 +73,13 @@ export const actions: Actions = {
|
||||
throw redirect(303, '/login');
|
||||
}
|
||||
|
||||
if (!(user as any)?.Admin) {
|
||||
const isAdmin = Boolean(
|
||||
(user as any)?.Admin ||
|
||||
(user as any)?.admin ||
|
||||
(user as any)?.training_admin
|
||||
);
|
||||
|
||||
if (!isAdmin) {
|
||||
return fail(403, {
|
||||
error: 'You do not have permission to manage training videos.'
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user