fix(dashboard): save validation feedback, public site light mode, global verify modal (#716)
* fix(dashboard): surface save errors, force public site light mode, global verify modal - Show snackbar with first validation error when course update fails client-side validation - Force light mode on public org-site pages; lesson player and dashboard keep theme selector - Mount VerifyEmailModal in root layout for all authenticated routes; keep trigger=app email guard Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix: instructor editor sync, auto-enroll after auth, email verification API - Initialize instructor form from org fallbacks and batch metadata updates - Auto-complete course enrollment after login/signup return from enroll page - Block new org auto-enroll and course enroll until email is verified Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix: address PR review on instructor setter, color scheme, auto-enroll - Use existing setter with batched metadata.instructor updates in InstructorForm - Allowlist public org-site paths so /lms and /courses/:id keep theme selector - Auto-enroll logged-in users via Better Auth session; remove sessionStorage flow - Retry enrollment after email verification without duplicate Join Course clicks Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix: allow enrollment without verification; gate dashboard only - Remove API email verification checks from enroll and auto-enroll flows - Move VerifyEmailModal to (app) layout so public org-site pages stay usable - Unverified users can enroll but hit the verification modal in LMS/dashboard Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * fix: address PR feedback on verify modal, routes, instructor form - Restore currentOrg.id guard on VerifyEmailModal open condition - InstructorForm imports currentOrg directly instead of prop drilling - Move public org-site light-mode route patterns to routes constants - Reset enrollment attempt key on enroll failure to allow retry Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
This commit is contained in:
co-authored by
Rotimi Best
Cursor Agent
parent
35f5216839
commit
be0085f090
@@ -440,6 +440,10 @@ export class CourseApi extends BaseApiWithErrors {
|
||||
const result = ZCourseUpdate.safeParse(fields);
|
||||
if (!result.success) {
|
||||
this.errors = mapZodErrorsToTranslations(result.error, 'course');
|
||||
const firstError = this.errors.general ?? Object.values(this.errors).find(Boolean);
|
||||
if (firstError) {
|
||||
snackbar.error(firstError);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+32
-20
@@ -7,19 +7,31 @@
|
||||
import { uploadImage } from '$lib/utils/services/upload';
|
||||
import type { Course } from '$features/course/utils/types';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import { currentOrg } from '$lib/utils/store/org';
|
||||
|
||||
interface Props {
|
||||
course: Course;
|
||||
setter: (value: any, key: string) => void;
|
||||
setter: (value: unknown, key: string) => void;
|
||||
}
|
||||
|
||||
let { course = $bindable(), setter }: Props = $props();
|
||||
|
||||
let name = $state(get(course, 'metadata.instructor.name'));
|
||||
let role = $state(get(course, 'metadata.instructor.role'));
|
||||
let imgUrl = $state(get(course, 'metadata.instructor.imgUrl'));
|
||||
let description = $state(get(course, 'metadata.instructor.description'));
|
||||
let courseNo = $state(get(course, 'metadata.instructor.coursesNo') ?? get(course, 'metadata.instructor.courseNo'));
|
||||
function readInstructorField(path: string, fallback = '') {
|
||||
const value = get(course, path);
|
||||
if (typeof value === 'string' && value.trim()) {
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
let name = $state(readInstructorField('metadata.instructor.name', $currentOrg.name ?? ''));
|
||||
let role = $state(readInstructorField('metadata.instructor.role'));
|
||||
let imgUrl = $state(readInstructorField('metadata.instructor.imgUrl', $currentOrg.avatarUrl ?? ''));
|
||||
let description = $state(readInstructorField('metadata.instructor.description'));
|
||||
let courseNo = $state(
|
||||
get(course, 'metadata.instructor.coursesNo') ?? get(course, 'metadata.instructor.courseNo') ?? ''
|
||||
);
|
||||
let avatar: File | undefined = $state();
|
||||
let isUploading = $state(false);
|
||||
|
||||
@@ -29,26 +41,26 @@
|
||||
isUploading = true;
|
||||
const logo = await uploadImage(_avatar);
|
||||
|
||||
if (!logo) return;
|
||||
if (!logo) {
|
||||
isUploading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
imgUrl = logo;
|
||||
isUploading = false;
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
setter(name, 'metadata.instructor.name');
|
||||
});
|
||||
$effect(() => {
|
||||
setter(role, 'metadata.instructor.role');
|
||||
});
|
||||
$effect(() => {
|
||||
setter(imgUrl, 'metadata.instructor.imgUrl');
|
||||
});
|
||||
$effect(() => {
|
||||
setter(description, 'metadata.instructor.description');
|
||||
});
|
||||
$effect(() => {
|
||||
setter(courseNo, 'metadata.instructor.coursesNo');
|
||||
setter(
|
||||
{
|
||||
name,
|
||||
role,
|
||||
imgUrl,
|
||||
description,
|
||||
coursesNo: courseNo
|
||||
},
|
||||
'metadata.instructor'
|
||||
);
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
|
||||
Reference in New Issue
Block a user