From e71430d02e9c66ae7397590bcc50c4b41fcece09 Mon Sep 17 00:00:00 2001 From: richard483 Date: Tue, 16 Jan 2024 16:53:43 +0700 Subject: [PATCH 1/2] added get company info api, job applicants api, bind job & user with contract --- docs/company-controller.md | 61 ++++ docs/job-controller.md | 263 ++++++++++++++++++ package-lock.json | 4 +- package.json | 2 +- .../migration.sql | 2 + prisma/schema.prisma | 14 +- prisma/seed.ts | 1 + src/company/company.controller.ts | 25 +- src/company/company.repository.ts | 8 + src/company/company.service.ts | 4 + src/contract/contract.repository.ts | 2 +- src/job/job.controller.ts | 28 ++ src/job/job.module.ts | 16 +- src/job/job.repository.ts | 40 ++- src/job/job.service.ts | 39 ++- 15 files changed, 491 insertions(+), 18 deletions(-) create mode 100644 docs/job-controller.md create mode 100644 prisma/migrations/20240116080618_updated_namin_and_contract_schema/migration.sql diff --git a/docs/company-controller.md b/docs/company-controller.md index e102499..07fc89b 100644 --- a/docs/company-controller.md +++ b/docs/company-controller.md @@ -85,6 +85,11 @@ HTTP Code: 400 1. [Update](#update) ## Update +| Key | Description | +| ------ | ------------- | +| Method | `POST` | +| Path | `/company/update` | + | Key | Type | | ---------- | ------ | @@ -139,4 +144,60 @@ HTTP Code: 400 }, "error": "BAD_REQUEST" } +``` + +--- + +1. [GetInfo](#getInfo) + +## Get Info + +| Key | Description | +| ------ | ------------- | +| Method | `POST` | +| Path | `/info/:companyId` | + +### Success Response + +HTTP Code: 200 + +```json +{ + "status": true, + "statusCode": 200, + "data": { + "id": "7b9e79f7-ebc1-49de-a0db-b9d53cfe1cf7", + "profilePicture": null, + "name": "Nijisanji Anycolor", + "description": "Nijisanji is a VTuber agency under Ichikara Inc. While the company is based in Japan, it also has branches in China, Indonesia, South Korea, and India.", + "createdAt": "2024-01-16T08:09:59.992Z", + "updatedAt": "2024-01-16T08:09:59.992Z" + } +} +``` + +### Invalid credential + +HTTP Code: 401 + +```json +{ + "status": true, + "statusCode": 401, + "data": { + "access": "UNAUTHORIZED" + } +} +``` + +### no data + +HTTP Code: 200 + +```json +{ + "status": true, + "statusCode": 200, + "data": null +} ``` \ No newline at end of file diff --git a/docs/job-controller.md b/docs/job-controller.md new file mode 100644 index 0000000..fd834b3 --- /dev/null +++ b/docs/job-controller.md @@ -0,0 +1,263 @@ +# Job controller + +--- + +[Get applicants list](#getApplicants) + +## Get Applicants + +| Key | Description | +| ------ | ------------- | +| Method | `POST` | +| Path | `/job/applicants/:jobId` | + +### Request Body (all fields are optional) + +| Key | Type | +| ---------- | ------ | +| `page` | number? | +| `size` | number? | + +### Success Response + +HTTP Code: 200 + +```json +{ + "status": true, + "statusCode": 200, + "data": { + "data": [ + { + "id": "a05c4cf9-fac2-4d63-a542-97945bffd0b0", + "email": "richard.william483@gmail.com", + "username": "richard__uwu", + "firstName": "Richard", + "lastName": "William", + "password": "$2b$10$Cxot0Ynr0gnSCDGC7wjo4upLoXABsgZ01BUmbqh2tQ7S76sXm3GN6", + "createdAt": "2024-01-16T08:10:00.242Z", + "updatedAt": "2024-01-16T08:10:00.876Z", + "roles": [ + "USER" + ], + "description": "default richard description that being created by seed.ts", + "previousWorkplaceId": [ + "7b9e79f7-ebc1-49de-a0db-b9d53cfe1cf7", + "84d98c27-d50a-4deb-b4be-446dbe342598", + "ff567d29-8dea-4de9-a24d-6affa42f50c8" + ], + "previousWorkplaceCount": 3, + "ratingsAvg": null, + "companyId": null, + "portfolio": [], + "profilePicture": null, + "hasGoogleAccount": false + }, + { + "id": "d433d551-64ed-40f1-b799-4c3499bd093c", + "email": "default.user@email.com", + "username": "Default User", + "firstName": "Default", + "lastName": "User", + "password": "$2b$10$5R7ho.XC03csXn3NrbjHKOxN.P0JrHjbIUD8BsM3Dgb7T0Xo40JOa", + "createdAt": "2024-01-16T08:10:00.313Z", + "updatedAt": "2024-01-16T08:10:00.875Z", + "roles": [ + "USER" + ], + "description": "default user description that being created by seed.ts", + "previousWorkplaceId": [ + "7b9e79f7-ebc1-49de-a0db-b9d53cfe1cf7", + "84d98c27-d50a-4deb-b4be-446dbe342598", + "ff567d29-8dea-4de9-a24d-6affa42f50c8" + ], + "previousWorkplaceCount": 3, + "ratingsAvg": null, + "companyId": null, + "portfolio": [], + "profilePicture": null, + "hasGoogleAccount": false + }, + { + "id": "d433d551-64ed-40f1-b799-4c3499bd093c", + "email": "default.user@email.com", + "username": "Default User", + "firstName": "Default", + "lastName": "User", + "password": "$2b$10$5R7ho.XC03csXn3NrbjHKOxN.P0JrHjbIUD8BsM3Dgb7T0Xo40JOa", + "createdAt": "2024-01-16T08:10:00.313Z", + "updatedAt": "2024-01-16T08:10:00.875Z", + "roles": [ + "USER" + ], + "description": "default user description that being created by seed.ts", + "previousWorkplaceId": [ + "7b9e79f7-ebc1-49de-a0db-b9d53cfe1cf7", + "84d98c27-d50a-4deb-b4be-446dbe342598", + "ff567d29-8dea-4de9-a24d-6affa42f50c8" + ], + "previousWorkplaceCount": 3, + "ratingsAvg": null, + "companyId": null, + "portfolio": [], + "profilePicture": null, + "hasGoogleAccount": false + }, + { + "id": "d433d551-64ed-40f1-b799-4c3499bd093c", + "email": "default.user@email.com", + "username": "Default User", + "firstName": "Default", + "lastName": "User", + "password": "$2b$10$5R7ho.XC03csXn3NrbjHKOxN.P0JrHjbIUD8BsM3Dgb7T0Xo40JOa", + "createdAt": "2024-01-16T08:10:00.313Z", + "updatedAt": "2024-01-16T08:10:00.875Z", + "roles": [ + "USER" + ], + "description": "default user description that being created by seed.ts", + "previousWorkplaceId": [ + "7b9e79f7-ebc1-49de-a0db-b9d53cfe1cf7", + "84d98c27-d50a-4deb-b4be-446dbe342598", + "ff567d29-8dea-4de9-a24d-6affa42f50c8" + ], + "previousWorkplaceCount": 3, + "ratingsAvg": null, + "companyId": null, + "portfolio": [], + "profilePicture": null, + "hasGoogleAccount": false + }, + { + "id": "d433d551-64ed-40f1-b799-4c3499bd093c", + "email": "default.user@email.com", + "username": "Default User", + "firstName": "Default", + "lastName": "User", + "password": "$2b$10$5R7ho.XC03csXn3NrbjHKOxN.P0JrHjbIUD8BsM3Dgb7T0Xo40JOa", + "createdAt": "2024-01-16T08:10:00.313Z", + "updatedAt": "2024-01-16T08:10:00.875Z", + "roles": [ + "USER" + ], + "description": "default user description that being created by seed.ts", + "previousWorkplaceId": [ + "7b9e79f7-ebc1-49de-a0db-b9d53cfe1cf7", + "84d98c27-d50a-4deb-b4be-446dbe342598", + "ff567d29-8dea-4de9-a24d-6affa42f50c8" + ], + "previousWorkplaceCount": 3, + "ratingsAvg": null, + "companyId": null, + "portfolio": [], + "profilePicture": null, + "hasGoogleAccount": false + } + ], + "hasPrevious": false, + "hasNext": true, + "totalPages": 6, + "isLast": false, + "isFirst": true + } +} +``` + +### Invalid credential + +HTTP Code: 401 + +```json +{ + "status": true, + "statusCode": 401, + "data": { + "access": "UNAUTHORIZED" + } +} +``` + +### Empty body + +HTTP Code: 400 + +```json +{ + "status": true, + "statusCode": 200, + "data": { + "hasPrevious": false, + "hasNext": false, + "totalPages": null, + "isLast": false, + "isFirst": true + } +} + +``` +--- +[Apply Job](#applyJob) + +## Apply Job + +| Key | Description | +| ------ | ---------------- | +| Method | `GET` | +| Path | `/job/apply/:jobId` | + + +### Success Response + +HTTP Code: 200 + +```json +{ + "status": true, + "statusCode": 200, + "data": { + "id": "7c7e2bac-415d-4f3b-8125-814684a78cdd", + "userId": "3f016730-07ae-4ef6-88f1-1fc3ce57008e", + "jobId": "e00b4d7f-19c1-40f7-8e96-895dcd9b6f0e", + "paymentId": null, + "title": "This is job1 title for 3f016730-07ae-4ef6-88f1-1fc3ce57008e", + "description": "Contract for the following job description: This is job1 description for ninisani", + "paymentRate": null, + "template": null, + "createdAt": "2024-01-16T09:46:53.601Z", + "updatedAt": "2024-01-16T09:46:53.601Z", + "status": "PENDING", + "customField": null, + "workSubmission": null, + "ratingId": null + } +} +``` + +### Unauthorized + +HTTP Code: 401 + +```json +{ + "status": true, + "statusCode": 401, + "data": { + "access": "UNAUTHORIZED" + } +} +``` + +### Job not found + +HTTP Code: 401 + +```json +{ + "status": false, + "statusCode": 418, + "message": { + "jobId": "NOT_FOUND" + }, + "error": "INTERNAL_SERVER_ERROR" +} +``` diff --git a/package-lock.json b/package-lock.json index 4086435..b9cc11f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "backend-8tech", - "version": "4.0.4", + "version": "4.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "backend-8tech", - "version": "4.0.4", + "version": "4.0.5", "license": "UNLICENSED", "dependencies": { "@nestjs/common": "^9.4.3", diff --git a/package.json b/package.json index 9bc4432..3fb2675 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "backend-8tech", - "version": "4.0.4", + "version": "4.0.5", "description": "", "author": "8tech", "private": true, diff --git a/prisma/migrations/20240116080618_updated_namin_and_contract_schema/migration.sql b/prisma/migrations/20240116080618_updated_namin_and_contract_schema/migration.sql new file mode 100644 index 0000000..a9f920f --- /dev/null +++ b/prisma/migrations/20240116080618_updated_namin_and_contract_schema/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Contract" ALTER COLUMN "paymentRate" DROP NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 8b749b8..5a12ee1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -33,7 +33,7 @@ model User { profilePicture String? hasGoogleAccount Boolean @default(false) company Company? @relation(fields: [companyId], references: [id]) - Contract Contract[] + contract Contract[] } model JobVacancy { @@ -56,7 +56,7 @@ model Company { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt jobs JobVacancy[] - User User[] + user User[] } model Contract { @@ -66,7 +66,7 @@ model Contract { paymentId String? title String description String - paymentRate Int + paymentRate Int? template String? @db.VarChar() createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@ -74,10 +74,10 @@ model Contract { customField String? workSubmission String? ratingId String? - JobVacancy JobVacancy? @relation(fields: [jobId], references: [id]) - User User? @relation(fields: [userId], references: [id]) - Payment Payment? @relation(fields: [paymentId], references: [id]) - Rating Rating? @relation(fields: [ratingId], references: [id]) + jobVacancy JobVacancy? @relation(fields: [jobId], references: [id]) + user User? @relation(fields: [userId], references: [id]) + payment Payment? @relation(fields: [paymentId], references: [id]) + rating Rating? @relation(fields: [ratingId], references: [id]) } model Payment { diff --git a/prisma/seed.ts b/prisma/seed.ts index f8ea59f..7fd1302 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -590,6 +590,7 @@ async function contract() { } async function clean() { + await prisma.rating.deleteMany({}); await prisma.contract.deleteMany({}); await prisma.jobVacancy.deleteMany({}); await prisma.company.deleteMany({}); diff --git a/src/company/company.controller.ts b/src/company/company.controller.ts index abd3a2d..ec11fc1 100644 --- a/src/company/company.controller.ts +++ b/src/company/company.controller.ts @@ -1,8 +1,20 @@ -import { Body, Controller, Post, Res } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { + Body, + Controller, + Get, + Param, + Post, + Res, + UseGuards, +} from '@nestjs/common'; +import { ApiParam, ApiTags } from '@nestjs/swagger'; import { CompanyService } from './company.service'; import { CompanyCreateDto } from './dto/company-create.dto'; import { CompanyUpdateDto } from './dto/company-update.dto'; +import { Roles } from '../auth/roles/role.decorator'; +import { JwtAuthGuard } from '../auth/jwt/jwt-auth.guard'; +import { RoleGuard } from '../auth/roles/role.guard'; +import { Role } from '../auth/roles/role.enum'; @ApiTags('Company') @Controller('company') @@ -16,8 +28,17 @@ export class CompanyController { } @Post('update') + @Roles(Role.RECRUITER, Role.RECRUITER) + @UseGuards(JwtAuthGuard, RoleGuard) async updateCompany(@Res() res, @Body() data: CompanyUpdateDto) { const response = await this.companyService.update(data); return response; } + + @Get('info/:companyId') + @ApiParam({ name: 'companyId', type: String }) + async getInfo(@Res() res, @Param() params: any) { + const response = await this.companyService.get(params.companyId); + return response; + } } diff --git a/src/company/company.repository.ts b/src/company/company.repository.ts index 8c325b4..6188541 100644 --- a/src/company/company.repository.ts +++ b/src/company/company.repository.ts @@ -25,4 +25,12 @@ export class CompanyRepository { data, }); } + + async get(companyId: string): Promise { + return this.prisma.company.findUnique({ + where: { + id: companyId, + }, + }); + } } diff --git a/src/company/company.service.ts b/src/company/company.service.ts index 43e09a3..7e132d4 100644 --- a/src/company/company.service.ts +++ b/src/company/company.service.ts @@ -16,4 +16,8 @@ export class CompanyService { const { id, ...data } = company; return this.companyRepository.update(id, data); } + + async get(companyId: string): Promise { + return this.companyRepository.get(companyId); + } } diff --git a/src/contract/contract.repository.ts b/src/contract/contract.repository.ts index 4003867..9a9e2a2 100644 --- a/src/contract/contract.repository.ts +++ b/src/contract/contract.repository.ts @@ -55,7 +55,7 @@ export class ContractRepository { id, }, include: { - User: true, + user: true, }, }); } diff --git a/src/job/job.controller.ts b/src/job/job.controller.ts index 32730d6..74b1207 100644 --- a/src/job/job.controller.ts +++ b/src/job/job.controller.ts @@ -4,6 +4,7 @@ import { Get, Param, Post, + Request, Res, UseGuards, } from '@nestjs/common'; @@ -74,4 +75,31 @@ export class JobController { const response = await this.jobService.findManyByList(data); return response; } + + @ApiBearerAuth() + @Roles(Role.USER) + @ApiParam({ name: 'jobId', type: String }) + @UseGuards(JwtAuthGuard, RoleGuard) + @Get('apply/:jobId') + async applyJob(@Request() req, @Param() params: any) { + return await this.jobService.applyJob(req.user.id, params.jobId); + } + + @ApiBearerAuth() + @Roles(Role.USER, Role.RECRUITER) + @ApiParam({ name: 'jobId', type: String }) + @UseGuards(JwtAuthGuard, RoleGuard) + @Post('applicants/:jobId') + async getApplicants( + @Request() req, + @Param() params: any, + @Body() data: JobFilterRequest, + ) { + const response = await this.jobService.applicantsList( + params.jobId, + data.page, + data.size, + ); + return response; + } } diff --git a/src/job/job.module.ts b/src/job/job.module.ts index 72f6e14..d99d56a 100644 --- a/src/job/job.module.ts +++ b/src/job/job.module.ts @@ -3,9 +3,23 @@ import { JobService } from './job.service'; import { PrismaService } from '../prisma/prisma.service'; import { JobController } from './job.controller'; import { JobRepository } from './job.repository'; +import { ContractService } from '../contract/contract.service'; +import { ContractRepository } from '../contract/contract.repository'; +import { ContractHelper } from '../contract/contract.helper'; +import { PaymentService } from '../payment/payment.service'; +import { PaymentRepository } from '../payment/payment.repository'; @Module({ - providers: [JobService, PrismaService, JobRepository], + providers: [ + JobService, + PrismaService, + JobRepository, + ContractService, + ContractRepository, + ContractHelper, + PaymentService, + PaymentRepository, + ], exports: [JobService], controllers: [JobController], }) diff --git a/src/job/job.repository.ts b/src/job/job.repository.ts index 7f6fbee..d35ca5e 100644 --- a/src/job/job.repository.ts +++ b/src/job/job.repository.ts @@ -1,4 +1,4 @@ -import { HttpException, Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { JobUpdateDto } from './dto/job-update.dto'; import { JobCreateDto } from './dto/job-create.dto'; @@ -78,6 +78,44 @@ export class JobRepository { }); } + async getApplicantsById( + jobId: string, + page: number, + size: number, + ): Promise { + let res; + + try { + res = await this.prisma.jobVacancy.findUnique({ + where: { + id: jobId, + }, + include: { + contracts: { + ...pagination(page, size), + include: { + user: true, + }, + orderBy: { + createdAt: 'asc', + }, + }, + _count: { + select: { + contracts: true, + }, + }, + }, + }); + } catch (e) { + throw new HttpException({ prisma: e.message }, HttpStatus.BAD_REQUEST); + } + + const userList = res?.contracts.map((contract) => contract.user); + const total = res?._count.contracts; + return returnablePaginated(userList, total, page, size); + } + async findManyByFieldAndSortBy(reqData: JobFilterRequest): Promise { const result = this.prisma.jobVacancy.findMany({ ...pagination(reqData.page, reqData.size), diff --git a/src/job/job.service.ts b/src/job/job.service.ts index 91f699b..6fb51ce 100644 --- a/src/job/job.service.ts +++ b/src/job/job.service.ts @@ -1,13 +1,17 @@ -import { Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { JobRepository } from './job.repository'; import { JobCreateDto } from './dto/job-create.dto'; import { JobUpdateDto } from './dto/job-update.dto'; -import { JobVacancy } from '@prisma/client'; +import { Contract, JobVacancy } from '@prisma/client'; import { JobFilterRequest } from './dto/job-filter.request'; +import { ContractService } from '../contract/contract.service'; @Injectable() export class JobService { - constructor(private jobRepository: JobRepository) {} + constructor( + private jobRepository: JobRepository, + private contractService: ContractService, + ) {} async create(job: JobCreateDto): Promise { return this.jobRepository.create(job); @@ -29,4 +33,33 @@ export class JobService { async findManyByList(data: JobFilterRequest): Promise { return this.jobRepository.findManyByFieldAndSortBy(data); } + + async applyJob(userId: string, jobId: string): Promise { + const job: JobVacancy = await this.jobRepository.getById(jobId); + if (!job) { + throw new HttpException({ jobId: 'NOT_FOUND' }, HttpStatus.I_AM_A_TEAPOT); + } + return this.contractService + .create({ + userId, + jobId, + title: job.title + ' for ' + userId, + description: + 'Contract for the following job description: ' + job.description, + }) + .catch((error) => { + throw new HttpException( + { prisma: error.message }, + HttpStatus.I_AM_A_TEAPOT, + ); + }); + } + + async applicantsList( + jobId: string, + page: number, + size: number, + ): Promise { + return this.jobRepository.getApplicantsById(jobId, page, size); + } } From 55ea30fe319f3c6ff4ab2601a42513fdb474cbb8 Mon Sep 17 00:00:00 2001 From: richard483 Date: Tue, 16 Jan 2024 17:12:34 +0700 Subject: [PATCH 2/2] added get user job application list --- docs/user-controller.md | 91 ++++++++++++++++++++++++++++++++++++ src/users/user.controller.ts | 21 ++++++++- src/users/user.repository.ts | 42 ++++++++++++++++- src/users/users.service.ts | 8 ++++ 4 files changed, 160 insertions(+), 2 deletions(-) diff --git a/docs/user-controller.md b/docs/user-controller.md index 7024e4a..586dfab 100644 --- a/docs/user-controller.md +++ b/docs/user-controller.md @@ -240,3 +240,94 @@ HTTP Code: 401 } } ``` + +[Get Applied Job](#getAppliedJob) + +## Get Applied Job + +| Key | Description | +| ------ | ---------------- | +| Method | `Get` | +| Path | `/user/applied` | + +### Request Body + +| Key | Type | +| ---------------- | ------ | +| `page` | number | +| `size` | number | + + +### Success Response + +HTTP Code: 200 + +```json +{ + "status": true, + "statusCode": 200, + "data": { + "data": [ + { + "id": "e00b4d7f-19c1-40f7-8e96-895dcd9b6f0e", + "title": "This is job1 title", + "description": "This is job1 description for ninisani", + "createdAt": "2024-01-16T08:10:00.882Z", + "updatedAt": "2024-01-16T08:10:00.882Z", + "companyId": "7b9e79f7-ebc1-49de-a0db-b9d53cfe1cf7" + }, + { + "id": "e00b4d7f-19c1-40f7-8e96-895dcd9b6f0e", + "title": "This is job1 title", + "description": "This is job1 description for ninisani", + "createdAt": "2024-01-16T08:10:00.882Z", + "updatedAt": "2024-01-16T08:10:00.882Z", + "companyId": "7b9e79f7-ebc1-49de-a0db-b9d53cfe1cf7" + }, + { + "id": "e00b4d7f-19c1-40f7-8e96-895dcd9b6f0e", + "title": "This is job1 title", + "description": "This is job1 description for ninisani", + "createdAt": "2024-01-16T08:10:00.882Z", + "updatedAt": "2024-01-16T08:10:00.882Z", + "companyId": "7b9e79f7-ebc1-49de-a0db-b9d53cfe1cf7" + }, + { + "id": "e00b4d7f-19c1-40f7-8e96-895dcd9b6f0e", + "title": "This is job1 title", + "description": "This is job1 description for ninisani", + "createdAt": "2024-01-16T08:10:00.882Z", + "updatedAt": "2024-01-16T08:10:00.882Z", + "companyId": "7b9e79f7-ebc1-49de-a0db-b9d53cfe1cf7" + }, + { + "id": "e00b4d7f-19c1-40f7-8e96-895dcd9b6f0e", + "title": "This is job1 title", + "description": "This is job1 description for ninisani", + "createdAt": "2024-01-16T08:10:00.882Z", + "updatedAt": "2024-01-16T08:10:00.882Z", + "companyId": "7b9e79f7-ebc1-49de-a0db-b9d53cfe1cf7" + } + ], + "hasPrevious": false, + "hasNext": true, + "totalPages": 5, + "isLast": false, + "isFirst": true + } +} +``` + +### Unauthorized + +HTTP Code: 401 + +```json +{ + "status": true, + "statusCode": 401, + "data": { + "access": "UNAUTHORIZED" + } +} +``` diff --git a/src/users/user.controller.ts b/src/users/user.controller.ts index 8a78db5..c3bb124 100644 --- a/src/users/user.controller.ts +++ b/src/users/user.controller.ts @@ -9,7 +9,12 @@ import { UseGuards, UseInterceptors, } from '@nestjs/common'; -import { ApiBearerAuth, ApiCookieAuth, ApiTags } from '@nestjs/swagger'; +import { + ApiBearerAuth, + ApiBody, + ApiCookieAuth, + ApiTags, +} from '@nestjs/swagger'; import { UsersService } from './users.service'; import { Roles } from '../auth/roles/role.decorator'; import { JwtAuthGuard } from '../auth/jwt/jwt-auth.guard'; @@ -108,4 +113,18 @@ export class UserController { console.info('#UserGetProfileInfo request incoming'); return await this.userService.findOneById(req.user.id); } + + @ApiBearerAuth() + @Roles(Role.USER) + @UseGuards(JwtAuthGuard, RoleGuard) + @ApiBody({ type: Object }) + @Post('applied') + async appliedJob(@Request() req, @Body() data: any) { + const response = await this.userService.getAppliedJob( + req.user.id, + data?.page, + data?.size, + ); + return response; + } } diff --git a/src/users/user.repository.ts b/src/users/user.repository.ts index 248f4e7..d74b725 100644 --- a/src/users/user.repository.ts +++ b/src/users/user.repository.ts @@ -1,4 +1,4 @@ -import { HttpException, Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { pagination, returnablePaginated } from '../prisma/prisma.util'; import { UserFilterRequestDto } from './dto/user-filter.dto'; @@ -138,6 +138,46 @@ export class UserRepository { }); } + async getAppliedJob( + userId: string, + page: number, + size: number, + ): Promise { + let res; + + try { + res = await this.prisma.user.findUnique({ + where: { + id: userId, + }, + include: { + contract: { + ...pagination(page, size), + include: { + jobVacancy: true, + }, + orderBy: { + createdAt: 'asc', + }, + }, + _count: { + select: { + contract: true, + }, + }, + }, + }); + } catch (e) { + throw new HttpException({ prisma: e.message }, HttpStatus.BAD_REQUEST); + } + + console.log('#getAppliedJob res', res); + + const jobList = res?.contract.map((contract) => contract.jobVacancy); + const total = res?._count.contract; + return returnablePaginated(jobList, total, page, size); + } + async updateUserGoogleStatus(email: string, value: boolean): Promise { return this.prisma.user.update({ where: { diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 0936ccc..28429c5 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -93,4 +93,12 @@ export class UsersService { throw new HttpException(error, HttpStatus.BAD_REQUEST); } } + + async getAppliedJob( + userId: string, + page: number, + size: number, + ): Promise { + return this.userRepository.getAppliedJob(userId, page, size); + } }