Files
classroomio/apps/dashboard/src/lib/features/course/api/course-clone.svelte.ts
T
Rotimi Best 4b52906445 feat: enhance API route naming conventions and structure
- Added comprehensive guidelines for API route naming and structure in `ROUTE_NAMING_BEST_PRACTICES.md`.
- Updated `ARCHITECHTURE.md` to reference the new best practices document.
- Refactored course-related routes to follow new naming conventions, including changes to endpoint paths and validation schemas.
- Removed the deprecated course clone route and integrated cloning functionality directly into the course routes.
- Adjusted various API methods to ensure consistent usage of HTTP methods and parameter structures across the application.
2025-12-05 17:24:16 +01:00

81 lines
2.7 KiB
TypeScript

import { BaseApiWithErrors, classroomio } from '$lib/utils/services/api';
import { copyCourseModal, copyCourseModalInitialState } from '../utils/store';
import type { TCourseClone } from '@cio/utils/validation/course';
import { ZCourseClone } from '@cio/utils/validation/course';
import { currentOrg } from '$lib/utils/store/org';
import generateSlug from '$lib/utils/functions/generateSlug';
import { get } from 'svelte/store';
import { goto } from '$app/navigation';
import { mapZodErrorsToTranslations } from '$lib/utils/validation';
import { resolve } from '$app/paths';
import { snackbar } from '$lib/components/Snackbar/store';
import { t } from '$lib/utils/functions/translations';
/**
* API class for course operations
*/
export class CourseCloneApi extends BaseApiWithErrors {
/**
* Clones a course
* @param courseId The ID of the course to clone
* @param title Course title
* @param description Course description (optional)
* @returns The cloned course data or null on error
*/
async clone(courseId: string, title: string, description?: string) {
// Generate unique slug from the title
const slug = generateSlug(title);
// Prepare data for validation
const data: TCourseClone = {
title,
description,
slug,
organizationId: get(currentOrg).id
};
// Client-side validation
const result = ZCourseClone.safeParse(data);
if (!result.success) {
this.errors = mapZodErrorsToTranslations(result.error, 'course');
return null;
}
await this.execute<(typeof classroomio.course)[':courseId']['clone']['$post']>({
requestFn: () =>
classroomio.course[':courseId'].clone.$post({
param: { courseId },
// @ts-expect-error - the json type is not inferred correctly
json: result.data
}),
logContext: 'cloning course',
onSuccess: (response) => {
this.errors = {};
copyCourseModal.set(copyCourseModalInitialState);
copyCourseModal.update((modal) => ({ ...modal, open: false }));
// Navigate to the new course
goto(resolve(`/courses/${response.course.id}`, {}));
snackbar.success(t.get('courses.copy_course.success.course_cloned'));
},
onError: (result) => {
if (typeof result === 'string') {
snackbar.error(result);
return;
}
if ('error' in result && 'field' in result) {
// Field-specific error automatically mapped to this.errors[field]
this.errors[result.field as string] = result.error;
} else if ('error' in result) {
snackbar.error(result.error);
}
}
});
}
}
export const courseCloneApi = new CourseCloneApi();