File

apps/recallassess/recallassess-api/src/api/client/my-course/my-course.controller.ts

Prefix

api/client/my-course

Index

Methods

Methods

Async downloadPostBatHtml
downloadPostBatHtml(learningGroupParticipantId: number, auth: CLAuthData, reply: FastifyReply)
Decorators :
@HttpCode(HttpStatus.OK)
@Get(':id/post-bat-download')
@ApiOperation({summary: 'Download post-BAT analysis HTML for a course with learning recommendations'})
@ApiResponse({status: 200, description: 'HTML content returned successfully', content: undefined})
@ApiResponse({status: 404, description: 'Course not found or post-BAT analysis not available'})

Download post-BAT HTML for a course GET /api/client/my-course/:id/post-bat-download

Parameters :
Name Type Optional
learningGroupParticipantId number No
auth CLAuthData No
reply FastifyReply No
Returns : Promise<void>
Async downloadPreBatHtml
downloadPreBatHtml(learningGroupParticipantId: number, auth: CLAuthData, reply: FastifyReply)
Decorators :
@HttpCode(HttpStatus.OK)
@Get(':id/pre-bat-download')
@ApiOperation({summary: 'Download pre-BAT analysis HTML for a course'})
@ApiResponse({status: 200, description: 'HTML content returned successfully', content: undefined})
@ApiResponse({status: 404, description: 'Course not found or pre-BAT analysis not available'})

Download pre-BAT HTML for a course GET /api/client/my-course/:id/pre-bat-download

Parameters :
Name Type Optional
learningGroupParticipantId number No
auth CLAuthData No
reply FastifyReply No
Returns : Promise<void>
Async getMyCourseByCode
getMyCourseByCode(courseCode: string, auth: CLAuthData)
Decorators :
@HttpCode(HttpStatus.OK)
@Get('by-code/:courseCode')
@ApiOperation({summary: 'Get my enrolled course by course code with sequential validation', description: 'Returns course data. If not accessible, includes redirect information to redirect to the first incomplete course.'})
@ApiResponse({status: 200, description: 'Returns enrolled course with redirect info if not accessible', schema: undefined})
@ApiResponse({status: 404, description: 'Course not found or not enrolled'})

Get a single enrolled course by course_code with sequential validation Returns redirect info if not accessible (soft redirect approach) GET /api/client/my-course/by-code/:courseCode Example: /api/client/my-course/by-code/communication-skills-101

Parameters :
Name Type Optional
courseCode string No
auth CLAuthData No
Returns : Promise<literal type>
Async getMyCourseById
getMyCourseById(learningGroupParticipantId: number, auth: CLAuthData)
Decorators :
@HttpCode(HttpStatus.OK)
@Get(':id')
@ApiOperation({summary: 'Get my enrolled course by LearningGroupParticipant ID'})
@ApiResponse({status: 200, description: 'Returns enrolled course with progress data', type: CLMyCourseDto})
@ApiResponse({status: 404, description: 'Course not found or not enrolled'})

Get a single enrolled course by LearningGroupParticipant ID GET /api/client/my-course/:id

Parameters :
Name Type Optional
learningGroupParticipantId number No
auth CLAuthData No
Async getMyCourses
getMyCourses(auth: CLAuthData)
Decorators :
@HttpCode(HttpStatus.OK)
@Get()
@ApiOperation({summary: 'Get my enrolled courses with progress'})
@ApiResponse({status: 200, description: 'Returns courses and company_active flag (for expired-license banner)', type: CLMyCourseListResponseDto})

Get all courses the current participant is enrolled in GET /api/client/my-course

Parameters :
Name Type Optional
auth CLAuthData No
Async getMyCoursesSequential
getMyCoursesSequential(auth: CLAuthData)
Decorators :
@HttpCode(HttpStatus.OK)
@Get('sequential')
@ApiOperation({summary: 'Get my enrolled courses ordered sequentially with accessibility'})
@ApiResponse({status: 200, description: 'Returns array of enrolled courses with sequential position and accessibility', type: undefined})

Get all courses ordered sequentially with accessibility validation GET /api/client/my-course/sequential

Parameters :
Name Type Optional
auth CLAuthData No
Async getPostBatPdfUrl
getPostBatPdfUrl(learningGroupParticipantId: number, auth: CLAuthData)
Decorators :
@HttpCode(HttpStatus.OK)
@Get(':id/post-bat-pdf-url')
@ApiOperation({summary: 'Get post-BAT PDF download URL (presigned S3 URL)'})
@ApiResponse({status: 200, description: 'Presigned URL returned successfully', schema: undefined})
@ApiResponse({status: 404, description: 'Course not found or PDF not available'})

Get post-BAT PDF download URL GET /api/client/my-course/:id/post-bat-pdf-url

Parameters :
Name Type Optional
learningGroupParticipantId number No
auth CLAuthData No
Returns : Promise<literal type>
Async getPreBatPdfUrl
getPreBatPdfUrl(learningGroupParticipantId: number, auth: CLAuthData)
Decorators :
@HttpCode(HttpStatus.OK)
@Get(':id/pre-bat-pdf-url')
@ApiOperation({summary: 'Get pre-BAT PDF download URL (presigned S3 URL)'})
@ApiResponse({status: 200, description: 'Presigned URL returned successfully', schema: undefined})
@ApiResponse({status: 404, description: 'Course not found or PDF not available'})

Get pre-BAT PDF download URL GET /api/client/my-course/:id/pre-bat-pdf-url

Parameters :
Name Type Optional
learningGroupParticipantId number No
auth CLAuthData No
Returns : Promise<literal type>
Async getResumeCourse
getResumeCourse(auth: CLAuthData)
Decorators :
@HttpCode(HttpStatus.OK)
@Get('resume')
@ApiOperation({summary: 'Get resume course information (last accessed course)'})
@ApiResponse({status: 200, description: 'Returns resume course information with last page accessed'})
@ApiResponse({status: 404, description: 'No course to resume'})

Get resume course information (last accessed course) GET /api/client/my-course/resume

Parameters :
Name Type Optional
auth CLAuthData No
Returns : Promise<literal type | null>
Async regeneratePostBatAnalysis
regeneratePostBatAnalysis(learningGroupParticipantId: number, auth: CLAuthData)
Decorators :
@HttpCode(HttpStatus.OK)
@Post(':id/regenerate-post-bat-analysis')
@ApiOperation({summary: 'Regenerate POST-BAT analysis by resending data to CR API'})
@ApiResponse({status: 200, description: 'POST-BAT analysis regeneration triggered successfully', schema: undefined})
@ApiResponse({status: 404, description: 'Course enrollment not found'})

Regenerate POST-BAT analysis by resending data to CR API POST /api/client/my-course/:id/regenerate-post-bat-analysis

Parameters :
Name Type Optional
learningGroupParticipantId number No
auth CLAuthData No
Returns : Promise<literal type>
Async regeneratePreBatAnalysis
regeneratePreBatAnalysis(learningGroupParticipantId: number, auth: CLAuthData)
Decorators :
@HttpCode(HttpStatus.OK)
@Post(':id/regenerate-pre-bat-analysis')
@ApiOperation({summary: 'Regenerate PRE-BAT analysis by resending data to CR API'})
@ApiResponse({status: 200, description: 'PRE-BAT analysis regeneration triggered successfully', schema: undefined})
@ApiResponse({status: 404, description: 'Course enrollment not found'})

Regenerate PRE-BAT analysis by resending data to CR API POST /api/client/my-course/:id/regenerate-pre-bat-analysis

Parameters :
Name Type Optional
learningGroupParticipantId number No
auth CLAuthData No
Returns : Promise<literal type>
import { CLAuthData, ClientAuth } from "@api/shared/decorators";
import {
  Controller,
  Get,
  HttpCode,
  HttpStatus,
  NotFoundException,
  Param,
  ParseIntPipe,
  Post,
  Res,
} from "@nestjs/common";
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
import { FastifyReply } from "fastify";
import { CLMyCourseDto, CLMyCourseListResponseDto } from "./dto";
import { CLMyCourseService } from "./my-course.service";

@ApiTags("Client - My Courses")
@Controller("api/client/my-course")
export class CLMyCourseController {
  constructor(private myCourseService: CLMyCourseService) {}

  /**
   * Get all courses the current participant is enrolled in
   * GET /api/client/my-course
   */
  @HttpCode(HttpStatus.OK)
  @Get()
  @ApiOperation({ summary: "Get my enrolled courses with progress" })
  @ApiResponse({
    status: 200,
    description: "Returns courses and company_active flag (for expired-license banner)",
    type: CLMyCourseListResponseDto,
  })
  async getMyCourses(@ClientAuth() auth: CLAuthData): Promise<CLMyCourseListResponseDto> {
    return this.myCourseService.getMyCourses(auth.participantId);
  }

  /**
   * Get all courses ordered sequentially with accessibility validation
   * GET /api/client/my-course/sequential
   */
  @HttpCode(HttpStatus.OK)
  @Get("sequential")
  @ApiOperation({ summary: "Get my enrolled courses ordered sequentially with accessibility" })
  @ApiResponse({
    status: 200,
    description: "Returns array of enrolled courses with sequential position and accessibility",
    type: [CLMyCourseDto],
  })
  async getMyCoursesSequential(@ClientAuth() auth: CLAuthData): Promise<CLMyCourseDto[]> {
    return this.myCourseService.getMyCoursesSequential(auth.participantId);
  }

  /**
   * Get a single enrolled course by course_code with sequential validation
   * Returns redirect info if not accessible (soft redirect approach)
   * GET /api/client/my-course/by-code/:courseCode
   * Example: /api/client/my-course/by-code/communication-skills-101
   */
  @HttpCode(HttpStatus.OK)
  @Get("by-code/:courseCode")
  @ApiOperation({
    summary: "Get my enrolled course by course code with sequential validation",
    description:
      "Returns course data. If not accessible, includes redirect information to redirect to the first incomplete course.",
  })
  @ApiResponse({
    status: 200,
    description: "Returns enrolled course with redirect info if not accessible",
    schema: {
      type: "object",
      properties: {
        course: { type: "object" },
        is_accessible: { type: "boolean" },
        requires_redirect: { type: "boolean" },
        redirect_to_course: { type: "object" },
        message: { type: "string" },
      },
    },
  })
  @ApiResponse({
    status: 404,
    description: "Course not found or not enrolled",
  })
  async getMyCourseByCode(
    @Param("courseCode") courseCode: string,
    @ClientAuth() auth: CLAuthData,
  ): Promise<{
    course: CLMyCourseDto;
    is_accessible: boolean;
    requires_redirect?: boolean;
    redirect_to_course?: CLMyCourseDto;
    message?: string;
  }> {
    return this.myCourseService.getMyCourseByCode(auth.participantId, courseCode);
  }

  /**
   * Get resume course information (last accessed course)
   * GET /api/client/my-course/resume
   */
  @HttpCode(HttpStatus.OK)
  @Get("resume")
  @ApiOperation({ summary: "Get resume course information (last accessed course)" })
  @ApiResponse({
    status: 200,
    description: "Returns resume course information with last page accessed",
  })
  @ApiResponse({
    status: 404,
    description: "No course to resume",
  })
  async getResumeCourse(@ClientAuth() auth: CLAuthData): Promise<{
    course: CLMyCourseDto;
    lastPageId: number;
    lastPageTitle: string;
    progress: number;
    daysSinceLastActivity: number;
  } | null> {
    return this.myCourseService.getResumeCourseInfo(auth.participantId);
  }

  /**
   * Get a single enrolled course by LearningGroupParticipant ID
   * GET /api/client/my-course/:id
   */
  @HttpCode(HttpStatus.OK)
  @Get(":id")
  @ApiOperation({ summary: "Get my enrolled course by LearningGroupParticipant ID" })
  @ApiResponse({
    status: 200,
    description: "Returns enrolled course with progress data",
    type: CLMyCourseDto,
  })
  @ApiResponse({
    status: 404,
    description: "Course not found or not enrolled",
  })
  async getMyCourseById(
    @Param("id", ParseIntPipe) learningGroupParticipantId: number,
    @ClientAuth() auth: CLAuthData,
  ): Promise<CLMyCourseDto> {
    const course = await this.myCourseService.getMyCourseById(auth.participantId, learningGroupParticipantId);

    if (!course) {
      throw new NotFoundException(
        `Course enrollment with ID ${learningGroupParticipantId} not found or not enrolled`,
      );
    }

    return course;
  }

  /**
   * Download pre-BAT HTML for a course
   * GET /api/client/my-course/:id/pre-bat-download
   */
  @HttpCode(HttpStatus.OK)
  @Get(":id/pre-bat-download")
  @ApiOperation({ summary: "Download pre-BAT analysis HTML for a course" })
  @ApiResponse({
    status: 200,
    description: "HTML content returned successfully",
    content: {
      "text/html": {
        schema: {
          type: "string",
        },
      },
    },
  })
  @ApiResponse({
    status: 404,
    description: "Course not found or pre-BAT analysis not available",
  })
  async downloadPreBatHtml(
    @Param("id", ParseIntPipe) learningGroupParticipantId: number,
    @ClientAuth() auth: CLAuthData,
    @Res() reply: FastifyReply,
  ): Promise<void> {
    const html = await this.myCourseService.generatePreBatHtml(auth.participantId, learningGroupParticipantId);

    reply.header("Content-Type", "text/html; charset=utf-8");
    reply.send(html);
  }

  /**
   * Get pre-BAT PDF download URL
   * GET /api/client/my-course/:id/pre-bat-pdf-url
   */
  @HttpCode(HttpStatus.OK)
  @Get(":id/pre-bat-pdf-url")
  @ApiOperation({ summary: "Get pre-BAT PDF download URL (presigned S3 URL)" })
  @ApiResponse({
    status: 200,
    description: "Presigned URL returned successfully",
    schema: {
      type: "object",
      properties: {
        url: { type: "string", example: "https://s3.amazonaws.com/..." },
      },
    },
  })
  @ApiResponse({
    status: 404,
    description: "Course not found or PDF not available",
  })
  async getPreBatPdfUrl(
    @Param("id", ParseIntPipe) learningGroupParticipantId: number,
    @ClientAuth() auth: CLAuthData,
  ): Promise<{ url: string }> {
    const url = await this.myCourseService.getPreBatPdfDownloadUrl(auth.participantId, learningGroupParticipantId);
    return { url };
  }

  /**
   * Download post-BAT HTML for a course
   * GET /api/client/my-course/:id/post-bat-download
   */
  @HttpCode(HttpStatus.OK)
  @Get(":id/post-bat-download")
  @ApiOperation({ summary: "Download post-BAT analysis HTML for a course with learning recommendations" })
  @ApiResponse({
    status: 200,
    description: "HTML content returned successfully",
    content: {
      "text/html": {
        schema: {
          type: "string",
        },
      },
    },
  })
  @ApiResponse({
    status: 404,
    description: "Course not found or post-BAT analysis not available",
  })
  async downloadPostBatHtml(
    @Param("id", ParseIntPipe) learningGroupParticipantId: number,
    @ClientAuth() auth: CLAuthData,
    @Res() reply: FastifyReply,
  ): Promise<void> {
    const html = await this.myCourseService.generatePostBatHtml(auth.participantId, learningGroupParticipantId);

    reply.header("Content-Type", "text/html; charset=utf-8");
    reply.send(html);
  }

  /**
   * Get post-BAT PDF download URL
   * GET /api/client/my-course/:id/post-bat-pdf-url
   */
  @HttpCode(HttpStatus.OK)
  @Get(":id/post-bat-pdf-url")
  @ApiOperation({ summary: "Get post-BAT PDF download URL (presigned S3 URL)" })
  @ApiResponse({
    status: 200,
    description: "Presigned URL returned successfully",
    schema: {
      type: "object",
      properties: {
        url: { type: "string", example: "https://s3.amazonaws.com/..." },
      },
    },
  })
  @ApiResponse({
    status: 404,
    description: "Course not found or PDF not available",
  })
  async getPostBatPdfUrl(
    @Param("id", ParseIntPipe) learningGroupParticipantId: number,
    @ClientAuth() auth: CLAuthData,
  ): Promise<{ url: string }> {
    const url = await this.myCourseService.getPostBatPdfDownloadUrl(auth.participantId, learningGroupParticipantId);
    return { url };
  }

  /**
   * Regenerate PRE-BAT analysis by resending data to CR API
   * POST /api/client/my-course/:id/regenerate-pre-bat-analysis
   */
  @HttpCode(HttpStatus.OK)
  @Post(":id/regenerate-pre-bat-analysis")
  @ApiOperation({ summary: "Regenerate PRE-BAT analysis by resending data to CR API" })
  @ApiResponse({
    status: 200,
    description: "PRE-BAT analysis regeneration triggered successfully",
    schema: {
      type: "object",
      properties: {
        success: { type: "boolean", example: true },
        message: { type: "string", example: "PRE-BAT analysis regeneration triggered successfully" },
      },
    },
  })
  @ApiResponse({
    status: 404,
    description: "Course enrollment not found",
  })
  async regeneratePreBatAnalysis(
    @Param("id", ParseIntPipe) learningGroupParticipantId: number,
    @ClientAuth() auth: CLAuthData,
  ): Promise<{ success: boolean; message: string }> {
    return this.myCourseService.regeneratePreBatAnalysis(auth.participantId, learningGroupParticipantId);
  }

  /**
   * Regenerate POST-BAT analysis by resending data to CR API
   * POST /api/client/my-course/:id/regenerate-post-bat-analysis
   */
  @HttpCode(HttpStatus.OK)
  @Post(":id/regenerate-post-bat-analysis")
  @ApiOperation({ summary: "Regenerate POST-BAT analysis by resending data to CR API" })
  @ApiResponse({
    status: 200,
    description: "POST-BAT analysis regeneration triggered successfully",
    schema: {
      type: "object",
      properties: {
        success: { type: "boolean", example: true },
        message: { type: "string", example: "POST-BAT analysis regeneration triggered successfully" },
      },
    },
  })
  @ApiResponse({
    status: 404,
    description: "Course enrollment not found",
  })
  async regeneratePostBatAnalysis(
    @Param("id", ParseIntPipe) learningGroupParticipantId: number,
    @ClientAuth() auth: CLAuthData,
  ): Promise<{ success: boolean; message: string }> {
    return this.myCourseService.regeneratePostBatAnalysis(auth.participantId, learningGroupParticipantId);
  }
}

results matching ""

    No results matching ""