apps/recallassess/recallassess-api/src/api/client/participant/participant.controller.ts
api/client/participant
Methods |
|
| Async addParticipant | |||||||||
addParticipant(data: AddParticipantDto, auth: CLAuthData)
|
|||||||||
Decorators :
@HttpCode(HttpStatus.CREATED)
|
|||||||||
|
Add a new participant POST /api/client/participant
Parameters :
Returns :
Promise<CLParticipantDto>
|
| Async checkParticipantActiveWork | |||||||||
checkParticipantActiveWork(id: number, auth: CLAuthData)
|
|||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
|||||||||
|
Check participant active work before deactivation GET /api/client/participant/:id/active-work
Parameters :
Returns :
Promise<ParticipantActiveWorkDto>
|
| Async deleteParticipant | |||||||||
deleteParticipant(id: number, auth: CLAuthData)
|
|||||||||
Decorators :
@HttpCode(HttpStatus.NO_CONTENT)
|
|||||||||
|
Delete a company contact. Participant administrator only; blocked when an active course allocation exists. DELETE /api/client/participant/:id
Parameters :
Returns :
Promise<void>
|
| Async getAllParticipants | |||||||||
getAllParticipants(query: ParticipantQueryDto, auth: CLAuthData)
|
|||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
|||||||||
|
Get all participants for client consumption with optional filtering and pagination GET /api/client/participant?page=1&limit=20&sq=john&status=active&participantGroup=Sales
Parameters :
Returns :
Promise<CLParticipantListResponse>
|
| Async getLicenseAvailability | ||||||
getLicenseAvailability(auth: CLAuthData)
|
||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
||||||
|
Get license availability for adding participants GET /api/client/participant/license-availability
Parameters :
Returns :
Promise<literal type>
|
| Async getParticipantAdmin | ||||||
getParticipantAdmin(auth: CLAuthData)
|
||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
||||||
|
Get the current participant's administrator GET /api/client/participant/admin
Parameters :
Returns :
Promise<literal type | null>
|
| Async getParticipantById | |||||||||
getParticipantById(id: number, auth: CLAuthData)
|
|||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
|||||||||
|
Get a single participant by ID GET /api/client/participant/:id
Parameters :
Returns :
Promise<CLParticipantDto>
|
| Async saveParticipant | ||||||||||||
saveParticipant(id: number, data: SaveParticipantDto, auth: CLAuthData)
|
||||||||||||
Decorators :
@HttpCode(HttpStatus.OK)
|
||||||||||||
|
Save (update) a participant PUT /api/client/participant/:id
Parameters :
Returns :
Promise<CLParticipantDto>
|
import { CLAuthData, ClientAuth } from "@api/shared/decorators";
import { Public } from "@bish-nest/core/auth/decorator/public.decorator";
import {
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
NotFoundException,
Param,
ParseIntPipe,
Post,
Put,
Query,
} from "@nestjs/common";
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
import {
AddParticipantDto,
CLParticipantDto,
CLParticipantListResponse,
ParticipantActiveWorkDto,
ParticipantQueryDto,
SaveParticipantDto,
} from "./dto";
import { CLParticipantService } from "./participant.service";
@ApiTags("Client - Participants")
@Controller("api/client/participant")
export class CLParticipantController {
constructor(private participantService: CLParticipantService) {}
/**
* Get all participants for client consumption with optional filtering and pagination
* GET /api/client/participant?page=1&limit=20&sq=john&status=active&participantGroup=Sales
*/
@HttpCode(HttpStatus.OK)
@Public()
@Get()
@ApiOperation({
summary: "Get all participants with pagination, search and filters",
description:
"Returns paginated participants filtered by search query, status, and participant group with metadata",
})
@ApiResponse({
status: 200,
description:
"Returns paginated list of participants with metadata (page, limit, totalCount, totalPages, etc.)",
})
async getAllParticipants(
@Query() query: ParticipantQueryDto,
@ClientAuth() auth: CLAuthData,
): Promise<CLParticipantListResponse> {
return this.participantService.getFilteredParticipants(
auth.companyId,
query.page || 1,
query.limit || 20,
query.sq,
query.status,
query.participantGroup,
auth.participantId,
auth.role,
);
}
/**
* Add a new participant
* POST /api/client/participant
*/
@HttpCode(HttpStatus.CREATED)
@Public()
@Post()
@ApiOperation({ summary: "Add a new participant" })
@ApiResponse({
status: 201,
description: "Participant added successfully",
type: CLParticipantDto,
})
@ApiResponse({
status: 409,
description: "A contact with this email already exists",
})
async addParticipant(
@Body() data: AddParticipantDto,
@ClientAuth() auth: CLAuthData,
): Promise<CLParticipantDto> {
return this.participantService.addParticipant(data, auth.companyId, auth.participantId);
}
/**
* Get license availability for adding participants
* GET /api/client/participant/license-availability
*/
@HttpCode(HttpStatus.OK)
@Public()
@Get("license-availability")
@ApiOperation({ summary: "Get license availability information" })
@ApiResponse({
status: 200,
description: "Returns license availability information",
})
async getLicenseAvailability(@ClientAuth() auth: CLAuthData): Promise<{
totalLicenses: number;
currentActiveParticipants: number;
availableSlots: number;
canAddMore: boolean;
}> {
return this.participantService.getParticipantLicenseAvailability(auth.companyId);
}
/**
* Check participant active work before deactivation
* GET /api/client/participant/:id/active-work
*/
@HttpCode(HttpStatus.OK)
@Public()
@Get(":id/active-work")
@ApiOperation({
summary: "Check if participant has active work",
description: "Returns information about incomplete courses and pending assessments",
})
@ApiResponse({
status: 200,
description: "Returns active work information",
type: ParticipantActiveWorkDto,
})
@ApiResponse({
status: 400,
description: "Participant not found",
})
async checkParticipantActiveWork(
@Param("id", ParseIntPipe) id: number,
@ClientAuth() auth: CLAuthData,
): Promise<ParticipantActiveWorkDto> {
return this.participantService.checkParticipantActiveWork(id, auth.companyId);
}
/**
* Get a single participant by ID
* GET /api/client/participant/:id
*/
@HttpCode(HttpStatus.OK)
@Public()
@Get(":id")
@ApiOperation({ summary: "Get a participant by ID" })
@ApiResponse({
status: 200,
description: "Returns a single participant with enriched data",
type: CLParticipantDto,
})
@ApiResponse({
status: 404,
description: "Participant not found",
})
async getParticipantById(
@Param("id", ParseIntPipe) id: number,
@ClientAuth() auth: CLAuthData,
): Promise<CLParticipantDto> {
const participant = await this.participantService.getParticipantById(id, auth.companyId);
if (!participant) {
throw new NotFoundException(`Participant with ID ${id} not found`);
}
return participant;
}
/**
* Get the current participant's administrator
* GET /api/client/participant/admin
*/
@HttpCode(HttpStatus.OK)
@Public()
@Get("admin")
@ApiOperation({
summary: "Get the current participant's administrator",
description: "Returns the Participant Administrator (User) who manages this participant for license allocation",
})
@ApiResponse({
status: 200,
description: "Returns the participant administrator's contact information",
})
@ApiResponse({
status: 404,
description: "Participant not found or no administrator assigned",
})
async getParticipantAdmin(@ClientAuth() auth: CLAuthData): Promise<{
id: number;
first_name: string;
last_name: string;
email: string;
phone: string | null;
} | null> {
if (!auth.participantId) {
throw new NotFoundException("Participant ID not found in authentication");
}
const admin = await this.participantService.getParticipantAdmin(
auth.participantId,
auth.companyId,
);
if (!admin) {
throw new NotFoundException(
"Participant administrator not found. Please contact your system administrator.",
);
}
return admin;
}
/**
* Save (update) a participant
* PUT /api/client/participant/:id
*/
@HttpCode(HttpStatus.OK)
@Public()
@Put(":id")
@ApiOperation({ summary: "Save a participant" })
@ApiResponse({
status: 200,
description: "Participant saved successfully",
type: CLParticipantDto,
})
@ApiResponse({
status: 404,
description: "Participant not found",
})
async saveParticipant(
@Param("id", ParseIntPipe) id: number,
@Body() data: SaveParticipantDto,
@ClientAuth() auth: CLAuthData,
): Promise<CLParticipantDto> {
const participant = await this.participantService.saveParticipant(
id,
auth.companyId,
data,
auth.participantId,
);
if (!participant) {
throw new NotFoundException(`Participant with ID ${id} not found`);
}
return participant;
}
/**
* Delete a company contact. Participant administrator only; blocked when an active course allocation exists.
* DELETE /api/client/participant/:id
*/
@HttpCode(HttpStatus.NO_CONTENT)
@Public()
@Delete(":id")
@ApiOperation({ summary: "Delete a participant contact" })
@ApiResponse({ status: 204, description: "Participant deleted successfully" })
@ApiResponse({ status: 403, description: "Forbidden — not a participant administrator" })
@ApiResponse({
status: 422,
description: "Contact has an active course allocation and cannot be deleted",
})
async deleteParticipant(
@Param("id", ParseIntPipe) id: number,
@ClientAuth() auth: CLAuthData,
): Promise<void> {
await this.participantService.deleteParticipantForParticipantAdmin(
id,
auth.companyId,
auth.participantId,
auth.role,
);
}
}