feat: improve LMS course discovery and enrollment UX
- Add paginated recommended courses to LMS dashboard (top 3, list
view)
and explore page (12 per page); exclude courses blocking new
students
- Polish course preview modal: remove padding, render requirements as
HTML, round banner image, secondary close button
- Redirect to course start page after successful enrollment instead of
/lms;
keep spinner active until navigation completes
- Auto-enable "allow new students" when a course is published
- Move Explore after My Learning in LMS sidebar nav
- Move Certificates after Content in course sidebar
- Add hiddenColumns prop to CourseListRow; show "Learn more" button
instead of actions menu in LMS explore context
This commit is contained in:
@@ -4,7 +4,8 @@ import { ALLOWED_CONTENT_TYPES, ALLOWED_DOCUMENT_TYPES } from '../constants';
|
||||
import { ZCourseCalloutInput } from './callout';
|
||||
|
||||
export const ZGetRecommendedCourses = z.object({
|
||||
limit: z.string().transform(Number).pipe(z.number().min(1).max(50)).optional()
|
||||
limit: z.string().transform(Number).pipe(z.number().min(1).max(50)).optional(),
|
||||
page: z.string().transform(Number).pipe(z.number().min(1)).optional()
|
||||
});
|
||||
export type TGetRecommendedCourses = z.infer<typeof ZGetRecommendedCourses>;
|
||||
|
||||
@@ -57,7 +58,9 @@ export type TCourseDownloadParam = z.infer<typeof ZCourseDownloadParam>;
|
||||
*/
|
||||
export const ZCertificateSignatory = z.object({
|
||||
name: z.string().max(80).default(''),
|
||||
role: z.string().max(80).default('')
|
||||
role: z.string().max(80).default(''),
|
||||
enabled: z.boolean().default(true),
|
||||
signatureUrl: z.url().optional()
|
||||
});
|
||||
export type TCertificateSignatory = z.infer<typeof ZCertificateSignatory>;
|
||||
|
||||
@@ -82,7 +85,14 @@ export type TCertificateDesign = z.infer<typeof ZCertificateDesign>;
|
||||
export const ZCertificateDownloadRequest = z.object({
|
||||
studentName: z.string().min(1).max(120),
|
||||
studentId: z.string().min(1).max(64).optional(),
|
||||
issuedAt: z.string().datetime().optional(),
|
||||
issuedAt: z.preprocess((value) => {
|
||||
if (typeof value !== 'string' || value.trim() === '') return undefined;
|
||||
|
||||
const parsed = new Date(value);
|
||||
if (Number.isNaN(parsed.getTime())) return undefined;
|
||||
|
||||
return parsed.toISOString();
|
||||
}, z.string().datetime().optional()),
|
||||
/** When true, skips the eligibility check (course owner previewing their own design). */
|
||||
previewMode: z.boolean().optional()
|
||||
});
|
||||
@@ -355,7 +365,8 @@ const ZCourseMetadataFields = z.object({
|
||||
lessonDownload: z.boolean().optional(),
|
||||
allowNewStudent: z.boolean(),
|
||||
sectionDisplay: z.record(z.string(), z.boolean()).optional(),
|
||||
isContentGroupingEnabled: z.boolean().optional()
|
||||
isContentGroupingEnabled: z.boolean().optional(),
|
||||
progressionMode: z.enum(['free', 'sequential']).optional()
|
||||
});
|
||||
|
||||
// Course metadata schema matching the database structure
|
||||
|
||||
Reference in New Issue
Block a user