diff --git a/prisma/migrations/20251003151925_added_preferences_table/migration.sql b/prisma/migrations/20251003151925_added_preferences_table/migration.sql new file mode 100644 index 0000000..ab54053 --- /dev/null +++ b/prisma/migrations/20251003151925_added_preferences_table/migration.sql @@ -0,0 +1,56 @@ +/* + Warnings: + + - You are about to drop the column `end_date_time` on the `leave_requests` table. All the data in the column will be lost. + - You are about to drop the column `start_date_time` on the `leave_requests` table. All the data in the column will be lost. + - You are about to drop the column `end_date_time` on the `leave_requests_archive` table. All the data in the column will be lost. + - You are about to drop the column `start_date_time` on the `leave_requests_archive` table. All the data in the column will be lost. + - A unique constraint covering the columns `[employee_id,leave_type,date]` on the table `leave_requests` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[leave_request_id]` on the table `leave_requests_archive` will be added. If there are existing duplicate values, this will fail. + - Added the required column `date` to the `leave_requests` table without a default value. This is not possible if the table is not empty. + - Added the required column `date` to the `leave_requests_archive` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterEnum +ALTER TYPE "leave_types" ADD VALUE 'HOLIDAY'; + +-- AlterTable +ALTER TABLE "leave_requests" DROP COLUMN "end_date_time", +DROP COLUMN "start_date_time", +ADD COLUMN "date" DATE NOT NULL, +ADD COLUMN "payable_hours" DECIMAL(5,2), +ADD COLUMN "requested_hours" DECIMAL(5,2); + +-- AlterTable +ALTER TABLE "leave_requests_archive" DROP COLUMN "end_date_time", +DROP COLUMN "start_date_time", +ADD COLUMN "date" DATE NOT NULL, +ADD COLUMN "payable_hours" DECIMAL(5,2), +ADD COLUMN "requested_hours" DECIMAL(5,2); + +-- CreateTable +CREATE TABLE "preferences" ( + "user_id" UUID NOT NULL, + "notifications" BOOLEAN NOT NULL DEFAULT false, + "dark_mode" BOOLEAN NOT NULL DEFAULT false, + "lang_switch" BOOLEAN NOT NULL DEFAULT false, + "lefty_mode" BOOLEAN NOT NULL DEFAULT false +); + +-- CreateIndex +CREATE UNIQUE INDEX "preferences_user_id_key" ON "preferences"("user_id"); + +-- CreateIndex +CREATE INDEX "leave_requests_employee_id_date_idx" ON "leave_requests"("employee_id", "date"); + +-- CreateIndex +CREATE UNIQUE INDEX "leave_requests_employee_id_leave_type_date_key" ON "leave_requests"("employee_id", "leave_type", "date"); + +-- CreateIndex +CREATE INDEX "leave_requests_archive_employee_id_date_idx" ON "leave_requests_archive"("employee_id", "date"); + +-- CreateIndex +CREATE UNIQUE INDEX "leave_requests_archive_leave_request_id_key" ON "leave_requests_archive"("leave_request_id"); + +-- AddForeignKey +ALTER TABLE "preferences" ADD CONSTRAINT "preferences_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b32b202..4a45c7b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -28,7 +28,7 @@ model Users { oauth_sessions OAuthSessions[] @relation("UserOAuthSessions") employees_archive EmployeesArchive[] @relation("UsersToEmployeesToArchive") customer_archive CustomersArchive[] @relation("UserToCustomersToArchive") - + preferences Preferences? @relation("UserPreferences") @@map("users") } @@ -314,6 +314,19 @@ model Attachments { @@map("attachments") } +model Preferences { + id Int @id @default(autoincrement()) + user Users @relation("UserPreferences", fields: [user_id], references: [id]) + user_id String @unique @db.Uuid + notifications Boolean @default(false) + dark_mode Boolean @default(false) + lang_switch Boolean @default(false) + lefty_mode Boolean @default(false) + + @@map("preferences") +} + + enum AttachmentStatus { ACTIVE DELETED diff --git a/src/modules/preferences/controllers/preferences.controller.ts b/src/modules/preferences/controllers/preferences.controller.ts new file mode 100644 index 0000000..ae5af16 --- /dev/null +++ b/src/modules/preferences/controllers/preferences.controller.ts @@ -0,0 +1,14 @@ +import { Body, Controller, Param, Patch } from "@nestjs/common"; +import { PreferencesService } from "../services/preferences.service"; +import { PreferencesDto } from "../dtos/preferences.dto"; + +@Controller('preferences') +export class PreferencesController { + constructor(private readonly service: PreferencesService){} + + @Patch(':email') + async updatePreferences(@Param('email') email: string, @Body()payload: PreferencesDto) { + return this.service.updatePreferences(email, payload); + } + +} \ No newline at end of file diff --git a/src/modules/preferences/dtos/preferences.dto.ts b/src/modules/preferences/dtos/preferences.dto.ts new file mode 100644 index 0000000..2bfa3e3 --- /dev/null +++ b/src/modules/preferences/dtos/preferences.dto.ts @@ -0,0 +1,16 @@ +import { IsBoolean, IsEmail, IsString } from "class-validator"; + +export class PreferencesDto { + + @IsBoolean() + notifications: boolean; + + @IsBoolean() + dark_mode: boolean; + + @IsBoolean() + lang_switch: boolean; + + @IsBoolean() + lefty_mode: boolean; +} \ No newline at end of file diff --git a/src/modules/preferences/preferences.module.ts b/src/modules/preferences/preferences.module.ts new file mode 100644 index 0000000..94161cb --- /dev/null +++ b/src/modules/preferences/preferences.module.ts @@ -0,0 +1,11 @@ +import { Module } from "@nestjs/common"; +import { PreferencesController } from "./controllers/preferences.controller"; +import { PreferencesService } from "./services/preferences.service"; + +@Module({ +controllers: [ PreferencesController ], +providers: [ PreferencesService ], +exports: [ PreferencesService ], +}) + +export class PreferencesModule {} \ No newline at end of file diff --git a/src/modules/preferences/services/preferences.service.ts b/src/modules/preferences/services/preferences.service.ts new file mode 100644 index 0000000..cafac84 --- /dev/null +++ b/src/modules/preferences/services/preferences.service.ts @@ -0,0 +1,32 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; +import { Preferences } from "@prisma/client"; +import { PrismaService } from "src/prisma/prisma.service"; +import { PreferencesDto } from "../dtos/preferences.dto"; + +@Injectable() +export class PreferencesService { + constructor(private readonly prisma: PrismaService){} + + async resolveUserIdWithEmail(email: string): Promise { + const user = await this.prisma.users.findFirst({ + where: { email }, + select: { id: true }, + }); + if(!user) throw new NotFoundException(`User with email ${ email } not found`); + return user.id; + } + + async updatePreferences(email: string, dto: PreferencesDto ): Promise { + const user_id = await this.resolveUserIdWithEmail(email); + return this.prisma.preferences.update({ + where: { user_id }, + data: { + notifications: dto.notifications, + dark_mode: dto.dark_mode, + lang_switch: dto.lang_switch, + lefty_mode: dto.lefty_mode, + }, + include: { user: true }, + }); + } +} \ No newline at end of file diff --git a/src/modules/shifts/controllers/shifts.controller.ts b/src/modules/shifts/controllers/shifts.controller.ts index 89af236..3a261ce 100644 --- a/src/modules/shifts/controllers/shifts.controller.ts +++ b/src/modules/shifts/controllers/shifts.controller.ts @@ -19,7 +19,6 @@ export class ShiftsController { constructor( private readonly shiftsService: ShiftsQueryService, private readonly shiftsCommandService: ShiftsCommandService, - private readonly shiftsValidationService: ShiftsQueryService, ){} @Put('upsert/:email/:date') @@ -85,14 +84,14 @@ export class ShiftsController { @Get('summary') async getSummary( @Query() query: GetShiftsOverviewDto): Promise { - return this.shiftsValidationService.getSummary(query.period_id); + return this.shiftsService.getSummary(query.period_id); } @Get('export.csv') @Header('Content-Type', 'text/csv; charset=utf-8') @Header('Content-Disposition', 'attachment; filename="shifts-validation.csv"') async exportCsv(@Query() query: GetShiftsOverviewDto): Promise{ - const rows = await this.shiftsValidationService.getSummary(query.period_id); + const rows = await this.shiftsService.getSummary(query.period_id); //CSV Headers const header = [ diff --git a/src/modules/shifts/services/shifts-query.service.ts b/src/modules/shifts/services/shifts-query.service.ts index 7bc6efe..cd1c286 100644 --- a/src/modules/shifts/services/shifts-query.service.ts +++ b/src/modules/shifts/services/shifts-query.service.ts @@ -99,10 +99,10 @@ export class ShiftsQueryService { data: { ...(timesheet_id !== undefined && { timesheet_id }), ...(bank_code_id !== undefined && { bank_code_id }), - ...(date !== undefined && { date }), - ...(start_time !== undefined && { start_time }), - ...(end_time !== undefined && { end_time }), - ...(comment !== undefined && { comment }), + ...(date !== undefined && { date }), + ...(start_time !== undefined && { start_time }), + ...(end_time !== undefined && { end_time }), + ...(comment !== undefined && { comment }), }, include: { timesheet: { include: { employee: { include: { user: true } } } }, bank_code: true, @@ -115,7 +115,7 @@ export class ShiftsQueryService { return this.prisma.shifts.delete({ where: { id } }); } - async getSummary(period_id: number): Promise { + async getSummary(period_id: number): Promise { //fetch pay-period to display const period = await this.prisma.payPeriods.findFirst({ where: { pay_period_no: period_id },