feat: Implement organization branding, course pacing, and display upcoming deadlines in the experience portal.

This commit is contained in:
2025-12-29 01:30:48 -03:00
parent 1a2b9e473c
commit 158aa5b315
41 changed files with 2422 additions and 262 deletions
+47
View File
@@ -5,6 +5,9 @@ export interface Course {
title: string;
description?: string;
instructor_id: string;
pacing_mode: 'self_paced' | 'instructor_led';
start_date?: string;
end_date?: string;
passing_percentage: number;
certificate_template?: string;
created_at: string;
@@ -57,26 +60,40 @@ export interface Lesson {
grading_category_id: string | null;
max_attempts: number | null;
allow_retry: boolean;
due_date?: string;
important_date_type?: 'exam' | 'assignment' | 'milestone' | 'live-session';
summary?: string;
transcription?: {
en?: string;
es?: string;
cues?: { start: number; end: number; text: string }[];
} | null;
created_at: string;
}
export interface Organization {
id: string;
name: string;
domain?: string;
logo_url?: string;
primary_color?: string;
secondary_color?: string;
certificate_template?: string;
created_at: string;
updated_at: string;
}
export interface BrandingPayload {
primary_color?: string;
secondary_color?: string;
}
export interface User {
id: string;
email: string;
full_name: string;
role: string;
organization_id?: string;
}
export interface AuthResponse {
@@ -152,6 +169,8 @@ const apiFetch = (url: string, options: RequestInit = {}) => {
export const cmsApi = {
// Organization
getOrganization: (): Promise<Organization> => apiFetch('/organization'),
getOrganizations: (): Promise<Organization[]> => apiFetch('/organizations'),
createOrganization: (name: string, domain?: string): Promise<Organization> => apiFetch('/organizations', { method: 'POST', body: JSON.stringify({ name, domain }) }),
// Auth
register: (payload: AuthPayload): Promise<AuthResponse> => apiFetch('/auth/register', { method: 'POST', body: JSON.stringify(payload) }),
@@ -171,8 +190,13 @@ export const cmsApi = {
createLesson: (module_id: string, title: string, content_type: string, position: number): Promise<Lesson> => apiFetch('/lessons', { method: 'POST', body: JSON.stringify({ module_id, title, content_type, position }) }),
getLesson: (id: string): Promise<Lesson> => apiFetch(`/lessons/${id}`),
updateLesson: (id: string, payload: Partial<Lesson>): Promise<Lesson> => apiFetch(`/lessons/${id}`, { method: 'PUT', body: JSON.stringify(payload) }),
transcribeLesson: (id: string): Promise<Lesson> => apiFetch(`/lessons/${id}/transcribe`, { method: 'POST' }),
summarizeLesson: (id: string): Promise<Lesson> => apiFetch(`/lessons/${id}/summarize`, { method: 'POST' }),
generateQuiz: (id: string): Promise<Block[]> => apiFetch(`/lessons/${id}/generate-quiz`, { method: 'POST' }),
deleteModule: (id: string): Promise<void> => apiFetch(`/modules/${id}`, { method: 'DELETE' }),
deleteLesson: (id: string): Promise<void> => apiFetch(`/lessons/${id}`, { method: 'DELETE' }),
reorderModules: (payload: { items: { id: string, position: number }[] }): Promise<void> => apiFetch('/modules/reorder', { method: 'POST', body: JSON.stringify(payload) }),
reorderLessons: (payload: { items: { id: string, position: number }[] }): Promise<void> => apiFetch('/lessons/reorder', { method: 'POST', body: JSON.stringify(payload) }),
// Grading
getGradingCategories: (courseId: string): Promise<GradingCategory[]> => apiFetch(`/courses/${courseId}/grading`),
@@ -183,6 +207,10 @@ export const cmsApi = {
getAuditLogs: (): Promise<AuditLog[]> => apiFetch('/audit-logs'),
getCourseAnalytics: (id: string): Promise<CourseAnalytics> => apiFetch(`/courses/${id}/analytics`),
// Users
getAllUsers: (): Promise<User[]> => apiFetch('/users'),
updateUser: (id: string, role: string, organization_id: string): Promise<void> => apiFetch(`/users/${id}`, { method: 'PUT', body: JSON.stringify({ role, organization_id }) }),
// Assets
uploadAsset: (file: File): Promise<UploadResponse> => {
const formData = new FormData();
@@ -204,4 +232,23 @@ export const cmsApi = {
return res.json();
});
},
// Organizations Branding
getOrganizationBranding: (id: string): Promise<Organization> => apiFetch(`/organizations/${id}/branding`),
updateOrganizationBranding: (id: string, payload: BrandingPayload): Promise<void> => apiFetch(`/organizations/${id}/branding`, { method: 'PUT', body: JSON.stringify(payload) }),
uploadOrganizationLogo: (id: string, file: File): Promise<UploadResponse> => {
const formData = new FormData();
formData.append('file', file);
const token = getToken();
const headers: Record<string, string> = {
...(token ? { 'Authorization': `Bearer ${token}` } : {})
};
return fetch(`${API_BASE_URL}/organizations/${id}/logo`, {
method: 'POST',
headers,
body: formData,
}).then(res => {
if (!res.ok) return res.json().then(err => Promise.reject(new Error(err.message || 'Logo upload failed')));
return res.json();
});
},
};