From 814b25742b8ab5320f00ea5c4dc7db5ecabb24df Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 21 Jul 2025 16:03:38 -0400 Subject: [PATCH 1/4] fix(guard): small fix --- src/common/guards/roles.guard.ts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/common/guards/roles.guard.ts b/src/common/guards/roles.guard.ts index f36a0be..ef8b7ff 100644 --- a/src/common/guards/roles.guard.ts +++ b/src/common/guards/roles.guard.ts @@ -17,22 +17,19 @@ interface RequestWithUser extends Request { export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} - canActivate(ctx: ExecutionContext): boolean { - const requiredRoles = this.reflector.get( - ROLES_KEY, - ctx.getHandler(), - ); - //for "deny-by-default" when role is wrong or unavailable - if (!requiredRoles || requiredRoles.length === 0) { - return false; + canActivate(context: ExecutionContext): boolean { + const roles = this.reflector.getAllAndOverride(ROLES_KEY, [context.getHandler(), context.getClass()]); + + if (!roles) { + return true; } - const request = ctx.switchToHttp().getRequest(); + const request = context.switchToHttp().getRequest(); const user = request.user; if (!user) { return false; } - if (!requiredRoles.includes(user.role)) { + if (!roles.includes(user.role)) { throw new ForbiddenException( `The role ${user.role} is not authorized to access this resource.`, ); From affed3ce562054e76230e78bb0f733f97f393cf1 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 22 Jul 2025 08:11:08 -0400 Subject: [PATCH 2/4] fix(module): small fixes to module customers --- src/modules/customers/controllers/customers.controller.ts | 6 +++--- .../dtos/{create-customer.ts => create-customer.dto.ts} | 2 +- .../dtos/{update-customer.ts => update-customer.dto.ts} | 2 +- src/modules/customers/services/customers.service.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename src/modules/customers/dtos/{create-customer.ts => create-customer.dto.ts} (96%) rename src/modules/customers/dtos/{update-customer.ts => update-customer.dto.ts} (67%) diff --git a/src/modules/customers/controllers/customers.controller.ts b/src/modules/customers/controllers/customers.controller.ts index 7b32e9b..7ac87dd 100644 --- a/src/modules/customers/controllers/customers.controller.ts +++ b/src/modules/customers/controllers/customers.controller.ts @@ -1,8 +1,8 @@ import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; import { CustomersService } from '../services/customers.service'; -import { Customers, Employees } from '@prisma/client'; -import { CreateCustomerDto } from '../dtos/create-customer'; -import { UpdateCustomerDto } from '../dtos/update-customer'; +import { Customers } from '@prisma/client'; +import { CreateCustomerDto } from '../dtos/create-customer.dto'; +import { UpdateCustomerDto } from '../dtos/update-customer.dto'; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { Roles as RoleEnum } from '.prisma/client'; diff --git a/src/modules/customers/dtos/create-customer.ts b/src/modules/customers/dtos/create-customer.dto.ts similarity index 96% rename from src/modules/customers/dtos/create-customer.ts rename to src/modules/customers/dtos/create-customer.dto.ts index 4ae03fe..f2d3fa9 100644 --- a/src/modules/customers/dtos/create-customer.ts +++ b/src/modules/customers/dtos/create-customer.dto.ts @@ -20,7 +20,7 @@ export class CreateCustomerDto { @IsEmail() @IsOptional() - email?: string; + email: string; @Type(() => Number) @IsInt() diff --git a/src/modules/customers/dtos/update-customer.ts b/src/modules/customers/dtos/update-customer.dto.ts similarity index 67% rename from src/modules/customers/dtos/update-customer.ts rename to src/modules/customers/dtos/update-customer.dto.ts index 0499fe2..fc1ba39 100644 --- a/src/modules/customers/dtos/update-customer.ts +++ b/src/modules/customers/dtos/update-customer.dto.ts @@ -1,4 +1,4 @@ import { PartialType } from "@nestjs/swagger"; -import { CreateCustomerDto } from "./create-customer"; +import { CreateCustomerDto } from "./create-customer.dto"; export class UpdateCustomerDto extends PartialType(CreateCustomerDto) {} \ No newline at end of file diff --git a/src/modules/customers/services/customers.service.ts b/src/modules/customers/services/customers.service.ts index f67f4bd..4f11bce 100644 --- a/src/modules/customers/services/customers.service.ts +++ b/src/modules/customers/services/customers.service.ts @@ -1,8 +1,8 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { PrismaService } from 'src/prisma/prisma.service'; -import { CreateCustomerDto } from '../dtos/create-customer'; +import { CreateCustomerDto } from '../dtos/create-customer.dto'; import { Customers, Users } from '@prisma/client'; -import { UpdateCustomerDto } from '../dtos/update-customer'; +import { UpdateCustomerDto } from '../dtos/update-customer.dto'; @Injectable() export class CustomersService { From bd954b5c8766b79ac2215f1f313055b95e06bb18 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 22 Jul 2025 14:33:21 -0400 Subject: [PATCH 3/4] feat(swagger): swagger tags and docs generation --- .gitignore | 2 + docs/swagger/swagger-spec.json | 2285 +++++++++++++++++ src/app.module.ts | 2 + src/main.ts | 37 +- .../controllers/customers.controller.ts | 23 +- .../customers/dtos/create-customer.dto.ts | 28 + .../dtos/swagger-entities/customers.entity.ts | 42 + .../controllers/employees.controller.ts | 37 +- .../employees/dtos/create-employee.dto.ts | 40 + .../dtos/swagger-entities/employees.entity.ts | 40 + .../controllers/leave-requests.controller.ts | 28 +- .../dtos/create-leave-requests.dto.ts | 25 + .../swagger-entities/leave-requests.entity.ts | 49 + .../oauth-access-tokens.controller.ts | 23 +- .../dtos/create-oauth-access-token.dto.ts | 31 + .../oauth-access-token.entity.ts | 72 + .../controllers/shift-codes.controller.ts | 25 +- .../dtos/create-shift-codes.dto.ts | 10 + .../swagger-entities/shift-codes.entity.ts | 21 + .../shifts/controllers/shifts.controller.ts | 29 +- src/modules/shifts/dtos/create-shifts.dto.ts | 23 +- .../dtos/swagger-entities/shift.entity.ts | 39 + .../controllers/timesheets.controller.ts | 23 +- .../timesheets/dtos/create-timesheet.dto.ts | 9 + .../dtos/swagger-entities/timesheet.entity.ts | 21 + .../swagger-entities/user.entity.ts | 48 + 26 files changed, 2979 insertions(+), 33 deletions(-) create mode 100644 docs/swagger/swagger-spec.json create mode 100644 src/modules/customers/dtos/swagger-entities/customers.entity.ts create mode 100644 src/modules/employees/dtos/swagger-entities/employees.entity.ts create mode 100644 src/modules/leave-requests/dtos/swagger-entities/leave-requests.entity.ts create mode 100644 src/modules/oauth-access-tokens/dtos/swagger-entities/oauth-access-token.entity.ts create mode 100644 src/modules/shift-codes/dtos/swagger-entities/shift-codes.entity.ts create mode 100644 src/modules/shifts/dtos/swagger-entities/shift.entity.ts create mode 100644 src/modules/timesheets/dtos/swagger-entities/timesheet.entity.ts create mode 100644 src/modules/users-management/swagger-entities/user.entity.ts diff --git a/.gitignore b/.gitignore index fd17afc..dfe855f 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,5 @@ pids report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json /generated/prisma + +!swagger-spec.json diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json new file mode 100644 index 0000000..f586066 --- /dev/null +++ b/docs/swagger/swagger-spec.json @@ -0,0 +1,2285 @@ +{ + "openapi": "3.0.0", + "paths": { + "/": { + "get": { + "operationId": "AppController_getHello", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "App" + ] + } + }, + "/health": { + "get": { + "operationId": "HealthController_check", + "parameters": [], + "responses": { + "200": { + "description": "" + } + }, + "tags": [ + "Health" + ] + } + }, + "/oauth-access-tokens": { + "post": { + "operationId": "OauthAccessTokensController_create", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateOauthAccessTokenDto" + } + } + } + }, + "responses": { + "201": { + "description": "OAuth access token created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OAuthAccessTokenEntity" + } + } + } + }, + "400": { + "description": "Incomplete task or invalid data" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Create OAuth access token", + "tags": [ + "OAuth Access Tokens" + ] + }, + "get": { + "operationId": "OauthAccessTokensController_findAll", + "parameters": [], + "responses": { + "201": { + "description": "List of OAuth access token found", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OAuthAccessTokenEntity" + } + } + } + } + }, + "400": { + "description": "List of OAuth access token not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find all OAuth access token", + "tags": [ + "OAuth Access Tokens" + ] + } + }, + "/oauth-access-tokens/{id}": { + "get": { + "operationId": "OauthAccessTokensController_findOne", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "OAuth access token found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OAuthAccessTokenEntity" + } + } + } + }, + "400": { + "description": "OAuth access token not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find OAuth access token", + "tags": [ + "OAuth Access Tokens" + ] + }, + "patch": { + "operationId": "OauthAccessTokensController_update", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateOauthAccessTokenDto" + } + } + } + }, + "responses": { + "201": { + "description": "OAuth access token updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OAuthAccessTokenEntity" + } + } + } + }, + "400": { + "description": "OAuth access token not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Update OAuth access token", + "tags": [ + "OAuth Access Tokens" + ] + }, + "delete": { + "operationId": "OauthAccessTokensController_remove", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "OAuth access token deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OAuthAccessTokenEntity" + } + } + } + }, + "400": { + "description": "OAuth access token not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Delete OAuth access token", + "tags": [ + "OAuth Access Tokens" + ] + } + }, + "/customers": { + "post": { + "operationId": "CustomersController_create", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateCustomerDto" + } + } + } + }, + "responses": { + "201": { + "description": "Customer created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerEntity" + } + } + } + }, + "400": { + "description": "Invalid task or invalid data" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Create customer", + "tags": [ + "Customers" + ] + }, + "get": { + "operationId": "CustomersController_findAll", + "parameters": [], + "responses": { + "201": { + "description": "List of customers found", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CustomerEntity" + } + } + } + } + }, + "400": { + "description": "List of customers not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find all customers", + "tags": [ + "Customers" + ] + } + }, + "/customers/{id}": { + "get": { + "operationId": "CustomersController_findOne", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Customer found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerEntity" + } + } + } + }, + "400": { + "description": "Customer not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find customer", + "tags": [ + "Customers" + ] + }, + "patch": { + "operationId": "CustomersController_update", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateCustomerDto" + } + } + } + }, + "responses": { + "201": { + "description": "Customer updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerEntity" + } + } + } + }, + "400": { + "description": "Customer not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Update customer", + "tags": [ + "Customers" + ] + }, + "delete": { + "operationId": "CustomersController_remove", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Customer deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CustomerEntity" + } + } + } + }, + "400": { + "description": "Customer not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Delete customer", + "tags": [ + "Customers" + ] + } + }, + "/employees": { + "post": { + "operationId": "EmployeesController_create", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateEmployeeDto" + } + } + } + }, + "responses": { + "201": { + "description": "Employee created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmployeeEntity" + } + } + } + }, + "400": { + "description": "Incomplete task or invalid data" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Create employee", + "tags": [ + "Employees" + ] + }, + "get": { + "operationId": "EmployeesController_findAll", + "parameters": [], + "responses": { + "201": { + "description": "List of employees found", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmployeeEntity" + } + } + } + } + }, + "400": { + "description": "List of employees not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find all employees", + "tags": [ + "Employees" + ] + } + }, + "/employees/{id}": { + "get": { + "operationId": "EmployeesController_findOne", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Employee found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmployeeEntity" + } + } + } + }, + "400": { + "description": "Employee not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find employee", + "tags": [ + "Employees" + ] + }, + "patch": { + "operationId": "EmployeesController_update", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateEmployeeDto" + } + } + } + }, + "responses": { + "201": { + "description": "Employee updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmployeeEntity" + } + } + } + }, + "400": { + "description": "Employee not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Update employee", + "tags": [ + "Employees" + ] + }, + "delete": { + "operationId": "EmployeesController_remove", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Employee deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmployeeEntity" + } + } + } + }, + "400": { + "description": "Employee not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Delete employee", + "tags": [ + "Employees" + ] + } + }, + "/leave-requests": { + "post": { + "operationId": "LeaveRequestController_create", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateLeaveRequestsDto" + } + } + } + }, + "responses": { + "201": { + "description": "Leave request created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LeaveRequestEntity" + } + } + } + }, + "400": { + "description": "Incomplete task or invalid data" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Create leave request", + "tags": [ + "Leave Requests" + ] + }, + "get": { + "operationId": "LeaveRequestController_findAll", + "parameters": [], + "responses": { + "201": { + "description": "List of Leave requests found", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LeaveRequestEntity" + } + } + } + } + }, + "400": { + "description": "Leave request not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find all leave request", + "tags": [ + "Leave Requests" + ] + } + }, + "/leave-requests/{id}": { + "get": { + "operationId": "LeaveRequestController_findOne", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Leave request found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LeaveRequestEntity" + } + } + } + }, + "400": { + "description": "Leave request not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find leave request", + "tags": [ + "Leave Requests" + ] + }, + "patch": { + "operationId": "LeaveRequestController_update", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateLeaveRequestsDto" + } + } + } + }, + "responses": { + "201": { + "description": "Leave request updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LeaveRequestEntity" + } + } + } + }, + "400": { + "description": "Leave request not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Update leave request", + "tags": [ + "Leave Requests" + ] + }, + "delete": { + "operationId": "LeaveRequestController_remove", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Leave request deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LeaveRequestEntity" + } + } + } + }, + "400": { + "description": "Leave request not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Delete leave request", + "tags": [ + "Leave Requests" + ] + } + }, + "/shift-codes": { + "post": { + "operationId": "ShiftCodesController_create", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateShiftCodesDto" + } + } + } + }, + "responses": { + "201": { + "description": "Shift code created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShiftCodesEntity" + } + } + } + }, + "400": { + "description": "Incomplete task or invalid data" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Create shift code", + "tags": [ + "Shift Codes" + ] + }, + "get": { + "operationId": "ShiftCodesController_findAll", + "parameters": [], + "responses": { + "201": { + "description": "List of shift codes found", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ShiftCodesEntity" + } + } + } + } + }, + "400": { + "description": "List of shift codes not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find all shift codes", + "tags": [ + "Shift Codes" + ] + } + }, + "/shift-codes/{id}": { + "get": { + "operationId": "ShiftCodesController_findOne", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Shift code found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShiftCodesEntity" + } + } + } + }, + "400": { + "description": "Shift code not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find shift code", + "tags": [ + "Shift Codes" + ] + }, + "patch": { + "operationId": "ShiftCodesController_update", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateShiftCodesDto" + } + } + } + }, + "responses": { + "201": { + "description": "Shift code updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShiftCodesEntity" + } + } + } + }, + "400": { + "description": "Shift code not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Update shift code", + "tags": [ + "Shift Codes" + ] + }, + "delete": { + "operationId": "ShiftCodesController_remove", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Shift code deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShiftCodesEntity" + } + } + } + }, + "400": { + "description": "Shift code not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Delete shift code", + "tags": [ + "Shift Codes" + ] + } + }, + "/shifts": { + "post": { + "operationId": "ShiftsController_create", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateShiftDto" + } + } + } + }, + "responses": { + "201": { + "description": "Shift created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShiftEntity" + } + } + } + }, + "400": { + "description": "Incomplete task or invalid data" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Create shift", + "tags": [ + "Shifts" + ] + }, + "get": { + "operationId": "ShiftsController_findAll", + "parameters": [], + "responses": { + "201": { + "description": "List of shifts found", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ShiftEntity" + } + } + } + } + }, + "400": { + "description": "List of shifts not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find all shifts", + "tags": [ + "Shifts" + ] + } + }, + "/shifts/{id}": { + "get": { + "operationId": "ShiftsController_findOne", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Shift found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShiftEntity" + } + } + } + }, + "400": { + "description": "Shift not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find shift", + "tags": [ + "Shifts" + ] + }, + "patch": { + "operationId": "ShiftsController_update", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateShiftsDto" + } + } + } + }, + "responses": { + "201": { + "description": "Shift updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShiftEntity" + } + } + } + }, + "400": { + "description": "Shift not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Update shift", + "tags": [ + "Shifts" + ] + }, + "delete": { + "operationId": "ShiftsController_remove", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Shift deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ShiftEntity" + } + } + } + }, + "400": { + "description": "Shift not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Delete shift", + "tags": [ + "Shifts" + ] + } + }, + "/timesheets": { + "post": { + "operationId": "TimesheetsController_create", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateTimesheetDto" + } + } + } + }, + "responses": { + "201": { + "description": "Timesheet created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TimesheetEntity" + } + } + } + }, + "400": { + "description": "Incomplete task or invalid data" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Create timesheet", + "tags": [ + "Timesheets" + ] + }, + "get": { + "operationId": "TimesheetsController_findAll", + "parameters": [], + "responses": { + "201": { + "description": "List of timesheet found", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimesheetEntity" + } + } + } + } + }, + "400": { + "description": "List of timesheets not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find all timesheets", + "tags": [ + "Timesheets" + ] + } + }, + "/timesheets/{id}": { + "get": { + "operationId": "TimesheetsController_findOne", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Timesheet found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TimesheetEntity" + } + } + } + }, + "400": { + "description": "Timesheet not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Find timesheet", + "tags": [ + "Timesheets" + ] + }, + "patch": { + "operationId": "TimesheetsController_update", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTimesheetDto" + } + } + } + }, + "responses": { + "201": { + "description": "Timesheet updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TimesheetEntity" + } + } + } + }, + "400": { + "description": "Timesheet not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Update timesheet", + "tags": [ + "Timesheets" + ] + }, + "delete": { + "operationId": "TimesheetsController_remove", + "parameters": [ + { + "name": "id", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + } + ], + "responses": { + "201": { + "description": "Timesheet deleted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TimesheetEntity" + } + } + } + }, + "400": { + "description": "Timesheet not found" + } + }, + "security": [ + { + "access-token": [] + } + ], + "summary": "Delete timesheet", + "tags": [ + "Timesheets" + ] + } + } + }, + "info": { + "title": "Targo_Backend", + "description": "Documentation de l`API REST pour Targo (NestJS + Prisma)", + "version": "1.0", + "contact": {} + }, + "tags": [ + { + "name": "Users", + "description": "" + }, + { + "name": "Employees", + "description": "" + }, + { + "name": "Customers", + "description": "" + }, + { + "name": "Timesheets", + "description": "" + }, + { + "name": "Shifts", + "description": "" + }, + { + "name": "Leave Requests", + "description": "" + }, + { + "name": "Shift Codes", + "description": "" + }, + { + "name": "OAuth Access Tokens", + "description": "" + }, + { + "name": "Authorization", + "description": "" + } + ], + "servers": [], + "components": { + "securitySchemes": { + "access-token": { + "scheme": "bearer", + "bearerFormat": "JWT", + "type": "http", + "name": "Authorization", + "description": "Invalid JWT token", + "in": "header" + } + }, + "schemas": { + "CreateOauthAccessTokenDto": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "example": "S7A2U8R7O6N6", + "description": "User`s unique identification number" + }, + "application": { + "type": "string", + "example": "app.targo.ca", + "description": "URL in which the access token is used for" + }, + "access_token": { + "type": "string", + "example": "L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7 ...", + "description": "Access token" + }, + "refresh_token": { + "type": "string", + "example": "Th3731102h1p07Th3R1n92", + "description": "Refresh token" + }, + "access_token_expiry": { + "format": "date-time", + "type": "string", + "example": "25/12/3018", + "description": "Access token`s expiry date" + }, + "refresh_token_expiry": { + "format": "date-time", + "type": "string", + "example": "26/02/3019", + "description": "Refresh token`s expiry date" + }, + "scopes": { + "example": "access tolkiens, email, etc... ", + "description": "scopes of infos needed for access", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "user_id", + "application", + "access_token", + "refresh_token", + "access_token_expiry" + ] + }, + "OAuthAccessTokenEntity": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "cklwi0vb70000z2z20q6f19qk", + "description": "Identifiant unique du token OAuth (généré automatiquement)" + }, + "user_id": { + "type": "string", + "example": "0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d", + "description": "UUID de l’utilisateur auquel ce token appartient" + }, + "application": { + "type": "string", + "example": "app.targo.ca", + "description": "Nom de l’application ou domaine utilisant ce token" + }, + "access_token": { + "type": "string", + "example": "L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7", + "description": "Token d’accès OAuth" + }, + "refresh_token": { + "type": "string", + "example": "Th3731102h1p07Th3R1n92", + "description": "Token de rafraîchissement OAuth" + }, + "access_token_expiry": { + "format": "date-time", + "type": "string", + "example": "3018-12-25T00:00:00.000Z", + "description": "Date d’expiration du token d’accès" + }, + "refresh_token_expiry": { + "format": "date-time", + "type": "string", + "example": "3019-02-26T00:00:00.000Z", + "description": "Date d’expiration du token de rafraîchissement (optionnelle)" + }, + "scopes": { + "example": [ + "email", + "profile", + "access_tolkiens" + ], + "description": "Liste des autorisations/scopes liés à ce token", + "type": "array", + "items": { + "type": "string" + } + }, + "is_revoked": { + "type": "boolean", + "example": false, + "description": "Indique si le token a été révoqué" + }, + "created_at": { + "format": "date-time", + "type": "string", + "example": "2025-07-22T08:44:00.000Z", + "description": "Date de création du token" + }, + "updated_at": { + "format": "date-time", + "type": "string", + "example": "2025-07-23T08:44:00.000Z", + "description": "Date de dernière mise à jour (optionnelle)" + } + }, + "required": [ + "id", + "user_id", + "application", + "access_token", + "refresh_token", + "access_token_expiry", + "is_revoked", + "created_at" + ] + }, + "UpdateOauthAccessTokenDto": { + "type": "object", + "properties": { + "user_id": { + "type": "string", + "example": "S7A2U8R7O6N6", + "description": "User`s unique identification number" + }, + "application": { + "type": "string", + "example": "app.targo.ca", + "description": "URL in which the access token is used for" + }, + "access_token": { + "type": "string", + "example": "L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7 ...", + "description": "Access token" + }, + "refresh_token": { + "type": "string", + "example": "Th3731102h1p07Th3R1n92", + "description": "Refresh token" + }, + "access_token_expiry": { + "format": "date-time", + "type": "string", + "example": "25/12/3018", + "description": "Access token`s expiry date" + }, + "refresh_token_expiry": { + "format": "date-time", + "type": "string", + "example": "26/02/3019", + "description": "Refresh token`s expiry date" + }, + "scopes": { + "example": "access tolkiens, email, etc... ", + "description": "scopes of infos needed for access", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "CreateCustomerDto": { + "type": "object", + "properties": { + "first_name": { + "type": "string", + "example": "Gandalf", + "description": "Customer`s first name" + }, + "last_name": { + "type": "string", + "example": "TheGray", + "description": "Customer`s last name" + }, + "email": { + "type": "string", + "example": "you_shall_not_pass@middleEarth.com", + "description": "Customer`s email" + }, + "phone_number": { + "type": "number", + "example": "8436637464", + "description": "Customer`s phone number" + }, + "residence": { + "type": "string", + "example": "1 Ringbearer`s way, Mount Doom city, ME, T1R 1N6 ", + "description": "Customer`s residence" + }, + "invoice_id": { + "type": "number", + "example": "4263253", + "description": "Customer`s invoice number" + } + }, + "required": [ + "first_name", + "last_name", + "email", + "phone_number" + ] + }, + "CustomerEntity": { + "type": "object", + "properties": { + "id": { + "type": "number", + "example": 1, + "description": "Identifiant unique du client (clé primaire auto-incrémentée)" + }, + "user_id": { + "type": "string", + "example": "0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d", + "description": "UUID de l’utilisateur lié à ce client" + }, + "email": { + "type": "string", + "example": "you_shall_not_pass@middleEarth.com", + "description": "Adresse email du client (optionnelle)" + }, + "phone_number": { + "type": "number", + "example": 8436637464, + "description": "Numéro de téléphone du client (chiffres seulement)" + }, + "residence": { + "type": "string", + "example": "1 Ringbearer’s way, Mount Doom city, ME, T1R 1N6", + "description": "Adresse de résidence du client (optionnelle)" + }, + "invoice_id": { + "type": "number", + "example": 4263253, + "description": "Numéro de facture lié à ce client (optionnel mais unique)" + } + }, + "required": [ + "id", + "user_id", + "phone_number" + ] + }, + "UpdateCustomerDto": { + "type": "object", + "properties": { + "first_name": { + "type": "string", + "example": "Gandalf", + "description": "Customer`s first name" + }, + "last_name": { + "type": "string", + "example": "TheGray", + "description": "Customer`s last name" + }, + "email": { + "type": "string", + "example": "you_shall_not_pass@middleEarth.com", + "description": "Customer`s email" + }, + "phone_number": { + "type": "number", + "example": "8436637464", + "description": "Customer`s phone number" + }, + "residence": { + "type": "string", + "example": "1 Ringbearer`s way, Mount Doom city, ME, T1R 1N6 ", + "description": "Customer`s residence" + }, + "invoice_id": { + "type": "number", + "example": "4263253", + "description": "Customer`s invoice number" + } + } + }, + "CreateEmployeeDto": { + "type": "object", + "properties": { + "first_name": { + "type": "string", + "example": "Frodo", + "description": "Employee`s first name" + }, + "last_name": { + "type": "string", + "example": "Baggins", + "description": "Employee`s last name" + }, + "email": { + "type": "string", + "example": "i_cant_do_this_sam@targointernet.com", + "description": "Employee`s email" + }, + "phone_number": { + "type": "number", + "example": "82538437464", + "description": "Employee`s phone number" + }, + "residence": { + "type": "string", + "example": "1 Bagshot Row, Hobbiton, The Shire, Middle-earth", + "description": "Employee`s residence" + }, + "external_payroll_id": { + "type": "number", + "example": "BagginsF7464", + "description": "Employee`s payroll id" + }, + "company_code": { + "type": "number", + "example": "335567447", + "description": "Employee`s company code" + }, + "first_work_day": { + "format": "date-time", + "type": "string", + "example": "23/09/3018", + "description": "Employee`s first working day" + }, + "last_work_day": { + "format": "date-time", + "type": "string", + "example": "25/03/3019", + "description": "Employee`s last working day" + } + }, + "required": [ + "first_name", + "last_name", + "email", + "phone_number", + "external_payroll_id", + "company_code", + "first_work_day" + ] + }, + "EmployeeEntity": { + "type": "object", + "properties": { + "id": { + "type": "number", + "example": 1, + "description": "Identifiant unique de l’employé (clé primaire auto-incrémentée)" + }, + "user_id": { + "type": "string", + "example": "0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d", + "description": "UUID de l’utilisateur lié à cet employé" + }, + "external_payroll_id": { + "type": "number", + "example": 7464, + "description": "Identifiant externe dans le système de paie" + }, + "company_code": { + "type": "number", + "example": 335567447, + "description": "Code de la compagnie" + }, + "first_work_day": { + "format": "date-time", + "type": "string", + "example": "3018-09-23T00:00:00.000Z", + "description": "Date de début d’emploi" + }, + "last_work_day": { + "format": "date-time", + "type": "string", + "example": "3019-03-25T00:00:00.000Z", + "description": "Date de fin d’emploi (optionnelle)" + } + }, + "required": [ + "id", + "user_id", + "external_payroll_id", + "company_code", + "first_work_day" + ] + }, + "UpdateEmployeeDto": { + "type": "object", + "properties": { + "first_name": { + "type": "string", + "example": "Frodo", + "description": "Employee`s first name" + }, + "last_name": { + "type": "string", + "example": "Baggins", + "description": "Employee`s last name" + }, + "email": { + "type": "string", + "example": "i_cant_do_this_sam@targointernet.com", + "description": "Employee`s email" + }, + "phone_number": { + "type": "number", + "example": "82538437464", + "description": "Employee`s phone number" + }, + "residence": { + "type": "string", + "example": "1 Bagshot Row, Hobbiton, The Shire, Middle-earth", + "description": "Employee`s residence" + }, + "external_payroll_id": { + "type": "number", + "example": "BagginsF7464", + "description": "Employee`s payroll id" + }, + "company_code": { + "type": "number", + "example": "335567447", + "description": "Employee`s company code" + }, + "first_work_day": { + "format": "date-time", + "type": "string", + "example": "23/09/3018", + "description": "Employee`s first working day" + }, + "last_work_day": { + "format": "date-time", + "type": "string", + "example": "25/03/3019", + "description": "Employee`s last working day" + } + } + }, + "CreateLeaveRequestsDto": { + "type": "object", + "properties": { + "employee_id": { + "type": "number", + "example": "4655867", + "description": "Employee`s id" + }, + "leave_type": { + "type": "string", + "example": "Sick or Vacation or Unpaid or Bereavement or Parental or Legal", + "description": "type of leave request for an accounting perception" + }, + "start_date_time": { + "format": "date-time", + "type": "string", + "example": "22/06/2463", + "description": "Leave request`s start date" + }, + "end_date_time": { + "format": "date-time", + "type": "string", + "example": "25/03/3019", + "description": "Leave request`s end date" + }, + "comment": { + "type": "string", + "example": "My precious", + "description": "Leave request`s comment" + }, + "approval_status": { + "type": "string", + "example": "True or False or Pending or Denied or Cancelled or Escalated", + "description": "Leave request`s approval status" + } + }, + "required": [ + "employee_id", + "leave_type", + "start_date_time", + "end_date_time", + "comment", + "approval_status" + ] + }, + "LeaveRequestEntity": { + "type": "object", + "properties": { + "id": { + "type": "number", + "example": 1, + "description": "Identifiant unique de la requête de congé (auto-incrémenté)" + }, + "employee_id": { + "type": "number", + "example": 42, + "description": "Identifiant de l’employé concerné" + }, + "leave_type": { + "type": "string", + "example": "SICK", + "enum": [ + "SICK", + "VACATION", + "UNPAID", + "BEREAVEMENT", + "PARENTAL", + "LEGAL" + ], + "description": "Type de congé demandé" + }, + "start_date_time": { + "format": "date-time", + "type": "string", + "example": "2463-06-22T00:00:00.000Z", + "description": "Date de début du congé" + }, + "end_date_time": { + "format": "date-time", + "type": "string", + "example": "3019-03-25T00:00:00.000Z", + "description": "Date de fin du congé (facultative)" + }, + "comment": { + "type": "string", + "example": "My precious", + "description": "Commentaire de l’employé lié à la demande" + }, + "approval_status": { + "type": "string", + "example": "PENDING", + "enum": [ + "PENDING", + "APPROVED", + "DENIED", + "CANCELLED", + "ESCALATED" + ], + "description": "Statut de validation de la demande" + } + }, + "required": [ + "id", + "employee_id", + "leave_type", + "start_date_time", + "comment", + "approval_status" + ] + }, + "UpdateLeaveRequestsDto": { + "type": "object", + "properties": { + "employee_id": { + "type": "number", + "example": "4655867", + "description": "Employee`s id" + }, + "leave_type": { + "type": "string", + "example": "Sick or Vacation or Unpaid or Bereavement or Parental or Legal", + "description": "type of leave request for an accounting perception" + }, + "start_date_time": { + "format": "date-time", + "type": "string", + "example": "22/06/2463", + "description": "Leave request`s start date" + }, + "end_date_time": { + "format": "date-time", + "type": "string", + "example": "25/03/3019", + "description": "Leave request`s end date" + }, + "comment": { + "type": "string", + "example": "My precious", + "description": "Leave request`s comment" + }, + "approval_status": { + "type": "string", + "example": "True or False or Pending or Denied or Cancelled or Escalated", + "description": "Leave request`s approval status" + } + } + }, + "CreateShiftCodesDto": { + "type": "object", + "properties": { + "shift_type": { + "type": "string", + "example": "Regular or Night or Emergency, etc...", + "description": "Type of shifts for an account perception" + }, + "bank_code": { + "type": "string", + "example": "G1, G2, G3, etc...", + "description": "bank`s code related to the type of shift" + } + }, + "required": [ + "shift_type", + "bank_code" + ] + }, + "ShiftCodesEntity": { + "type": "object", + "properties": { + "id": { + "type": "number", + "example": 1, + "description": "Identifiant unique du code de quart (généré automatiquement)" + }, + "shift_type": { + "type": "string", + "example": "Night", + "description": "Type de quart de travail pour perception comptable ou RH" + }, + "bank_code": { + "type": "string", + "example": "G2", + "description": "Code bancaire ou budgétaire lié au quart de travail" + } + }, + "required": [ + "id", + "shift_type", + "bank_code" + ] + }, + "UpdateShiftCodesDto": { + "type": "object", + "properties": { + "shift_type": { + "type": "string", + "example": "Regular or Night or Emergency, etc...", + "description": "Type of shifts for an account perception" + }, + "bank_code": { + "type": "string", + "example": "G1, G2, G3, etc...", + "description": "bank`s code related to the type of shift" + } + } + }, + "CreateShiftDto": { + "type": "object", + "properties": { + "timesheet_id": { + "type": "number", + "example": "Th3F3110w5h1pX2024", + "description": "identification number for a set timesheet" + }, + "shift_code_id": { + "type": "number", + "example": "0n3R1n962Ru13xX", + "description": "identification number for a shift code" + }, + "date": { + "format": "date-time", + "type": "string", + "example": "20/10/3018", + "description": "Date where the shift takes place" + }, + "start_time": { + "format": "date-time", + "type": "string", + "example": "08:00", + "description": "Start time of the said shift" + }, + "end_time": { + "format": "date-time", + "type": "string", + "example": "17:00", + "description": "End time of the said shift" + } + }, + "required": [ + "timesheet_id", + "shift_code_id", + "date", + "start_time", + "end_time" + ] + }, + "ShiftEntity": { + "type": "object", + "properties": { + "id": { + "type": "number", + "example": 1, + "description": "Identifiant unique du quart de travail (généré automatiquement)" + }, + "timesheet_id": { + "type": "number", + "example": 101, + "description": "Identifiant de la feuille de temps à laquelle le quart est rattaché" + }, + "shift_code_id": { + "type": "number", + "example": 7, + "description": "Identifiant du code de quart utilisé (lien avec ShiftCodes)" + }, + "date": { + "format": "date-time", + "type": "string", + "example": "3018-10-20T00:00:00.000Z", + "description": "Date à laquelle le quart a lieu" + }, + "start_time": { + "format": "date-time", + "type": "string", + "example": "3018-10-20T08:00:00.000Z", + "description": "Heure de début du quart" + }, + "end_time": { + "format": "date-time", + "type": "string", + "example": "3018-10-20T17:00:00.000Z", + "description": "Heure de fin du quart" + } + }, + "required": [ + "id", + "timesheet_id", + "shift_code_id", + "date", + "start_time", + "end_time" + ] + }, + "UpdateShiftsDto": { + "type": "object", + "properties": { + "timesheet_id": { + "type": "number", + "example": "Th3F3110w5h1pX2024", + "description": "identification number for a set timesheet" + }, + "shift_code_id": { + "type": "number", + "example": "0n3R1n962Ru13xX", + "description": "identification number for a shift code" + }, + "date": { + "format": "date-time", + "type": "string", + "example": "20/10/3018", + "description": "Date where the shift takes place" + }, + "start_time": { + "format": "date-time", + "type": "string", + "example": "08:00", + "description": "Start time of the said shift" + }, + "end_time": { + "format": "date-time", + "type": "string", + "example": "17:00", + "description": "End time of the said shift" + } + } + }, + "CreateTimesheetDto": { + "type": "object", + "properties": { + "employee_id": { + "type": "number", + "example": "426433", + "description": "identification number of the employee" + }, + "is_approved": { + "type": "boolean", + "example": "True or False or Pending or Denied or Cancelled or Escalated", + "description": "Timesheet`s approval status" + } + }, + "required": [ + "employee_id", + "is_approved" + ] + }, + "TimesheetEntity": { + "type": "object", + "properties": { + "id": { + "type": "number", + "example": 1, + "description": "Identifiant unique de la feuille de temps (généré automatiquement)" + }, + "employee_id": { + "type": "number", + "example": 426433, + "description": "Identifiant de l’employé associé à cette feuille de temps" + }, + "is_approved": { + "type": "boolean", + "example": true, + "description": "Statut d’approbation de la feuille de temps" + } + }, + "required": [ + "id", + "employee_id", + "is_approved" + ] + }, + "UpdateTimesheetDto": { + "type": "object", + "properties": { + "employee_id": { + "type": "number", + "example": "426433", + "description": "identification number of the employee" + }, + "is_approved": { + "type": "boolean", + "example": "True or False or Pending or Denied or Cancelled or Escalated", + "description": "Timesheet`s approval status" + } + } + } + } + } +} \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index 18edf62..ec0027b 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -12,6 +12,7 @@ import { LeaveRequestsModule } from './modules/leave-requests/leave-requests.mod import { ShiftCodeModule } from './modules/shift-codes/shift-codes.module'; import { ShiftsModule } from './modules/shifts/shifts.module'; import { TimesheetsModule } from './modules/timesheets/timesheets.module'; +import { AuthenticationModule } from './modules/authentication/auth.module'; @Module({ imports: [ @@ -25,6 +26,7 @@ import { TimesheetsModule } from './modules/timesheets/timesheets.module'; ShiftCodeModule, ShiftsModule, TimesheetsModule, + AuthenticationModule, ], controllers: [AppController, HealthController], providers: [AppService], diff --git a/src/main.ts b/src/main.ts index f11766c..929668a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,11 +5,13 @@ import { ValidationPipe } from '@nestjs/common'; import { JwtAuthGuard } from './modules/authentication/guards/jwt-auth.guard'; import { RolesGuard } from './common/guards/roles.guard'; import { OwnershipGuard } from './common/guards/ownership.guard'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; +import { writeFileSync } from 'fs'; async function bootstrap() { const app = await NestFactory.create(AppModule); - //setup Reflector for Roles() - const reflector = app.get(Reflector); + + const reflector = app.get(Reflector); //setup Reflector for Roles() app.useGlobalPipes( new ValidationPipe({ whitelist: true, transform: true})); @@ -19,6 +21,37 @@ async function bootstrap() { new OwnershipGuard(reflector, app.get(ModuleRef)), //Global use of OwnershipGuard, not implemented yet ); + //swagger config + const config = new DocumentBuilder() + .setTitle('Targo_Backend') + .setDescription('Documentation de l`API REST pour Targo (NestJS + Prisma)') + .setVersion('1.0') + .addBearerAuth({ + type: 'http', + scheme: 'bearer', + bearerFormat: 'JWT', + name: 'Authorization', + description: 'Invalid JWT token', + in: 'header', + }, 'access-token') + .addTag('Users') + .addTag('Employees') + .addTag('Customers') + .addTag('Timesheets') + .addTag('Shifts') + .addTag('Leave Requests') + .addTag('Shift Codes') + .addTag('OAuth Access Tokens') + .addTag('Authorization') + .build(); + + //document builder for swagger docs + const documentFactory = () => SwaggerModule.createDocument(app, config); + const document = documentFactory() + SwaggerModule.setup('api/docs', app, document); + + writeFileSync('./docs/swagger/swagger-spec.json', JSON.stringify(document, null, 2)); + await app.listen(process.env.PORT ?? 3000); } bootstrap(); diff --git a/src/modules/customers/controllers/customers.controller.ts b/src/modules/customers/controllers/customers.controller.ts index 7ac87dd..66f216a 100644 --- a/src/modules/customers/controllers/customers.controller.ts +++ b/src/modules/customers/controllers/customers.controller.ts @@ -1,35 +1,53 @@ -import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from '@nestjs/common'; import { CustomersService } from '../services/customers.service'; import { Customers } from '@prisma/client'; import { CreateCustomerDto } from '../dtos/create-customer.dto'; import { UpdateCustomerDto } from '../dtos/update-customer.dto'; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { Roles as RoleEnum } from '.prisma/client'; +import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { JwtAuthGuard } from 'src/modules/authentication/guards/jwt-auth.guard'; +import { CustomerEntity } from '../dtos/swagger-entities/customers.entity'; +@ApiTags('Customers') +@ApiBearerAuth('access-token') +@UseGuards(JwtAuthGuard) @Controller('customers') export class CustomersController { constructor(private readonly customersService: CustomersService) {} @Post() @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Create customer' }) + @ApiResponse({ status: 201, description: 'Customer created', type: CustomerEntity }) + @ApiResponse({ status: 400, description: 'Invalid task or invalid data' }) create(@Body() dto: CreateCustomerDto): Promise { return this.customersService.create(dto); } @Get() @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Find all customers' }) + @ApiResponse({ status: 201, description: 'List of customers found', type: CustomerEntity, isArray: true }) + @ApiResponse({ status: 400, description: 'List of customers not found' }) findAll(): Promise { return this.customersService.findAll(); } @Get(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Find customer' }) + @ApiResponse({ status: 201, description: 'Customer found', type: CustomerEntity }) + @ApiResponse({ status: 400, description: 'Customer not found' }) findOne(@Param('id', ParseIntPipe) id: number): Promise { return this.customersService.findOne(id); } @Patch(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE,RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Update customer' }) + @ApiResponse({ status: 201, description: 'Customer updated', type: CustomerEntity }) + @ApiResponse({ status: 400, description: 'Customer not found' }) update( @Param('id', ParseIntPipe) id: number, @Body() dto: UpdateCustomerDto, @@ -39,6 +57,9 @@ export class CustomersController { @Delete(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Delete customer' }) + @ApiResponse({ status: 201, description: 'Customer deleted', type: CustomerEntity }) + @ApiResponse({ status: 400, description: 'Customer not found' }) remove(@Param('id', ParseIntPipe) id: number): Promise{ return this.customersService.remove(id); } diff --git a/src/modules/customers/dtos/create-customer.dto.ts b/src/modules/customers/dtos/create-customer.dto.ts index f2d3fa9..5b6630b 100644 --- a/src/modules/customers/dtos/create-customer.dto.ts +++ b/src/modules/customers/dtos/create-customer.dto.ts @@ -1,3 +1,4 @@ +import { ApiProperty } from "@nestjs/swagger"; import { Type } from "class-transformer"; import { IsEmail, @@ -10,27 +11,54 @@ import { export class CreateCustomerDto { + @ApiProperty({ + example: 'Gandalf', + description: 'Customer`s first name', + }) + @IsString() @IsNotEmpty() first_name: string; + @ApiProperty({ + example: 'TheGray', + description: 'Customer`s last name', + }) @IsString() @IsNotEmpty() last_name: string; + @ApiProperty({ + example: 'you_shall_not_pass@middleEarth.com', + description: 'Customer`s email', + }) @IsEmail() @IsOptional() email: string; + @ApiProperty({ + example: '8436637464', + description: 'Customer`s phone number', + }) @Type(() => Number) @IsInt() @IsPositive() phone_number: number; + @ApiProperty({ + example: '1 Ringbearer`s way, Mount Doom city, ME, T1R 1N6 ', + description: 'Customer`s residence', + required: false, + }) @IsString() @IsOptional() residence?: string; + @ApiProperty({ + example: '4263253', + description: 'Customer`s invoice number', + required: false, + }) @IsInt() @IsNotEmpty() invoice_id: number; diff --git a/src/modules/customers/dtos/swagger-entities/customers.entity.ts b/src/modules/customers/dtos/swagger-entities/customers.entity.ts new file mode 100644 index 0000000..b1cdf36 --- /dev/null +++ b/src/modules/customers/dtos/swagger-entities/customers.entity.ts @@ -0,0 +1,42 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class CustomerEntity { + @ApiProperty({ + example: 1, + description: 'Identifiant unique du client (clé primaire auto-incrémentée)', + }) + id: number; + + @ApiProperty({ + example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', + description: 'UUID de l’utilisateur lié à ce client', + }) + user_id: string; + + @ApiProperty({ + example: 'you_shall_not_pass@middleEarth.com', + description: 'Adresse email du client (optionnelle)', + required: false, + }) + email?: string; + + @ApiProperty({ + example: 8436637464, + description: 'Numéro de téléphone du client (chiffres seulement)', + }) + phone_number: number; + + @ApiProperty({ + example: '1 Ringbearer’s way, Mount Doom city, ME, T1R 1N6', + description: 'Adresse de résidence du client (optionnelle)', + required: false, + }) + residence?: string; + + @ApiProperty({ + example: 4263253, + description: 'Numéro de facture lié à ce client (optionnel mais unique)', + required: false, + }) + invoice_id?: number; +} diff --git a/src/modules/employees/controllers/employees.controller.ts b/src/modules/employees/controllers/employees.controller.ts index 468285a..c736182 100644 --- a/src/modules/employees/controllers/employees.controller.ts +++ b/src/modules/employees/controllers/employees.controller.ts @@ -1,46 +1,52 @@ -import { - Body, - Controller, - Delete, - Get, - Param, - ParseIntPipe, - Patch, - Post, -} from '@nestjs/common'; -import { Employees } from '@prisma/client'; +import { Body,Controller,Delete,Get,Param,ParseIntPipe,Patch,Post,UseGuards } from '@nestjs/common'; +import { Employees, Roles as RoleEnum } from '@prisma/client'; import { EmployeesService } from '../services/employees.service'; import { CreateEmployeeDto } from '../dtos/create-employee.dto'; import { UpdateEmployeeDto } from '../dtos/update-employee.dto'; - -//decorators and roles imports import { RolesAllowed } from '../../../common/decorators/roles.decorators'; -import { Roles as RoleEnum } from 'prisma/prisma-client'; +import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { JwtAuthGuard } from 'src/modules/authentication/guards/jwt-auth.guard'; +import { EmployeeEntity } from '../dtos/swagger-entities/employees.entity'; +@ApiTags('Employees') +@ApiBearerAuth('access-token') +@UseGuards(JwtAuthGuard) @Controller('employees') export class EmployeesController { constructor(private readonly employeesService: EmployeesService) {} @Post() @RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({summary: 'Create employee' }) + @ApiResponse({ status: 201, description: 'Employee created', type: EmployeeEntity }) + @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) create(@Body() dto: CreateEmployeeDto): Promise { return this.employeesService.create(dto); } @Get() @RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ACCOUNTING) + @ApiOperation({summary: 'Find all employees' }) + @ApiResponse({ status: 201, description: 'List of employees found', type: EmployeeEntity, isArray: true }) + @ApiResponse({ status: 400, description: 'List of employees not found' }) findAll(): Promise { return this.employeesService.findAll(); } @Get(':id') @RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING ) + @ApiOperation({summary: 'Find employee' }) + @ApiResponse({ status: 201, description: 'Employee found', type: EmployeeEntity }) + @ApiResponse({ status: 400, description: 'Employee not found' }) findOne(@Param('id', ParseIntPipe) id: number): Promise { return this.employeesService.findOne(id); } @Patch(':id') @RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR,RoleEnum.ACCOUNTING ) + @ApiOperation({summary: 'Update employee' }) + @ApiResponse({ status: 201, description: 'Employee updated', type: EmployeeEntity }) + @ApiResponse({ status: 400, description: 'Employee not found' }) update( @Param('id', ParseIntPipe) id: number, @Body() dto: UpdateEmployeeDto, @@ -50,6 +56,9 @@ export class EmployeesController { @Delete(':id') @RolesAllowed(RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR ) + @ApiOperation({summary: 'Delete employee' }) + @ApiResponse({ status: 201, description: 'Employee deleted', type: EmployeeEntity }) + @ApiResponse({ status: 400, description: 'Employee not found' }) remove(@Param('id', ParseIntPipe) id: number): Promise { return this.employeesService.remove(id); } diff --git a/src/modules/employees/dtos/create-employee.dto.ts b/src/modules/employees/dtos/create-employee.dto.ts index 88bd9e3..a595a0c 100644 --- a/src/modules/employees/dtos/create-employee.dto.ts +++ b/src/modules/employees/dtos/create-employee.dto.ts @@ -9,44 +9,84 @@ import { IsString, } from 'class-validator'; import { Type } from 'class-transformer'; +import { ApiProperty } from '@nestjs/swagger'; export class CreateEmployeeDto { + + @ApiProperty({ + example: 'Frodo', + description: 'Employee`s first name', + }) @IsString() @IsNotEmpty() first_name: string; + @ApiProperty({ + example: 'Baggins', + description: 'Employee`s last name', + }) @IsString() @IsNotEmpty() last_name: string; + @ApiProperty({ + example: 'i_cant_do_this_sam@targointernet.com', + description: 'Employee`s email', + }) @IsEmail() @IsOptional() email: string; + @ApiProperty({ + example: '82538437464', + description: 'Employee`s phone number', + }) @Type(() => Number) @IsInt() @IsPositive() phone_number: number; + @ApiProperty({ + example: '1 Bagshot Row, Hobbiton, The Shire, Middle-earth', + description: 'Employee`s residence', + required: false, + }) @IsString() @IsOptional() residence?: string; + @ApiProperty({ + example: 'BagginsF7464', + description: 'Employee`s payroll id', + }) @IsInt() @IsPositive() @Type(() => Number) external_payroll_id: number; + @ApiProperty({ + example: '335567447', + description: 'Employee`s company code', + }) @IsInt() @IsPositive() @Type(() => Number) company_code: number; + @ApiProperty({ + example: '23/09/3018', + description: 'Employee`s first working day', + }) @IsDateString() @Type(() => Date) @IsDate() first_work_day: Date; + @ApiProperty({ + example: '25/03/3019', + description: 'Employee`s last working day', + required: false, + }) @IsDateString() @Type(() => Date) @IsDate() diff --git a/src/modules/employees/dtos/swagger-entities/employees.entity.ts b/src/modules/employees/dtos/swagger-entities/employees.entity.ts new file mode 100644 index 0000000..8828d25 --- /dev/null +++ b/src/modules/employees/dtos/swagger-entities/employees.entity.ts @@ -0,0 +1,40 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class EmployeeEntity { + @ApiProperty({ + example: 1, + description: 'Identifiant unique de l’employé (clé primaire auto-incrémentée)', + }) + id: number; + + @ApiProperty({ + example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', + description: 'UUID de l’utilisateur lié à cet employé', + }) + user_id: string; + + @ApiProperty({ + example: 7464, + description: 'Identifiant externe dans le système de paie', + }) + external_payroll_id: number; + + @ApiProperty({ + example: 335567447, + description: 'Code de la compagnie', + }) + company_code: number; + + @ApiProperty({ + example: '3018-09-23T00:00:00.000Z', + description: 'Date de début d’emploi', + }) + first_work_day: Date; + + @ApiProperty({ + example: '3019-03-25T00:00:00.000Z', + description: 'Date de fin d’emploi (optionnelle)', + required: false, + }) + last_work_day?: Date; +} diff --git a/src/modules/leave-requests/controllers/leave-requests.controller.ts b/src/modules/leave-requests/controllers/leave-requests.controller.ts index f992ee5..6c13ea3 100644 --- a/src/modules/leave-requests/controllers/leave-requests.controller.ts +++ b/src/modules/leave-requests/controllers/leave-requests.controller.ts @@ -1,44 +1,62 @@ -import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common"; import { LeaveRequestsService } from "../services/leave-request.service"; import { CreateLeaveRequestsDto } from "../dtos/create-leave-requests.dto"; import { LeaveRequests } from "@prisma/client"; import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { Roles as RoleEnum } from '.prisma/client'; +import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { JwtAuthGuard } from "src/modules/authentication/guards/jwt-auth.guard"; +import { LeaveRequestEntity } from "../dtos/swagger-entities/leave-requests.entity"; +@ApiTags('Leave Requests') +@ApiBearerAuth('access-token') +@UseGuards(JwtAuthGuard) @Controller('leave-requests') export class LeaveRequestController { constructor(private readonly leaveRequetsService: LeaveRequestsService){} @Post() @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({summary: 'Create leave request' }) + @ApiResponse({ status: 201, description: 'Leave request created',type: LeaveRequestEntity }) + @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) create(@Body() dto: CreateLeaveRequestsDto): Promise { return this. leaveRequetsService.create(dto); } @Get() @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({summary: 'Find all leave request' }) + @ApiResponse({ status: 201, description: 'List of Leave requests found',type: LeaveRequestEntity, isArray: true }) + @ApiResponse({ status: 400, description: 'Leave request not found' }) findAll(): Promise { return this.leaveRequetsService.findAll(); } @Get(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({summary: 'Find leave request' }) + @ApiResponse({ status: 201, description: 'Leave request found',type: LeaveRequestEntity }) + @ApiResponse({ status: 400, description: 'Leave request not found' }) findOne(@Param('id', ParseIntPipe) id: number): Promise { return this.leaveRequetsService.findOne(id); } @Patch(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) - update( - @Param('id', ParseIntPipe) id: number, - @Body() dto: UpdateLeaveRequestsDto, - ): Promise { + @ApiOperation({summary: 'Update leave request' }) + @ApiResponse({ status: 201, description: 'Leave request updated',type: LeaveRequestEntity }) + @ApiResponse({ status: 400, description: 'Leave request not found' }) + update(@Param('id', ParseIntPipe) id: number,@Body() dto: UpdateLeaveRequestsDto): Promise { return this.leaveRequetsService.update(id, dto); } @Delete(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({summary: 'Delete leave request' }) + @ApiResponse({ status: 201, description: 'Leave request deleted',type: LeaveRequestEntity }) + @ApiResponse({ status: 400, description: 'Leave request not found' }) remove(@Param('id', ParseIntPipe) id: number): Promise { return this.leaveRequetsService.remove(id); } diff --git a/src/modules/leave-requests/dtos/create-leave-requests.dto.ts b/src/modules/leave-requests/dtos/create-leave-requests.dto.ts index 2091785..703cf91 100644 --- a/src/modules/leave-requests/dtos/create-leave-requests.dto.ts +++ b/src/modules/leave-requests/dtos/create-leave-requests.dto.ts @@ -1,27 +1,52 @@ +import { ApiProperty } from "@nestjs/swagger"; import { LeaveApprovalStatus, LeaveTypes } from "@prisma/client"; import { Type } from "class-transformer"; import { IsEnum, IsInt, IsNotEmpty, IsOptional, IsString } from "class-validator"; export class CreateLeaveRequestsDto { + @ApiProperty({ + example: '4655867', + description: 'Employee`s id', + }) @Type(()=> Number) @IsInt() employee_id: number; + @ApiProperty({ + example: 'Sick or Vacation or Unpaid or Bereavement or Parental or Legal', + description: 'type of leave request for an accounting perception', + }) @IsEnum(LeaveTypes) leave_type: LeaveTypes; + @ApiProperty({ + example: '22/06/2463', + description: 'Leave request`s start date', + }) @Type(()=>Date) @IsNotEmpty() start_date_time:Date; + @ApiProperty({ + example: '25/03/3019', + description: 'Leave request`s end date', + }) @Type(()=>Date) @IsOptional() end_date_time?: Date; + @ApiProperty({ + example: 'My precious', + description: 'Leave request`s comment', + }) @IsString() @IsNotEmpty() comment: string; + @ApiProperty({ + example: 'True or False or Pending or Denied or Cancelled or Escalated', + description: 'Leave request`s approval status', + }) @IsEnum(LeaveApprovalStatus) @IsOptional() approval_status?: LeaveApprovalStatus; diff --git a/src/modules/leave-requests/dtos/swagger-entities/leave-requests.entity.ts b/src/modules/leave-requests/dtos/swagger-entities/leave-requests.entity.ts new file mode 100644 index 0000000..909323c --- /dev/null +++ b/src/modules/leave-requests/dtos/swagger-entities/leave-requests.entity.ts @@ -0,0 +1,49 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { LeaveApprovalStatus, LeaveTypes } from '@prisma/client'; + +export class LeaveRequestEntity { + @ApiProperty({ + example: 1, + description: 'Identifiant unique de la requête de congé (auto-incrémenté)', + }) + id: number; + + @ApiProperty({ + example: 42, + description: 'Identifiant de l’employé concerné', + }) + employee_id: number; + + @ApiProperty({ + example: 'SICK', + enum: LeaveTypes, + description: 'Type de congé demandé', + }) + leave_type: LeaveTypes; + + @ApiProperty({ + example: '2463-06-22T00:00:00.000Z', + description: 'Date de début du congé', + }) + start_date_time: Date; + + @ApiProperty({ + example: '3019-03-25T00:00:00.000Z', + description: 'Date de fin du congé (facultative)', + required: false, + }) + end_date_time?: Date; + + @ApiProperty({ + example: 'My precious', + description: 'Commentaire de l’employé lié à la demande', + }) + comment: string; + + @ApiProperty({ + example: 'PENDING', + enum: LeaveApprovalStatus, + description: 'Statut de validation de la demande', + }) + approval_status: LeaveApprovalStatus; +} diff --git a/src/modules/oauth-access-tokens/controllers/oauth-access-tokens.controller.ts b/src/modules/oauth-access-tokens/controllers/oauth-access-tokens.controller.ts index edcdbde..156f463 100644 --- a/src/modules/oauth-access-tokens/controllers/oauth-access-tokens.controller.ts +++ b/src/modules/oauth-access-tokens/controllers/oauth-access-tokens.controller.ts @@ -1,41 +1,62 @@ -import { Body, Controller, Delete, Get, Param, Patch, Post } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, Patch, Post, UseGuards } from '@nestjs/common'; import { OauthAccessTokensService } from '../services/oauth-access-tokens.service'; import { CreateOauthAccessTokenDto } from '../dtos/create-oauth-access-token.dto'; import { OAuthAccessTokens } from '@prisma/client'; import { UpdateOauthAccessTokenDto } from '../dtos/update-oauth-access-token.dto'; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { Roles as RoleEnum } from '.prisma/client'; +import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { JwtAuthGuard } from 'src/modules/authentication/guards/jwt-auth.guard'; +import { OAuthAccessTokenEntity } from '../dtos/swagger-entities/oauth-access-token.entity'; +@ApiTags('OAuth Access Tokens') +@ApiBearerAuth('access-token') +@UseGuards(JwtAuthGuard) @Controller('oauth-access-tokens') export class OauthAccessTokensController { constructor(private readonly oauthAccessTokensService: OauthAccessTokensService){} @Post() @RolesAllowed(RoleEnum.ADMIN) + @ApiOperation({summary: 'Create OAuth access token' }) + @ApiResponse({ status: 201, description: 'OAuth access token created', type: OAuthAccessTokenEntity }) + @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) create(@Body()dto: CreateOauthAccessTokenDto): Promise { return this.oauthAccessTokensService.create(dto); } @Get() @RolesAllowed(RoleEnum.ADMIN) + @ApiOperation({summary: 'Find all OAuth access token' }) + @ApiResponse({ status: 201, description: 'List of OAuth access token found', type: OAuthAccessTokenEntity, isArray: true }) + @ApiResponse({ status: 400, description: 'List of OAuth access token not found' }) findAll(): Promise { return this.oauthAccessTokensService.findAll(); } @Get(':id') @RolesAllowed(RoleEnum.ADMIN) + @ApiOperation({summary: 'Find OAuth access token' }) + @ApiResponse({ status: 201, description: 'OAuth access token found', type: OAuthAccessTokenEntity }) + @ApiResponse({ status: 400, description: 'OAuth access token not found' }) findOne(@Param('id') id: string): Promise { return this.oauthAccessTokensService.findOne(id); } @Patch(':id') @RolesAllowed(RoleEnum.ADMIN) + @ApiOperation({summary: 'Update OAuth access token' }) + @ApiResponse({ status: 201, description: 'OAuth access token updated', type: OAuthAccessTokenEntity }) + @ApiResponse({ status: 400, description: 'OAuth access token not found' }) update(@Param('id') id: string, @Body() dto: UpdateOauthAccessTokenDto): Promise { return this.oauthAccessTokensService.update(id,dto); } @Delete(':id') @RolesAllowed(RoleEnum.ADMIN) + @ApiOperation({summary: 'Delete OAuth access token' }) + @ApiResponse({ status: 201, description: 'OAuth access token deleted', type: OAuthAccessTokenEntity }) + @ApiResponse({ status: 400, description: 'OAuth access token not found' }) remove(@Param('id') id: string): Promise { return this.oauthAccessTokensService.remove(id); } diff --git a/src/modules/oauth-access-tokens/dtos/create-oauth-access-token.dto.ts b/src/modules/oauth-access-tokens/dtos/create-oauth-access-token.dto.ts index 03db393..f3604b7 100644 --- a/src/modules/oauth-access-tokens/dtos/create-oauth-access-token.dto.ts +++ b/src/modules/oauth-access-tokens/dtos/create-oauth-access-token.dto.ts @@ -1,29 +1,60 @@ +import { ApiProperty } from "@nestjs/swagger"; import { Type } from "class-transformer"; import { IsArray, IsDate, IsOptional, IsString, IsUUID } from "class-validator"; export class CreateOauthAccessTokenDto { + @ApiProperty({ + example: 'S7A2U8R7O6N6', + description: 'User`s unique identification number', + }) @IsUUID() user_id: string; + @ApiProperty({ + example: 'app.targo.ca', + description: 'URL in which the access token is used for', + }) @IsString() application: string; + @ApiProperty({ + example: 'L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7 ...', + description: 'Access token', + }) @IsString() access_token: string; + @ApiProperty({ + example: 'Th3731102h1p07Th3R1n92', + description: 'Refresh token', + }) @IsString() refresh_token: string; + @ApiProperty({ + example: '25/12/3018', + description: 'Access token`s expiry date', + }) @Type(()=> Date) @IsDate() access_token_expiry: Date; + @ApiProperty({ + example: '26/02/3019', + description: 'Refresh token`s expiry date', + required: false, + }) @Type(()=> Date) @IsDate() @IsOptional() refresh_token_expiry?: Date; + @ApiProperty({ + example: 'access tolkiens, email, etc... ', + description: 'scopes of infos needed for access', + required: false, + }) @IsArray() @IsString() @IsOptional() diff --git a/src/modules/oauth-access-tokens/dtos/swagger-entities/oauth-access-token.entity.ts b/src/modules/oauth-access-tokens/dtos/swagger-entities/oauth-access-token.entity.ts new file mode 100644 index 0000000..8694eae --- /dev/null +++ b/src/modules/oauth-access-tokens/dtos/swagger-entities/oauth-access-token.entity.ts @@ -0,0 +1,72 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class OAuthAccessTokenEntity { + @ApiProperty({ + example: 'cklwi0vb70000z2z20q6f19qk', + description: 'Identifiant unique du token OAuth (généré automatiquement)', + }) + id: string; + + @ApiProperty({ + example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', + description: 'UUID de l’utilisateur auquel ce token appartient', + }) + user_id: string; + + @ApiProperty({ + example: 'app.targo.ca', + description: 'Nom de l’application ou domaine utilisant ce token', + }) + application: string; + + @ApiProperty({ + example: 'L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7', + description: 'Token d’accès OAuth', + }) + access_token: string; + + @ApiProperty({ + example: 'Th3731102h1p07Th3R1n92', + description: 'Token de rafraîchissement OAuth', + }) + refresh_token: string; + + @ApiProperty({ + example: '3018-12-25T00:00:00.000Z', + description: 'Date d’expiration du token d’accès', + }) + access_token_expiry: Date; + + @ApiProperty({ + example: '3019-02-26T00:00:00.000Z', + description: 'Date d’expiration du token de rafraîchissement (optionnelle)', + required: false, + }) + refresh_token_expiry?: Date; + + @ApiProperty({ + example: ['email', 'profile', 'access_tolkiens'], + description: 'Liste des autorisations/scopes liés à ce token', + required: false, + }) + scopes: string[]; + + @ApiProperty({ + example: false, + description: 'Indique si le token a été révoqué', + }) + is_revoked: boolean; + + @ApiProperty({ + example: '2025-07-22T08:44:00.000Z', + description: 'Date de création du token', + }) + created_at: Date; + + @ApiProperty({ + example: '2025-07-23T08:44:00.000Z', + description: 'Date de dernière mise à jour (optionnelle)', + required: false, + }) + updated_at?: Date; +} diff --git a/src/modules/shift-codes/controllers/shift-codes.controller.ts b/src/modules/shift-codes/controllers/shift-codes.controller.ts index b9ea9f8..db16eb6 100644 --- a/src/modules/shift-codes/controllers/shift-codes.controller.ts +++ b/src/modules/shift-codes/controllers/shift-codes.controller.ts @@ -1,35 +1,53 @@ -import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common"; import { ShiftCodesService } from "../services/shift-codes.service"; import { CreateShiftCodesDto } from "../dtos/create-shift-codes.dto"; import { ShiftCodes } from "@prisma/client"; import { UpdateShiftCodesDto } from "../dtos/update-shift-codes.dto"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { Roles as RoleEnum } from '.prisma/client'; +import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { JwtAuthGuard } from "src/modules/authentication/guards/jwt-auth.guard"; +import { ShiftCodesEntity } from "../dtos/swagger-entities/shift-codes.entity"; -@Controller() +@ApiTags('Shift Codes') +@ApiBearerAuth('access-token') +@UseGuards(JwtAuthGuard) +@Controller('shift-codes') export class ShiftCodesController { constructor(private readonly shiftCodesService: ShiftCodesService) {} @Post() @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR) + @ApiOperation({ summary: 'Create shift code' }) + @ApiResponse({ status: 201, description: 'Shift code created',type: ShiftCodesEntity }) + @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) create(@Body()dto: CreateShiftCodesDto): Promise { return this.shiftCodesService.create(dto); } @Get() @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR) + @ApiOperation({ summary: 'Find all shift codes' }) + @ApiResponse({ status: 201, description: 'List of shift codes found',type: ShiftCodesEntity, isArray: true }) + @ApiResponse({ status: 400, description: 'List of shift codes not found' }) findAll(): Promise { return this.shiftCodesService.findAll(); } @Get(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR) + @ApiOperation({ summary: 'Find shift code' }) + @ApiResponse({ status: 201, description: 'Shift code found',type: ShiftCodesEntity }) + @ApiResponse({ status: 400, description: 'Shift code not found' }) findOne(@Param('id', ParseIntPipe) id: number): Promise { return this.shiftCodesService.findOne(id); } @Patch(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR) + @ApiOperation({ summary: 'Update shift code' }) + @ApiResponse({ status: 201, description: 'Shift code updated',type: ShiftCodesEntity }) + @ApiResponse({ status: 400, description: 'Shift code not found' }) update(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateShiftCodesDto): Promise { return this.shiftCodesService.update(id,dto); @@ -37,6 +55,9 @@ export class ShiftCodesController { @Delete(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR) + @ApiOperation({ summary: 'Delete shift code' }) + @ApiResponse({ status: 201, description: 'Shift code deleted',type: ShiftCodesEntity }) + @ApiResponse({ status: 400, description: 'Shift code not found' }) remove(@Param('id', ParseIntPipe)id: number): Promise { return this.shiftCodesService.remove(id); } diff --git a/src/modules/shift-codes/dtos/create-shift-codes.dto.ts b/src/modules/shift-codes/dtos/create-shift-codes.dto.ts index f92d04d..3fa173c 100644 --- a/src/modules/shift-codes/dtos/create-shift-codes.dto.ts +++ b/src/modules/shift-codes/dtos/create-shift-codes.dto.ts @@ -1,10 +1,20 @@ +import { ApiProperty } from "@nestjs/swagger"; import { IsNotEmpty, IsString } from "class-validator"; export class CreateShiftCodesDto { + + @ApiProperty({ + example: 'Regular or Night or Emergency, etc...', + description: 'Type of shifts for an account perception', + }) @IsString() @IsNotEmpty() shift_type: string; + @ApiProperty({ + example: 'G1, G2, G3, etc...', + description: 'bank`s code related to the type of shift', + }) @IsString() @IsNotEmpty() bank_code: string; diff --git a/src/modules/shift-codes/dtos/swagger-entities/shift-codes.entity.ts b/src/modules/shift-codes/dtos/swagger-entities/shift-codes.entity.ts new file mode 100644 index 0000000..b34bc80 --- /dev/null +++ b/src/modules/shift-codes/dtos/swagger-entities/shift-codes.entity.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class ShiftCodesEntity { + @ApiProperty({ + example: 1, + description: 'Identifiant unique du code de quart (généré automatiquement)', + }) + id: number; + + @ApiProperty({ + example: 'Night', + description: 'Type de quart de travail pour perception comptable ou RH', + }) + shift_type: string; + + @ApiProperty({ + example: 'G2', + description: 'Code bancaire ou budgétaire lié au quart de travail', + }) + bank_code: string; +} diff --git a/src/modules/shifts/controllers/shifts.controller.ts b/src/modules/shifts/controllers/shifts.controller.ts index ad7faf7..d1d23b0 100644 --- a/src/modules/shifts/controllers/shifts.controller.ts +++ b/src/modules/shifts/controllers/shifts.controller.ts @@ -1,45 +1,62 @@ -import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from "@nestjs/common"; +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common"; import { ShiftsService } from "../services/shifts.service"; import { Shifts } from "@prisma/client"; import { CreateShiftDto } from "../dtos/create-shifts.dto"; import { UpdateShiftsDto } from "../dtos/update-shifts.dto"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { Roles as RoleEnum } from '.prisma/client'; +import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +import { JwtAuthGuard } from "src/modules/authentication/guards/jwt-auth.guard"; +import { ShiftEntity } from "../dtos/swagger-entities/shift.entity"; +@ApiTags('Shifts') +@ApiBearerAuth('access-token') +@UseGuards(JwtAuthGuard) @Controller('shifts') export class ShiftsController { constructor(private readonly shiftsService: ShiftsService){} - @Post() @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Create shift' }) + @ApiResponse({ status: 201, description: 'Shift created',type: ShiftEntity }) + @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) create(@Body() dto: CreateShiftDto): Promise { return this.shiftsService.create(dto); } @Get() @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Find all shifts' }) + @ApiResponse({ status: 201, description: 'List of shifts found',type: ShiftEntity, isArray: true }) + @ApiResponse({ status: 400, description: 'List of shifts not found' }) findAll(): Promise { return this.shiftsService.findAll(); } @Get(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Find shift' }) + @ApiResponse({ status: 201, description: 'Shift found',type: ShiftEntity }) + @ApiResponse({ status: 400, description: 'Shift not found' }) findOne(@Param('id', ParseIntPipe) id: number): Promise { return this.shiftsService.findOne(id); } @Patch(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) - update( - @Param('id', ParseIntPipe) id: number, - @Body() dto: UpdateShiftsDto, - ): Promise { + @ApiOperation({ summary: 'Update shift' }) + @ApiResponse({ status: 201, description: 'Shift updated',type: ShiftEntity }) + @ApiResponse({ status: 400, description: 'Shift not found' }) + update(@Param('id', ParseIntPipe) id: number,@Body() dto: UpdateShiftsDto): Promise { return this.shiftsService.update(id, dto); } @Delete(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Delete shift' }) + @ApiResponse({ status: 201, description: 'Shift deleted',type: ShiftEntity }) + @ApiResponse({ status: 400, description: 'Shift not found' }) remove(@Param('id', ParseIntPipe) id: number): Promise { return this.shiftsService.remove(id); } diff --git a/src/modules/shifts/dtos/create-shifts.dto.ts b/src/modules/shifts/dtos/create-shifts.dto.ts index 4365be6..7ec4a51 100644 --- a/src/modules/shifts/dtos/create-shifts.dto.ts +++ b/src/modules/shifts/dtos/create-shifts.dto.ts @@ -1,26 +1,47 @@ +import { ApiProperty } from "@nestjs/swagger"; import { Type } from "class-transformer"; import { IsDate, IsDateString, IsInt } from "class-validator"; export class CreateShiftDto { + + @ApiProperty({ + example: 'Th3F3110w5h1pX2024', + description: 'identification number for a set timesheet', + }) @Type(() => Number) @IsInt() timesheet_id: number; + @ApiProperty({ + example: '0n3R1n962Ru13xX', + description: 'identification number for a shift code', + }) @Type(() => Number) @IsInt() shift_code_id: number; - + @ApiProperty({ + example: '20/10/3018', + description: 'Date where the shift takes place', + }) @IsDateString() @Type(() => Date) @IsDate() date: Date; + @ApiProperty({ + example: '08:00', + description: 'Start time of the said shift', + }) @IsDateString() @Type(() => Date) @IsDate() start_time: Date; + @ApiProperty({ + example: '17:00', + description: 'End time of the said shift', + }) @IsDateString() @Type(() => Date) @IsDate() diff --git a/src/modules/shifts/dtos/swagger-entities/shift.entity.ts b/src/modules/shifts/dtos/swagger-entities/shift.entity.ts new file mode 100644 index 0000000..3f43863 --- /dev/null +++ b/src/modules/shifts/dtos/swagger-entities/shift.entity.ts @@ -0,0 +1,39 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class ShiftEntity { + @ApiProperty({ + example: 1, + description: 'Identifiant unique du quart de travail (généré automatiquement)', + }) + id: number; + + @ApiProperty({ + example: 101, + description: 'Identifiant de la feuille de temps à laquelle le quart est rattaché', + }) + timesheet_id: number; + + @ApiProperty({ + example: 7, + description: 'Identifiant du code de quart utilisé (lien avec ShiftCodes)', + }) + shift_code_id: number; + + @ApiProperty({ + example: '3018-10-20T00:00:00.000Z', + description: 'Date à laquelle le quart a lieu', + }) + date: Date; + + @ApiProperty({ + example: '3018-10-20T08:00:00.000Z', + description: 'Heure de début du quart', + }) + start_time: Date; + + @ApiProperty({ + example: '3018-10-20T17:00:00.000Z', + description: 'Heure de fin du quart', + }) + end_time: Date; +} diff --git a/src/modules/timesheets/controllers/timesheets.controller.ts b/src/modules/timesheets/controllers/timesheets.controller.ts index a8369ee..e90e08f 100644 --- a/src/modules/timesheets/controllers/timesheets.controller.ts +++ b/src/modules/timesheets/controllers/timesheets.controller.ts @@ -1,35 +1,53 @@ -import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common'; +import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from '@nestjs/common'; import { TimesheetsService } from '../services/timesheets.service'; import { CreateTimesheetDto } from '../dtos/create-timesheet.dto'; import { Timesheets } from '@prisma/client'; import { UpdateTimesheetDto } from '../dtos/update-timesheet.dto'; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { Roles as RoleEnum } from '.prisma/client'; +import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { JwtAuthGuard } from 'src/modules/authentication/guards/jwt-auth.guard'; +import { TimesheetEntity } from '../dtos/swagger-entities/timesheet.entity'; +@ApiTags('Timesheets') +@ApiBearerAuth('access-token') +@UseGuards(JwtAuthGuard) @Controller('timesheets') export class TimesheetsController { constructor(private readonly timesheetsService: TimesheetsService) {} @Post() @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Create timesheet' }) + @ApiResponse({ status: 201, description: 'Timesheet created', type: TimesheetEntity }) + @ApiResponse({ status: 400, description: 'Incomplete task or invalid data' }) create(@Body() dto: CreateTimesheetDto): Promise { return this.timesheetsService.create(dto); } @Get() @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Find all timesheets' }) + @ApiResponse({ status: 201, description: 'List of timesheet found', type: TimesheetEntity, isArray: true }) + @ApiResponse({ status: 400, description: 'List of timesheets not found' }) findAll(): Promise { return this.timesheetsService.findAll(); } @Get(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Find timesheet' }) + @ApiResponse({ status: 201, description: 'Timesheet found', type: TimesheetEntity }) + @ApiResponse({ status: 400, description: 'Timesheet not found' }) findOne(@Param('id', ParseIntPipe) id: number): Promise { return this.timesheetsService.findOne(id); } @Patch(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Update timesheet' }) + @ApiResponse({ status: 201, description: 'Timesheet updated', type: TimesheetEntity }) + @ApiResponse({ status: 400, description: 'Timesheet not found' }) update( @Param('id', ParseIntPipe) id:number, @Body() dto: UpdateTimesheetDto, @@ -39,6 +57,9 @@ export class TimesheetsController { @Delete(':id') @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) + @ApiOperation({ summary: 'Delete timesheet' }) + @ApiResponse({ status: 201, description: 'Timesheet deleted', type: TimesheetEntity }) + @ApiResponse({ status: 400, description: 'Timesheet not found' }) remove(@Param('id', ParseIntPipe) id: number): Promise { return this.timesheetsService.remove(id); } diff --git a/src/modules/timesheets/dtos/create-timesheet.dto.ts b/src/modules/timesheets/dtos/create-timesheet.dto.ts index 8194cc1..8f0bdfd 100644 --- a/src/modules/timesheets/dtos/create-timesheet.dto.ts +++ b/src/modules/timesheets/dtos/create-timesheet.dto.ts @@ -1,12 +1,21 @@ +import { ApiProperty } from "@nestjs/swagger"; import { Type } from "class-transformer"; import { IsBoolean, IsInt, IsOptional } from "class-validator"; export class CreateTimesheetDto { + @ApiProperty({ + example: '426433', + description: 'identification number of the employee', + }) @Type(() => Number) @IsInt() employee_id: number; + @ApiProperty({ + example: 'True or False or Pending or Denied or Cancelled or Escalated', + description: 'Timesheet`s approval status', + }) @IsOptional() @IsBoolean() is_approved?: boolean; diff --git a/src/modules/timesheets/dtos/swagger-entities/timesheet.entity.ts b/src/modules/timesheets/dtos/swagger-entities/timesheet.entity.ts new file mode 100644 index 0000000..3e261c2 --- /dev/null +++ b/src/modules/timesheets/dtos/swagger-entities/timesheet.entity.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class TimesheetEntity { + @ApiProperty({ + example: 1, + description: 'Identifiant unique de la feuille de temps (généré automatiquement)', + }) + id: number; + + @ApiProperty({ + example: 426433, + description: 'Identifiant de l’employé associé à cette feuille de temps', + }) + employee_id: number; + + @ApiProperty({ + example: true, + description: 'Statut d’approbation de la feuille de temps', + }) + is_approved: boolean; +} diff --git a/src/modules/users-management/swagger-entities/user.entity.ts b/src/modules/users-management/swagger-entities/user.entity.ts new file mode 100644 index 0000000..271b41e --- /dev/null +++ b/src/modules/users-management/swagger-entities/user.entity.ts @@ -0,0 +1,48 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Roles } from '@prisma/client'; + +export class UserEntity { + @ApiProperty({ + example: 'd67f05be-6dd1-464f-b5f7-31b325e21b4a', + description: 'UUID unique de l’utilisateur (clé primaire)', + }) + id: string; + + @ApiProperty({ + example: 'Aragorn', + description: 'Prénom de l’utilisateur', + }) + first_name: string; + + @ApiProperty({ + example: 'Elessar', + description: 'Nom de famille de l’utilisateur', + }) + last_name: string; + + @ApiProperty({ + example: 'king@arnor-gondor.gov', + description: 'Adresse courriel unique', + }) + email: string; + + @ApiProperty({ + example: 5141234567, + description: 'Numéro de téléphone unique', + }) + phone_number: number; + + @ApiProperty({ + example: 'Minas Tirith, Gondor', + description: 'Adresse de résidence (facultative)', + required: false, + }) + residence?: string; + + @ApiProperty({ + example: 'EMPLOYEE', + enum: Roles, + description: 'Rôle attribué à l’utilisateur', + }) + role: Roles; +} From 7b7b42780f69d7c9420f96149311a867dad5e753 Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Tue, 22 Jul 2025 15:56:01 -0400 Subject: [PATCH 4/4] fix(swagger): typos and fixes --- docs/swagger/swagger-spec.json | 108 +++++++++--------- .../customers/dtos/create-customer.dto.ts | 3 +- .../dtos/swagger-entities/customers.entity.ts | 12 +- .../dtos/swagger-entities/employees.entity.ts | 12 +- .../swagger-entities/leave-requests.entity.ts | 18 +-- .../dtos/create-oauth-access-token.dto.ts | 2 +- .../oauth-access-token.entity.ts | 26 ++--- .../swagger-entities/shift-codes.entity.ts | 6 +- src/modules/shifts/dtos/create-shifts.dto.ts | 4 +- .../dtos/swagger-entities/shift.entity.ts | 12 +- .../timesheets/dtos/create-timesheet.dto.ts | 2 +- .../dtos/swagger-entities/timesheet.entity.ts | 6 +- .../swagger-entities/user.entity.ts | 14 +-- 13 files changed, 112 insertions(+), 113 deletions(-) diff --git a/docs/swagger/swagger-spec.json b/docs/swagger/swagger-spec.json index f586066..b094ca8 100644 --- a/docs/swagger/swagger-spec.json +++ b/docs/swagger/swagger-spec.json @@ -1482,7 +1482,7 @@ }, "scopes": { "example": "access tolkiens, email, etc... ", - "description": "scopes of infos needed for access", + "description": "scopes of infos linked to the access token", "type": "array", "items": { "type": "string" @@ -1503,39 +1503,39 @@ "id": { "type": "string", "example": "cklwi0vb70000z2z20q6f19qk", - "description": "Identifiant unique du token OAuth (généré automatiquement)" + "description": "Unique ID of an OAuth token (auto-generated)" }, "user_id": { "type": "string", "example": "0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d", - "description": "UUID de l’utilisateur auquel ce token appartient" + "description": "UUID User`s unique identification number" }, "application": { "type": "string", "example": "app.targo.ca", - "description": "Nom de l’application ou domaine utilisant ce token" + "description": "URL in which the access token is used for" }, "access_token": { "type": "string", "example": "L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7", - "description": "Token d’accès OAuth" + "description": "Access token" }, "refresh_token": { "type": "string", "example": "Th3731102h1p07Th3R1n92", - "description": "Token de rafraîchissement OAuth" + "description": "Refresh token" }, "access_token_expiry": { "format": "date-time", "type": "string", "example": "3018-12-25T00:00:00.000Z", - "description": "Date d’expiration du token d’accès" + "description": "Access token`s expiry date" }, "refresh_token_expiry": { "format": "date-time", "type": "string", "example": "3019-02-26T00:00:00.000Z", - "description": "Date d’expiration du token de rafraîchissement (optionnelle)" + "description": "Refresh token`s expiry date (optional)" }, "scopes": { "example": [ @@ -1543,7 +1543,7 @@ "profile", "access_tolkiens" ], - "description": "Liste des autorisations/scopes liés à ce token", + "description": "scopes of infos linked to the access token", "type": "array", "items": { "type": "string" @@ -1552,19 +1552,19 @@ "is_revoked": { "type": "boolean", "example": false, - "description": "Indique si le token a été révoqué" + "description": "revoke status" }, "created_at": { "format": "date-time", "type": "string", - "example": "2025-07-22T08:44:00.000Z", - "description": "Date de création du token" + "example": "2025-07-22", + "description": "creation date" }, "updated_at": { "format": "date-time", "type": "string", - "example": "2025-07-23T08:44:00.000Z", - "description": "Date de dernière mise à jour (optionnelle)" + "example": "2025-07-23", + "description": "Latest update (optional)" } }, "required": [ @@ -1615,7 +1615,7 @@ }, "scopes": { "example": "access tolkiens, email, etc... ", - "description": "scopes of infos needed for access", + "description": "scopes of infos linked to the access token", "type": "array", "items": { "type": "string" @@ -1670,32 +1670,32 @@ "id": { "type": "number", "example": 1, - "description": "Identifiant unique du client (clé primaire auto-incrémentée)" + "description": "Unique ID of a customer(primary-key, auto-incremented)" }, "user_id": { "type": "string", "example": "0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d", - "description": "UUID de l’utilisateur lié à ce client" + "description": "UUID of the user linked to that customer" }, "email": { "type": "string", "example": "you_shall_not_pass@middleEarth.com", - "description": "Adresse email du client (optionnelle)" + "description": "customer`s email (optional)" }, "phone_number": { "type": "number", "example": 8436637464, - "description": "Numéro de téléphone du client (chiffres seulement)" + "description": "customer`s phone number (numbers only)" }, "residence": { "type": "string", "example": "1 Ringbearer’s way, Mount Doom city, ME, T1R 1N6", - "description": "Adresse de résidence du client (optionnelle)" + "description": "customer`s residence address (optional)" }, "invoice_id": { "type": "number", "example": 4263253, - "description": "Numéro de facture lié à ce client (optionnel mais unique)" + "description": "customer`s invoice number (optionnal, unique)" } }, "required": [ @@ -1806,34 +1806,34 @@ "id": { "type": "number", "example": 1, - "description": "Identifiant unique de l’employé (clé primaire auto-incrémentée)" + "description": "Unique ID of an employee(primary-key, auto-incremented)" }, "user_id": { "type": "string", "example": "0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d", - "description": "UUID de l’utilisateur lié à cet employé" + "description": "UUID of the user linked to that employee" }, "external_payroll_id": { "type": "number", "example": 7464, - "description": "Identifiant externe dans le système de paie" + "description": "external ID for the pay system" }, "company_code": { "type": "number", "example": 335567447, - "description": "Code de la compagnie" + "description": "company code" }, "first_work_day": { "format": "date-time", "type": "string", "example": "3018-09-23T00:00:00.000Z", - "description": "Date de début d’emploi" + "description": "Employee first day at work" }, "last_work_day": { "format": "date-time", "type": "string", "example": "3019-03-25T00:00:00.000Z", - "description": "Date de fin d’emploi (optionnelle)" + "description": "Employee last day at work" } }, "required": [ @@ -1947,12 +1947,12 @@ "id": { "type": "number", "example": 1, - "description": "Identifiant unique de la requête de congé (auto-incrémenté)" + "description": "Leave request`s unique id(auto-incremented)" }, "employee_id": { "type": "number", "example": 42, - "description": "Identifiant de l’employé concerné" + "description": "ID of concerned employee" }, "leave_type": { "type": "string", @@ -1965,24 +1965,24 @@ "PARENTAL", "LEGAL" ], - "description": "Type de congé demandé" + "description": "type of leave request for an accounting perception" }, "start_date_time": { "format": "date-time", "type": "string", - "example": "2463-06-22T00:00:00.000Z", - "description": "Date de début du congé" + "example": "22/06/2463", + "description": "Leave request`s start date" }, "end_date_time": { "format": "date-time", "type": "string", - "example": "3019-03-25T00:00:00.000Z", - "description": "Date de fin du congé (facultative)" + "example": "25/03/3019", + "description": "Leave request`s end date (optionnal)" }, "comment": { "type": "string", "example": "My precious", - "description": "Commentaire de l’employé lié à la demande" + "description": "Leave request employee`s comment" }, "approval_status": { "type": "string", @@ -1994,7 +1994,7 @@ "CANCELLED", "ESCALATED" ], - "description": "Statut de validation de la demande" + "description": "Leave request`s approval status" } }, "required": [ @@ -2068,17 +2068,17 @@ "id": { "type": "number", "example": 1, - "description": "Identifiant unique du code de quart (généré automatiquement)" + "description": "Unique ID of a shift-code (auto-generated)" }, "shift_type": { "type": "string", "example": "Night", - "description": "Type de quart de travail pour perception comptable ou RH" + "description": "Type of shifts for an account perception" }, "bank_code": { "type": "string", "example": "G2", - "description": "Code bancaire ou budgétaire lié au quart de travail" + "description": "bank`s code related to the type of shift" } }, "required": [ @@ -2108,12 +2108,12 @@ "timesheet_id": { "type": "number", "example": "Th3F3110w5h1pX2024", - "description": "identification number for a set timesheet" + "description": "ID number for a set timesheet" }, "shift_code_id": { "type": "number", "example": "0n3R1n962Ru13xX", - "description": "identification number for a shift code" + "description": "ID number of a shift code (link with shift-codes)" }, "date": { "format": "date-time", @@ -2148,35 +2148,35 @@ "id": { "type": "number", "example": 1, - "description": "Identifiant unique du quart de travail (généré automatiquement)" + "description": "Unique ID of the shift (auto-generated)" }, "timesheet_id": { "type": "number", "example": 101, - "description": "Identifiant de la feuille de temps à laquelle le quart est rattaché" + "description": "ID number for a set timesheet" }, "shift_code_id": { "type": "number", "example": 7, - "description": "Identifiant du code de quart utilisé (lien avec ShiftCodes)" + "description": "ID number of a shift code (link with shift-codes)" }, "date": { "format": "date-time", "type": "string", "example": "3018-10-20T00:00:00.000Z", - "description": "Date à laquelle le quart a lieu" + "description": "Date where the shift takes place" }, "start_time": { "format": "date-time", "type": "string", "example": "3018-10-20T08:00:00.000Z", - "description": "Heure de début du quart" + "description": "Start time of the said shift" }, "end_time": { "format": "date-time", "type": "string", "example": "3018-10-20T17:00:00.000Z", - "description": "Heure de fin du quart" + "description": "End time of the said shift" } }, "required": [ @@ -2194,12 +2194,12 @@ "timesheet_id": { "type": "number", "example": "Th3F3110w5h1pX2024", - "description": "identification number for a set timesheet" + "description": "ID number for a set timesheet" }, "shift_code_id": { "type": "number", "example": "0n3R1n962Ru13xX", - "description": "identification number for a shift code" + "description": "ID number of a shift code (link with shift-codes)" }, "date": { "format": "date-time", @@ -2227,7 +2227,7 @@ "employee_id": { "type": "number", "example": "426433", - "description": "identification number of the employee" + "description": "employee`s ID number of linked timsheet" }, "is_approved": { "type": "boolean", @@ -2246,17 +2246,17 @@ "id": { "type": "number", "example": 1, - "description": "Identifiant unique de la feuille de temps (généré automatiquement)" + "description": "timesheet`s unique ID (auto-generated)" }, "employee_id": { "type": "number", "example": 426433, - "description": "Identifiant de l’employé associé à cette feuille de temps" + "description": "employee`s ID number of linked timsheet" }, "is_approved": { "type": "boolean", "example": true, - "description": "Statut d’approbation de la feuille de temps" + "description": "Timesheet`s approval status" } }, "required": [ @@ -2271,7 +2271,7 @@ "employee_id": { "type": "number", "example": "426433", - "description": "identification number of the employee" + "description": "employee`s ID number of linked timsheet" }, "is_approved": { "type": "boolean", diff --git a/src/modules/customers/dtos/create-customer.dto.ts b/src/modules/customers/dtos/create-customer.dto.ts index 5b6630b..201aaa3 100644 --- a/src/modules/customers/dtos/create-customer.dto.ts +++ b/src/modules/customers/dtos/create-customer.dto.ts @@ -14,8 +14,7 @@ export class CreateCustomerDto { @ApiProperty({ example: 'Gandalf', description: 'Customer`s first name', - }) - + }) @IsString() @IsNotEmpty() first_name: string; diff --git a/src/modules/customers/dtos/swagger-entities/customers.entity.ts b/src/modules/customers/dtos/swagger-entities/customers.entity.ts index b1cdf36..3e8ac99 100644 --- a/src/modules/customers/dtos/swagger-entities/customers.entity.ts +++ b/src/modules/customers/dtos/swagger-entities/customers.entity.ts @@ -3,39 +3,39 @@ import { ApiProperty } from '@nestjs/swagger'; export class CustomerEntity { @ApiProperty({ example: 1, - description: 'Identifiant unique du client (clé primaire auto-incrémentée)', + description: 'Unique ID of a customer(primary-key, auto-incremented)', }) id: number; @ApiProperty({ example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', - description: 'UUID de l’utilisateur lié à ce client', + description: 'UUID of the user linked to that customer', }) user_id: string; @ApiProperty({ example: 'you_shall_not_pass@middleEarth.com', - description: 'Adresse email du client (optionnelle)', + description: 'customer`s email (optional)', required: false, }) email?: string; @ApiProperty({ example: 8436637464, - description: 'Numéro de téléphone du client (chiffres seulement)', + description: 'customer`s phone number (numbers only)', }) phone_number: number; @ApiProperty({ example: '1 Ringbearer’s way, Mount Doom city, ME, T1R 1N6', - description: 'Adresse de résidence du client (optionnelle)', + description: 'customer`s residence address (optional)', required: false, }) residence?: string; @ApiProperty({ example: 4263253, - description: 'Numéro de facture lié à ce client (optionnel mais unique)', + description: 'customer`s invoice number (optionnal, unique)', required: false, }) invoice_id?: number; diff --git a/src/modules/employees/dtos/swagger-entities/employees.entity.ts b/src/modules/employees/dtos/swagger-entities/employees.entity.ts index 8828d25..d1431bb 100644 --- a/src/modules/employees/dtos/swagger-entities/employees.entity.ts +++ b/src/modules/employees/dtos/swagger-entities/employees.entity.ts @@ -3,37 +3,37 @@ import { ApiProperty } from '@nestjs/swagger'; export class EmployeeEntity { @ApiProperty({ example: 1, - description: 'Identifiant unique de l’employé (clé primaire auto-incrémentée)', + description: 'Unique ID of an employee(primary-key, auto-incremented)', }) id: number; @ApiProperty({ example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', - description: 'UUID de l’utilisateur lié à cet employé', + description: 'UUID of the user linked to that employee', }) user_id: string; @ApiProperty({ example: 7464, - description: 'Identifiant externe dans le système de paie', + description: 'external ID for the pay system', }) external_payroll_id: number; @ApiProperty({ example: 335567447, - description: 'Code de la compagnie', + description: 'company code', }) company_code: number; @ApiProperty({ example: '3018-09-23T00:00:00.000Z', - description: 'Date de début d’emploi', + description: 'Employee first day at work', }) first_work_day: Date; @ApiProperty({ example: '3019-03-25T00:00:00.000Z', - description: 'Date de fin d’emploi (optionnelle)', + description: 'Employee last day at work', required: false, }) last_work_day?: Date; diff --git a/src/modules/leave-requests/dtos/swagger-entities/leave-requests.entity.ts b/src/modules/leave-requests/dtos/swagger-entities/leave-requests.entity.ts index 909323c..2344828 100644 --- a/src/modules/leave-requests/dtos/swagger-entities/leave-requests.entity.ts +++ b/src/modules/leave-requests/dtos/swagger-entities/leave-requests.entity.ts @@ -4,46 +4,46 @@ import { LeaveApprovalStatus, LeaveTypes } from '@prisma/client'; export class LeaveRequestEntity { @ApiProperty({ example: 1, - description: 'Identifiant unique de la requête de congé (auto-incrémenté)', + description: 'Leave request`s unique id(auto-incremented)', }) id: number; @ApiProperty({ example: 42, - description: 'Identifiant de l’employé concerné', + description: 'ID of concerned employee', }) employee_id: number; @ApiProperty({ example: 'SICK', enum: LeaveTypes, - description: 'Type de congé demandé', + description: 'type of leave request for an accounting perception', }) leave_type: LeaveTypes; @ApiProperty({ - example: '2463-06-22T00:00:00.000Z', - description: 'Date de début du congé', + example: '22/06/2463', + description: 'Leave request`s start date', }) start_date_time: Date; @ApiProperty({ - example: '3019-03-25T00:00:00.000Z', - description: 'Date de fin du congé (facultative)', + example: '25/03/3019', + description: 'Leave request`s end date (optionnal)', required: false, }) end_date_time?: Date; @ApiProperty({ example: 'My precious', - description: 'Commentaire de l’employé lié à la demande', + description: 'Leave request employee`s comment', }) comment: string; @ApiProperty({ example: 'PENDING', enum: LeaveApprovalStatus, - description: 'Statut de validation de la demande', + description: 'Leave request`s approval status', }) approval_status: LeaveApprovalStatus; } diff --git a/src/modules/oauth-access-tokens/dtos/create-oauth-access-token.dto.ts b/src/modules/oauth-access-tokens/dtos/create-oauth-access-token.dto.ts index f3604b7..8e6d1ef 100644 --- a/src/modules/oauth-access-tokens/dtos/create-oauth-access-token.dto.ts +++ b/src/modules/oauth-access-tokens/dtos/create-oauth-access-token.dto.ts @@ -52,7 +52,7 @@ export class CreateOauthAccessTokenDto { @ApiProperty({ example: 'access tolkiens, email, etc... ', - description: 'scopes of infos needed for access', + description: 'scopes of infos linked to the access token', required: false, }) @IsArray() diff --git a/src/modules/oauth-access-tokens/dtos/swagger-entities/oauth-access-token.entity.ts b/src/modules/oauth-access-tokens/dtos/swagger-entities/oauth-access-token.entity.ts index 8694eae..e80e6d7 100644 --- a/src/modules/oauth-access-tokens/dtos/swagger-entities/oauth-access-token.entity.ts +++ b/src/modules/oauth-access-tokens/dtos/swagger-entities/oauth-access-token.entity.ts @@ -3,69 +3,69 @@ import { ApiProperty } from '@nestjs/swagger'; export class OAuthAccessTokenEntity { @ApiProperty({ example: 'cklwi0vb70000z2z20q6f19qk', - description: 'Identifiant unique du token OAuth (généré automatiquement)', + description: 'Unique ID of an OAuth token (auto-generated)', }) id: string; @ApiProperty({ example: '0e6e2e1f-b157-4c7c-ae3f-999b3e4f914d', - description: 'UUID de l’utilisateur auquel ce token appartient', + description: 'UUID User`s unique identification number', }) user_id: string; @ApiProperty({ example: 'app.targo.ca', - description: 'Nom de l’application ou domaine utilisant ce token', + description: 'URL in which the access token is used for', }) application: string; @ApiProperty({ example: 'L5O6R4D3/O6F3#T8H4E3&R6I4N6G4S7', - description: 'Token d’accès OAuth', + description: 'Access token', }) access_token: string; @ApiProperty({ example: 'Th3731102h1p07Th3R1n92', - description: 'Token de rafraîchissement OAuth', + description: 'Refresh token', }) refresh_token: string; @ApiProperty({ example: '3018-12-25T00:00:00.000Z', - description: 'Date d’expiration du token d’accès', + description: 'Access token`s expiry date', }) access_token_expiry: Date; @ApiProperty({ example: '3019-02-26T00:00:00.000Z', - description: 'Date d’expiration du token de rafraîchissement (optionnelle)', + description: 'Refresh token`s expiry date (optional)', required: false, }) refresh_token_expiry?: Date; @ApiProperty({ example: ['email', 'profile', 'access_tolkiens'], - description: 'Liste des autorisations/scopes liés à ce token', + description: 'scopes of infos linked to the access token', required: false, }) scopes: string[]; @ApiProperty({ example: false, - description: 'Indique si le token a été révoqué', + description: 'revoke status', }) is_revoked: boolean; @ApiProperty({ - example: '2025-07-22T08:44:00.000Z', - description: 'Date de création du token', + example: '2025-07-22', + description: 'creation date', }) created_at: Date; @ApiProperty({ - example: '2025-07-23T08:44:00.000Z', - description: 'Date de dernière mise à jour (optionnelle)', + example: '2025-07-23', + description: 'Latest update (optional)', required: false, }) updated_at?: Date; diff --git a/src/modules/shift-codes/dtos/swagger-entities/shift-codes.entity.ts b/src/modules/shift-codes/dtos/swagger-entities/shift-codes.entity.ts index b34bc80..6a90b6b 100644 --- a/src/modules/shift-codes/dtos/swagger-entities/shift-codes.entity.ts +++ b/src/modules/shift-codes/dtos/swagger-entities/shift-codes.entity.ts @@ -3,19 +3,19 @@ import { ApiProperty } from '@nestjs/swagger'; export class ShiftCodesEntity { @ApiProperty({ example: 1, - description: 'Identifiant unique du code de quart (généré automatiquement)', + description: 'Unique ID of a shift-code (auto-generated)', }) id: number; @ApiProperty({ example: 'Night', - description: 'Type de quart de travail pour perception comptable ou RH', + description: 'Type of shifts for an account perception', }) shift_type: string; @ApiProperty({ example: 'G2', - description: 'Code bancaire ou budgétaire lié au quart de travail', + description: 'bank`s code related to the type of shift', }) bank_code: string; } diff --git a/src/modules/shifts/dtos/create-shifts.dto.ts b/src/modules/shifts/dtos/create-shifts.dto.ts index 7ec4a51..2ed7dcb 100644 --- a/src/modules/shifts/dtos/create-shifts.dto.ts +++ b/src/modules/shifts/dtos/create-shifts.dto.ts @@ -6,7 +6,7 @@ export class CreateShiftDto { @ApiProperty({ example: 'Th3F3110w5h1pX2024', - description: 'identification number for a set timesheet', + description: 'ID number for a set timesheet', }) @Type(() => Number) @IsInt() @@ -14,7 +14,7 @@ export class CreateShiftDto { @ApiProperty({ example: '0n3R1n962Ru13xX', - description: 'identification number for a shift code', + description: 'ID number of a shift code (link with shift-codes)', }) @Type(() => Number) @IsInt() diff --git a/src/modules/shifts/dtos/swagger-entities/shift.entity.ts b/src/modules/shifts/dtos/swagger-entities/shift.entity.ts index 3f43863..787dfea 100644 --- a/src/modules/shifts/dtos/swagger-entities/shift.entity.ts +++ b/src/modules/shifts/dtos/swagger-entities/shift.entity.ts @@ -3,37 +3,37 @@ import { ApiProperty } from '@nestjs/swagger'; export class ShiftEntity { @ApiProperty({ example: 1, - description: 'Identifiant unique du quart de travail (généré automatiquement)', + description: 'Unique ID of the shift (auto-generated)', }) id: number; @ApiProperty({ example: 101, - description: 'Identifiant de la feuille de temps à laquelle le quart est rattaché', + description: 'ID number for a set timesheet', }) timesheet_id: number; @ApiProperty({ example: 7, - description: 'Identifiant du code de quart utilisé (lien avec ShiftCodes)', + description: 'ID number of a shift code (link with shift-codes)', }) shift_code_id: number; @ApiProperty({ example: '3018-10-20T00:00:00.000Z', - description: 'Date à laquelle le quart a lieu', + description: 'Date where the shift takes place', }) date: Date; @ApiProperty({ example: '3018-10-20T08:00:00.000Z', - description: 'Heure de début du quart', + description: 'Start time of the said shift', }) start_time: Date; @ApiProperty({ example: '3018-10-20T17:00:00.000Z', - description: 'Heure de fin du quart', + description: 'End time of the said shift', }) end_time: Date; } diff --git a/src/modules/timesheets/dtos/create-timesheet.dto.ts b/src/modules/timesheets/dtos/create-timesheet.dto.ts index 8f0bdfd..26b4e63 100644 --- a/src/modules/timesheets/dtos/create-timesheet.dto.ts +++ b/src/modules/timesheets/dtos/create-timesheet.dto.ts @@ -6,7 +6,7 @@ export class CreateTimesheetDto { @ApiProperty({ example: '426433', - description: 'identification number of the employee', + description: 'employee`s ID number of linked timsheet', }) @Type(() => Number) @IsInt() diff --git a/src/modules/timesheets/dtos/swagger-entities/timesheet.entity.ts b/src/modules/timesheets/dtos/swagger-entities/timesheet.entity.ts index 3e261c2..617d143 100644 --- a/src/modules/timesheets/dtos/swagger-entities/timesheet.entity.ts +++ b/src/modules/timesheets/dtos/swagger-entities/timesheet.entity.ts @@ -3,19 +3,19 @@ import { ApiProperty } from '@nestjs/swagger'; export class TimesheetEntity { @ApiProperty({ example: 1, - description: 'Identifiant unique de la feuille de temps (généré automatiquement)', + description: 'timesheet`s unique ID (auto-generated)', }) id: number; @ApiProperty({ example: 426433, - description: 'Identifiant de l’employé associé à cette feuille de temps', + description: 'employee`s ID number of linked timsheet', }) employee_id: number; @ApiProperty({ example: true, - description: 'Statut d’approbation de la feuille de temps', + description: 'Timesheet`s approval status', }) is_approved: boolean; } diff --git a/src/modules/users-management/swagger-entities/user.entity.ts b/src/modules/users-management/swagger-entities/user.entity.ts index 271b41e..61883ab 100644 --- a/src/modules/users-management/swagger-entities/user.entity.ts +++ b/src/modules/users-management/swagger-entities/user.entity.ts @@ -4,37 +4,37 @@ import { Roles } from '@prisma/client'; export class UserEntity { @ApiProperty({ example: 'd67f05be-6dd1-464f-b5f7-31b325e21b4a', - description: 'UUID unique de l’utilisateur (clé primaire)', + description: 'User`s unique UUID (primary key)', }) id: string; @ApiProperty({ example: 'Aragorn', - description: 'Prénom de l’utilisateur', + description: 'user`s first name', }) first_name: string; @ApiProperty({ example: 'Elessar', - description: 'Nom de famille de l’utilisateur', + description: 'user`s last name', }) last_name: string; @ApiProperty({ example: 'king@arnor-gondor.gov', - description: 'Adresse courriel unique', + description: 'Unique email address', }) email: string; @ApiProperty({ example: 5141234567, - description: 'Numéro de téléphone unique', + description: 'Unique phone number', }) phone_number: number; @ApiProperty({ example: 'Minas Tirith, Gondor', - description: 'Adresse de résidence (facultative)', + description: 'residence address (optional)', required: false, }) residence?: string; @@ -42,7 +42,7 @@ export class UserEntity { @ApiProperty({ example: 'EMPLOYEE', enum: Roles, - description: 'Rôle attribué à l’utilisateur', + description: 'User`s given role', }) role: Roles; }