feat: certification rules, content sanitization, handle draft course updates and reset content, better redis logging

This commit is contained in:
Rotimi Best
2026-03-30 08:24:55 +01:00
parent ff2ad59377
commit 5a8f50f67b
131 changed files with 5643 additions and 2134 deletions
@@ -10,3 +10,10 @@ export function toggleConfetti() {
show: !confetti.show
}));
}
export function setConfetti(show: boolean) {
confettiStore.update((confetti) => ({
...confetti,
show
}));
}
@@ -18,6 +18,7 @@
HoverableItem,
PreviewIcon
} from '@cio/ui/custom/moving-icons';
import type { TCourseUpdate } from '@cio/utils/validation/course';
import { IconButton } from '@cio/ui/custom/icon-button';
import { CloseButton } from '$features/ui';
@@ -151,15 +152,22 @@
course.slug = course.slug || generateSlug(course.title, { appendTimestamp: true });
console.log('course', course);
await courseApi.update(courseId, {
const updatePayload = {
...course,
type: course.type!,
slug: course.slug!,
isPublished: course.isPublished ?? undefined,
overview: course.overview ?? undefined,
isCertificateDownloadable: course.isCertificateDownloadable ?? undefined,
certificateTheme: course.certificateTheme ?? undefined
});
certificate: course.certificate
? {
...course.certificate,
isDownloadable: course.certificate.isDownloadable ?? undefined,
theme: course.certificate.theme ?? undefined
}
: undefined
} as TCourseUpdate;
await courseApi.update(courseId, updatePayload);
loading = false;
syncCourseStore(course);
@@ -173,7 +181,7 @@
});
}
function setter(value: any, setterKey: string) {
function setter(value: unknown, setterKey: string) {
if (typeof value === 'undefined') return;
// Untrack the course change to avoid unnecessary re-renders
@@ -231,7 +239,7 @@
</Sidebar.GroupLabel>
<Sidebar.GroupContent>
<Sidebar.Menu>
{#each sections as section}
{#each sections as section (section.key)}
{@const SectionIcon = section.icon}
<HoverableItem>
{#snippet children(isHovered)}
@@ -253,7 +261,7 @@
</Sidebar.Group>
{:else}
<Sidebar.Menu>
{#each sections as section}
{#each sections as section (section.key)}
{@const SectionIcon = section.icon}
<HoverableItem>
{#snippet children(isHovered)}
@@ -32,7 +32,8 @@ export const QUESTION_TYPES = QUESTION_TYPE_REGISTRY.filter((qt) => !HIDDEN_QUES
id: questionType.id,
key: questionType.key,
label: questionType.label,
labelKey: QUESTION_TYPE_LABEL_KEYS[questionType.key]
labelKey: QUESTION_TYPE_LABEL_KEYS[questionType.key],
autoGradable: questionType.autoGradable
})
);