From 57d63ec3fe1fe0df2ad4720ea73007b675f80ada Mon Sep 17 00:00:00 2001 From: nicoleamber Date: Fri, 8 Sep 2023 13:19:56 +0800 Subject: [PATCH] [M1_TR-214] Backend for M1_TR-211 --- server/.gitignore | 1 + server/package.json | 6 +- server/src/api/jobs/dto/job-list.dto.ts | 10 ++ server/src/api/jobs/dto/job-query.dto.ts | 16 +++ server/src/api/jobs/jobs.controller.spec.ts | 69 +++++++++++ server/src/api/jobs/jobs.controller.ts | 19 ++++ server/src/api/jobs/jobs.module.ts | 10 ++ server/src/api/jobs/jobs.service.spec.ts | 70 ++++++++++++ server/src/api/jobs/jobs.service.ts | 56 +++++++++ .../src/api/sample/sample.controller.spec.ts | 18 --- server/src/api/sample/sample.controller.ts | 9 -- server/src/api/sample/sample.dto.ts | 11 -- server/src/api/sample/sample.entities.ts | 7 -- server/src/app.module.ts | 6 +- server/src/main.ts | 11 ++ .../20230907065110_init/migration.sql | 107 ++++++++++++++++++ .../src/models/migrations/migration_lock.toml | 3 + server/src/models/schema.prisma | 100 +++++++++++++++- server/src/models/seed.ts | 28 +++++ server/src/models/seeders/customer.seed.ts | 24 ++++ server/src/models/seeders/estimation.ts | 25 ++++ server/src/models/seeders/job.seed.ts | 27 +++++ server/src/models/seeders/schedule.seed.ts | 24 ++++ server/src/models/seeders/user.seed.ts | 22 ++++ server/yarn.lock | 54 +++++++-- 25 files changed, 671 insertions(+), 62 deletions(-) create mode 100644 server/src/api/jobs/dto/job-list.dto.ts create mode 100644 server/src/api/jobs/dto/job-query.dto.ts create mode 100644 server/src/api/jobs/jobs.controller.spec.ts create mode 100644 server/src/api/jobs/jobs.controller.ts create mode 100644 server/src/api/jobs/jobs.module.ts create mode 100644 server/src/api/jobs/jobs.service.spec.ts create mode 100644 server/src/api/jobs/jobs.service.ts delete mode 100644 server/src/api/sample/sample.controller.spec.ts delete mode 100644 server/src/api/sample/sample.controller.ts delete mode 100644 server/src/api/sample/sample.dto.ts delete mode 100644 server/src/api/sample/sample.entities.ts create mode 100644 server/src/models/migrations/20230907065110_init/migration.sql create mode 100644 server/src/models/migrations/migration_lock.toml create mode 100644 server/src/models/seed.ts create mode 100644 server/src/models/seeders/customer.seed.ts create mode 100644 server/src/models/seeders/estimation.ts create mode 100644 server/src/models/seeders/job.seed.ts create mode 100644 server/src/models/seeders/schedule.seed.ts create mode 100644 server/src/models/seeders/user.seed.ts diff --git a/server/.gitignore b/server/.gitignore index af40b5b..0938d32 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -36,3 +36,4 @@ lerna-debug.log* # Keep environment variables out of version control .env +/test diff --git a/server/package.json b/server/package.json index 0661ea4..d190922 100644 --- a/server/package.json +++ b/server/package.json @@ -17,7 +17,8 @@ "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json" + "test:e2e": "jest --config ./test/jest-e2e.json", + "seed": "ts-node src/models/seed.ts" }, "dependencies": { "@nestjs/common": "^10.0.0", @@ -31,10 +32,13 @@ "rxjs": "^7.8.1" }, "devDependencies": { + "@faker-js/faker": "^8.0.2", "@nestjs/cli": "^10.0.0", "@nestjs/schematics": "^10.0.0", + "@nestjs/swagger": "^7.1.10", "@nestjs/testing": "^10.0.0", "@types/express": "^4.17.17", + "@types/faker": "^6.6.9", "@types/jest": "^29.5.2", "@types/node": "^20.3.1", "@types/supertest": "^2.0.12", diff --git a/server/src/api/jobs/dto/job-list.dto.ts b/server/src/api/jobs/dto/job-list.dto.ts new file mode 100644 index 0000000..717769e --- /dev/null +++ b/server/src/api/jobs/dto/job-list.dto.ts @@ -0,0 +1,10 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Job } from '@prisma/client'; + +export class JobListDto { + @ApiProperty() + jobs: Job[]; + + @ApiProperty() + count: number; +} diff --git a/server/src/api/jobs/dto/job-query.dto.ts b/server/src/api/jobs/dto/job-query.dto.ts new file mode 100644 index 0000000..a8f26d5 --- /dev/null +++ b/server/src/api/jobs/dto/job-query.dto.ts @@ -0,0 +1,16 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsNumber, IsOptional } from 'class-validator'; + +export class JobQueryDto { + @ApiProperty() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + page: number; + + @ApiProperty({ required: false}) + @IsOptional() + @IsNumber() + @Transform(({ value }) => parseInt(value)) + perPage?: number; +} diff --git a/server/src/api/jobs/jobs.controller.spec.ts b/server/src/api/jobs/jobs.controller.spec.ts new file mode 100644 index 0000000..57d3453 --- /dev/null +++ b/server/src/api/jobs/jobs.controller.spec.ts @@ -0,0 +1,69 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { JobsController } from './jobs.controller'; +import { JobsService } from './jobs.service'; + +describe('JobsController', () => { + let jobController: JobsController; + + const mockJobService = { + findAll: jest.fn() + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [JobsController], + providers: [ + { + provide: JobsService, + useValue: mockJobService, + }, + ], + }).compile(); + + jobController = module.get(JobsController); + }); + + describe('findAll', () => { + it('should return a list of jobs with the total number of jobs', async () => { + const jobQuery = { page: 1, perPage: 2 }; + const jobList = { + jobs: [ + { + id: 1, + title: "Job A", + type: "Type A", + tags: [], + remarks: null, + customerId: 1, + paymentMethod: null, + userId: 1, + pipelinePhase: null, + createdAt: new Date("2023-09-07T09:38:42.296Z"), + updatedAt: new Date("2023-09-07T09:38:42.296Z"), + }, + { + id: 2, + title: "Job B", + type: "Type B", + tags: [], + remarks: null, + customerId: 2, + paymentMethod: null, + userId: 2, + pipelinePhase: null, + createdAt: new Date("2023-09-07T09:38:42.296Z"), + updatedAt: new Date("2023-09-07T09:38:42.296Z"), + } + ], + count: 2 + }; + + jest.spyOn(mockJobService, 'findAll').mockResolvedValue(jobList); + + const jobs = await jobController.findAll(jobQuery); + + expect(mockJobService.findAll).toHaveBeenCalledWith(jobQuery); + expect(jobs).toEqual(jobList); + }); + }); +}); diff --git a/server/src/api/jobs/jobs.controller.ts b/server/src/api/jobs/jobs.controller.ts new file mode 100644 index 0000000..4106a9c --- /dev/null +++ b/server/src/api/jobs/jobs.controller.ts @@ -0,0 +1,19 @@ +import { Controller, Get, Query, UsePipes, ValidationPipe } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { JobListDto } from './dto/job-list.dto'; +import { JobQueryDto } from './dto/job-query.dto'; +import { JobsService } from './jobs.service'; + +@ApiTags('jobs') +@Controller('jobs') +export class JobsController { + constructor(private readonly jobsService: JobsService) {} + + @Get() + @UsePipes(new ValidationPipe({ transform: true })) + async findAll(@Query() jobQuery: JobQueryDto): Promise { + const jobs = await this.jobsService.findAll(jobQuery); + + return jobs; + } +} diff --git a/server/src/api/jobs/jobs.module.ts b/server/src/api/jobs/jobs.module.ts new file mode 100644 index 0000000..7e16890 --- /dev/null +++ b/server/src/api/jobs/jobs.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { PrismaService } from 'src/database/connection.service'; +import { JobsController } from './jobs.controller'; +import { JobsService } from './jobs.service'; + +@Module({ + controllers: [JobsController], + providers: [JobsService, PrismaService], +}) +export class JobsModule {} diff --git a/server/src/api/jobs/jobs.service.spec.ts b/server/src/api/jobs/jobs.service.spec.ts new file mode 100644 index 0000000..dbd1f80 --- /dev/null +++ b/server/src/api/jobs/jobs.service.spec.ts @@ -0,0 +1,70 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { PrismaService } from '../../database/connection.service'; +import { JobsService } from './jobs.service'; + +describe('JobsService', () => { + let jobService: JobsService; + + const mockPrismaService = { + job: { + findMany: jest.fn(), + count: jest.fn(), + }, + $transaction: jest.fn() + }; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + JobsService, + { + provide: PrismaService, + useValue: mockPrismaService + } + ], + }).compile(); + + jobService = module.get(JobsService); + }); + + describe('findAll', () => { + it('should be return a list of jobs with the total number of jobs', async () => { + const jobQuery = { page: 1, perPage: 2 }; + const mockJobList = [ + { + id: 1, + title: "Job A", + type: "Type A", + tags: [], + remarks: null, + customerId: 1, + paymentMethod: null, + userId: 1, + pipelinePhase: null, + createdAt: new Date("2023-09-07T09:38:42.296Z"), + updatedAt: new Date("2023-09-07T09:38:42.296Z"), + }, + { + id: 2, + title: "Job B", + type: "Type B", + tags: [], + remarks: null, + customerId: 2, + paymentMethod: null, + userId: 2, + pipelinePhase: null, + createdAt: new Date("2023-09-07T09:38:42.296Z"), + updatedAt: new Date("2023-09-07T09:38:42.296Z"), + } + ]; + const mockJobCount = 2; + + mockPrismaService.$transaction.mockResolvedValue([ mockJobList, mockJobCount ]); + + const jobs = await jobService.findAll(jobQuery); + + expect(jobs).toEqual({ jobs: mockJobList, count: mockJobCount }); + }); + }); +}); diff --git a/server/src/api/jobs/jobs.service.ts b/server/src/api/jobs/jobs.service.ts new file mode 100644 index 0000000..0dc8e2d --- /dev/null +++ b/server/src/api/jobs/jobs.service.ts @@ -0,0 +1,56 @@ +import { BadRequestException, Injectable } from '@nestjs/common'; +import { PrismaService } from '../../database/connection.service'; +import { JobListDto } from './dto/job-list.dto'; +import { JobQueryDto } from './dto/job-query.dto'; + +@Injectable() +export class JobsService { + constructor(private prisma: PrismaService){} + + async findAll(query: JobQueryDto): Promise { + try { + const { page, perPage = 12 } = query; + const skip = (page - 1) * perPage; + + const [ jobs, count ] = await this.prisma.$transaction([ + this.prisma.job.findMany({ + take: perPage, + skip, + include: { + customer: { + select: { + firstName: true, + lastName: true, + } + }, + schedules: { + select: { + startDate: true, + startTime: true, + endDate: true, + endTime: true + } + }, + estimation: { + select: { + status: true, + totalCost: true, + } + }, + personInCharge: { + select: { + firstName: true, + lastName: true + } + } + } + }), + this.prisma.job.count() + ]); + + return { jobs, count }; + } catch (err) { + throw new BadRequestException('Something went wrong.'); + } + } +} diff --git a/server/src/api/sample/sample.controller.spec.ts b/server/src/api/sample/sample.controller.spec.ts deleted file mode 100644 index 3164a8c..0000000 --- a/server/src/api/sample/sample.controller.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { SampleController } from './sample.controller'; - -describe('SampleController', () => { - let controller: SampleController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [SampleController], - }).compile(); - - controller = module.get(SampleController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/server/src/api/sample/sample.controller.ts b/server/src/api/sample/sample.controller.ts deleted file mode 100644 index 3308a83..0000000 --- a/server/src/api/sample/sample.controller.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; - -@Controller() -export class SampleController { - @Get() - getHello(): string { - return '(SERVER) Hello World!'; - } -} diff --git a/server/src/api/sample/sample.dto.ts b/server/src/api/sample/sample.dto.ts deleted file mode 100644 index 42ff6bb..0000000 --- a/server/src/api/sample/sample.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IsString } from 'class-validator'; - -export class SampleUserDTO { - id: number; - - @IsString() - name: string; - - @IsString() - email: string; -} diff --git a/server/src/api/sample/sample.entities.ts b/server/src/api/sample/sample.entities.ts deleted file mode 100644 index 43baf1a..0000000 --- a/server/src/api/sample/sample.entities.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { SampleUser } from '@prisma/client'; - -export class SampleUserEntity implements SampleUser { - id: number; - name: string; - email: string; -} diff --git a/server/src/app.module.ts b/server/src/app.module.ts index 001246b..b830525 100644 --- a/server/src/app.module.ts +++ b/server/src/app.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; +import { JobsModule } from './api/jobs/jobs.module'; import { DatabaseModule } from './database/database.module'; -import { SampleController } from './api/sample/sample.controller'; @Module({ - imports: [DatabaseModule], - controllers: [SampleController], + imports: [DatabaseModule, JobsModule], + controllers: [], providers: [], }) export class AppModule {} diff --git a/server/src/main.ts b/server/src/main.ts index 8e348dc..8047b12 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -1,8 +1,19 @@ import { NestFactory } from '@nestjs/core'; +import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); + + const config = new DocumentBuilder() + .setTitle('Sim-JMS') + .setDescription('This is the API documentation for sim-jms') + .setVersion('1.0') + .build(); + + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api', app, document); + await app.listen(4000); } bootstrap(); diff --git a/server/src/models/migrations/20230907065110_init/migration.sql b/server/src/models/migrations/20230907065110_init/migration.sql new file mode 100644 index 0000000..4bc22b3 --- /dev/null +++ b/server/src/models/migrations/20230907065110_init/migration.sql @@ -0,0 +1,107 @@ +-- CreateEnum +CREATE TYPE "Role" AS ENUM ('USER', 'ADMIN'); + +-- CreateEnum +CREATE TYPE "Tag" AS ENUM ('TAG_A', 'TAG_B', 'TAG_C'); + +-- CreateEnum +CREATE TYPE "PaymentMethod" AS ENUM ('CASH', 'CARD', 'BANK_TRANSFER'); + +-- CreateEnum +CREATE TYPE "Status" AS ENUM ('NOT_YET_CREATED', 'MAKING', 'APPROVED', 'SENT_TO_CUSTOMER', 'CLOSED'); + +-- CreateEnum +CREATE TYPE "PipelinePhase" AS ENUM ('NEGOTIATION', 'DELIVERY'); + +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "firstName" TEXT NOT NULL, + "lastName" TEXT NOT NULL, + "email" TEXT NOT NULL, + "role" "Role" NOT NULL DEFAULT 'USER', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Job" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "type" TEXT NOT NULL, + "tags" "Tag"[], + "remarks" TEXT, + "customerId" INTEGER NOT NULL, + "paymentMethod" "PaymentMethod" NOT NULL DEFAULT 'CASH', + "userId" INTEGER NOT NULL, + "pipelinePhase" "PipelinePhase" NOT NULL DEFAULT 'NEGOTIATION', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Job_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Customer" ( + "id" SERIAL NOT NULL, + "firstName" TEXT NOT NULL, + "lastName" TEXT NOT NULL, + "email" TEXT NOT NULL, + "contact" TEXT NOT NULL, + "address" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Customer_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Schedule" ( + "id" SERIAL NOT NULL, + "startDate" TIMESTAMP(3) NOT NULL, + "endDate" TIMESTAMP(3) NOT NULL, + "startTime" TIMESTAMP(3) NOT NULL, + "endTime" TIMESTAMP(3) NOT NULL, + "jobId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Schedule_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Estimation" ( + "id" SERIAL NOT NULL, + "title" TEXT NOT NULL, + "document" TEXT NOT NULL, + "totalCost" DECIMAL(65,30) NOT NULL, + "status" "Status" NOT NULL, + "jobId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Estimation_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Job_title_customerId_key" ON "Job"("title", "customerId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Customer_email_key" ON "Customer"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Estimation_jobId_key" ON "Estimation"("jobId"); + +-- AddForeignKey +ALTER TABLE "Job" ADD CONSTRAINT "Job_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Job" ADD CONSTRAINT "Job_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Schedule" ADD CONSTRAINT "Schedule_jobId_fkey" FOREIGN KEY ("jobId") REFERENCES "Job"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Estimation" ADD CONSTRAINT "Estimation_jobId_fkey" FOREIGN KEY ("jobId") REFERENCES "Job"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/server/src/models/migrations/migration_lock.toml b/server/src/models/migrations/migration_lock.toml new file mode 100644 index 0000000..fbffa92 --- /dev/null +++ b/server/src/models/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/server/src/models/schema.prisma b/server/src/models/schema.prisma index 108018e..fa427aa 100644 --- a/server/src/models/schema.prisma +++ b/server/src/models/schema.prisma @@ -10,9 +10,99 @@ datasource db { url = env("DATABASE_URL") } -// Temporary model so that Prisma Client can initialize -model SampleUser { - id Int @id @default(autoincrement()) - name String - email String @unique +model User { + id Int @id @default(autoincrement()) + firstName String + lastName String + email String + role Role @default(USER) + jobs Job[] + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) +} + +model Job { + id Int @id @default(autoincrement()) + title String + type String + tags Tag[] + remarks String? + customer Customer @relation(fields: [customerId], references: [id]) + customerId Int + paymentMethod PaymentMethod @default(CASH) + personInCharge User @relation(fields: [userId], references: [id]) + userId Int + schedules Schedule[] + estimation Estimation? + pipelinePhase PipelinePhase @default(NEGOTIATION) + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) + + @@unique([title, customerId]) +} + +model Customer { + id Int @id @default(autoincrement()) + firstName String + lastName String + email String @unique + contact String + address String + jobs Job[] + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) +} + +model Schedule { + id Int @id @default(autoincrement()) + startDate DateTime + endDate DateTime + startTime DateTime + endTime DateTime + job Job @relation(fields: [jobId], references: [id]) + jobId Int + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) +} + +model Estimation { + id Int @id @default(autoincrement()) + title String + document String + totalCost Decimal + status Status + job Job @relation(fields: [jobId], references: [id]) + jobId Int @unique + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) +} + +enum Role { + USER + ADMIN +} + +enum Tag { + TAG_A + TAG_B + TAG_C +} + +enum PaymentMethod { + CASH + CARD + BANK_TRANSFER +} + +enum Status { + NOT_YET_CREATED + MAKING + APPROVED + SENT_TO_CUSTOMER + CLOSED +} + +enum PipelinePhase { + NEGOTIATION + DELIVERY } diff --git a/server/src/models/seed.ts b/server/src/models/seed.ts new file mode 100644 index 0000000..72958c7 --- /dev/null +++ b/server/src/models/seed.ts @@ -0,0 +1,28 @@ +import { PrismaClient } from "@prisma/client"; + +import seedCustomers from './seeders/customer.seed'; +import seedEstimations from './seeders/estimation'; +import seedJobs from './seeders/job.seed'; +import seedSchedules from './seeders/schedule.seed'; +import seedUsers from "./seeders/user.seed"; + +async function seed() { + const prisma = new PrismaClient(); + + try { + await prisma.$transaction(async () => { + await seedUsers(); + await seedCustomers(); + await seedJobs(); + await seedEstimations(); + await seedSchedules(); + }); + } catch (error) { + console.log('Error seeding database'); + throw error; + } finally { + await prisma.$disconnect(); + } +} + +seed(); diff --git a/server/src/models/seeders/customer.seed.ts b/server/src/models/seeders/customer.seed.ts new file mode 100644 index 0000000..feaebf5 --- /dev/null +++ b/server/src/models/seeders/customer.seed.ts @@ -0,0 +1,24 @@ +import { faker } from '@faker-js/faker'; +import { PrismaClient } from '@prisma/client'; + +export default async function seedCustomers() { + const prisma = new PrismaClient(); + + const seedDataCount = 20; + let customerData = []; + + for (let i = 0; i < seedDataCount; i ++) { + const newCustomer ={ + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: faker.internet.email(), + contact: faker.phone.number('+63 9# ### ## ##'), + address: faker.location.streetAddress(true) + } + customerData = [...customerData, newCustomer] + } + + await prisma.customer.createMany({ + data: customerData + }) +} diff --git a/server/src/models/seeders/estimation.ts b/server/src/models/seeders/estimation.ts new file mode 100644 index 0000000..0474b18 --- /dev/null +++ b/server/src/models/seeders/estimation.ts @@ -0,0 +1,25 @@ +import { faker } from '@faker-js/faker'; +import { PrismaClient, Status } from '@prisma/client'; + +export default async function seedEstimations() { + const prisma = new PrismaClient(); + + const status = Object.values(Status); + const seedDataCount = 20 + let estimationData = []; + + for (let i = 0; i < seedDataCount; i ++) { + const newEstimation = { + title: faker.lorem.word(), + document: faker.system.commonFileName('pdf'), + totalCost: faker.finance.amount({min: 1000, max: 10000, dec: 2}), + status: faker.helpers.arrayElement(status), + jobId: i + 1, + } + estimationData = [...estimationData, newEstimation] + } + + await prisma.estimation.createMany({ + data: estimationData + }) +} diff --git a/server/src/models/seeders/job.seed.ts b/server/src/models/seeders/job.seed.ts new file mode 100644 index 0000000..6156395 --- /dev/null +++ b/server/src/models/seeders/job.seed.ts @@ -0,0 +1,27 @@ +import { faker } from '@faker-js/faker'; +import { PaymentMethod, PrismaClient, Tag } from '@prisma/client'; + +export default async function seedJobs() { + const prisma = new PrismaClient(); + + const paymentMethod = Object.values(PaymentMethod); + const tags = Object.values(Tag); + const seedDataCount = 20 + let jobData = []; + + for (let i = 0; i < seedDataCount; i ++) { + const newJob = { + title: faker.lorem.words(), + type: faker.lorem.word(), + userId: faker.number.int({ min: 1, max: 5 }), + customerId: i + 1, + paymentMethod: faker.helpers.arrayElement(paymentMethod), + tags: faker.helpers.arrayElements(tags, { min: 1, max: 3 } ) + } + jobData = [...jobData, newJob] + } + + await prisma.job.createMany({ + data: jobData + }) +} diff --git a/server/src/models/seeders/schedule.seed.ts b/server/src/models/seeders/schedule.seed.ts new file mode 100644 index 0000000..c797bbf --- /dev/null +++ b/server/src/models/seeders/schedule.seed.ts @@ -0,0 +1,24 @@ +import { faker } from '@faker-js/faker'; +import { PrismaClient } from '@prisma/client'; + +export default async function seedSchedules() { + const prisma = new PrismaClient(); + + const seedDataCount = 20; + let scheduleData = []; + + for (let i = 0; i < seedDataCount; i++) { + const newSchedule = { + startDate: faker.date.soon(), + endDate: faker.date.soon(), + startTime: faker.date.soon(), + endTime: faker.date.soon(), + jobId: faker.number.int({ min: 1, max: 20 }) + } + scheduleData = [...scheduleData, newSchedule] + } + + await prisma.schedule.createMany({ + data: scheduleData + }); +} diff --git a/server/src/models/seeders/user.seed.ts b/server/src/models/seeders/user.seed.ts new file mode 100644 index 0000000..31db144 --- /dev/null +++ b/server/src/models/seeders/user.seed.ts @@ -0,0 +1,22 @@ +import { faker } from '@faker-js/faker'; +import { PrismaClient } from '@prisma/client'; + +export default async function seedUsers() { + const prisma = new PrismaClient(); + + const seedDataCount = 5; + let userData = []; + + for (let i = 0; i < seedDataCount; i++) { + const newUser = { + firstName: faker.person.firstName(), + lastName: faker.person.lastName(), + email: faker.internet.email(), + } + userData = [...userData, newUser] + } + + await prisma.user.createMany({ + data: userData + }) +} diff --git a/server/yarn.lock b/server/yarn.lock index d91c693..2b04a6b 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -402,6 +402,11 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.46.0.tgz#3f7802972e8b6fe3f88ed1aabc74ec596c456db6" integrity sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA== +"@faker-js/faker@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-8.0.2.tgz#bab698c5d3da9c52744e966e0e3eedb6c8b05c37" + integrity sha512-Uo3pGspElQW91PCvKSIAXoEgAUlRnH29sX2/p89kg7sP1m2PzCufHINd0FhTXQf6DYGiUlVncdSPa2F9wxed2A== + "@humanwhocodes/config-array@^0.11.10": version "0.11.10" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" @@ -731,6 +736,11 @@ path-to-regexp "3.2.0" tslib "2.6.1" +"@nestjs/mapped-types@2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.2.tgz#c8a090a8d22145b85ed977414c158534210f2e4f" + integrity sha512-V0izw6tWs6fTp9+KiiPUbGHWALy563Frn8X6Bm87ANLRuE46iuBMD5acKBDP5lKL/75QFvrzSJT7HkCbB0jTpg== + "@nestjs/platform-express@^10.0.0": version "10.1.3" resolved "https://registry.yarnpkg.com/@nestjs/platform-express/-/platform-express-10.1.3.tgz#d1f644e86f2bc45c7529b9eed7669613f4392e99" @@ -753,6 +763,17 @@ jsonc-parser "3.2.0" pluralize "8.0.0" +"@nestjs/swagger@^7.1.10": + version "7.1.10" + resolved "https://registry.yarnpkg.com/@nestjs/swagger/-/swagger-7.1.10.tgz#955deb9c428fae779d2988a0d24a55977a7be11d" + integrity sha512-qreCcxgHFyFX1mOfK36pxiziy4xoa/XcxC0h4Zr9yH54WuqMqO9aaNFhFyuQ1iyd/3YBVQB21Un4gQnh9iGm0w== + dependencies: + "@nestjs/mapped-types" "2.0.2" + js-yaml "4.1.0" + lodash "4.17.21" + path-to-regexp "3.2.0" + swagger-ui-dist "5.4.2" + "@nestjs/testing@^10.0.0": version "10.1.3" resolved "https://registry.yarnpkg.com/@nestjs/testing/-/testing-10.1.3.tgz#596a1dc580373b3b0b070ac73bf70e45f80197dd" @@ -940,6 +961,13 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/faker@^6.6.9": + version "6.6.9" + resolved "https://registry.yarnpkg.com/@types/faker/-/faker-6.6.9.tgz#1064e7c46be58388fa326e2f918a4f02ab740a7a" + integrity sha512-Y9YYm5L//8ooiiknO++4Gr539zzdI0j3aXnOBjo1Vk+kTvffY10GuE2wn78AFPECwZ5MYGTjiDVw1naLLdDimw== + dependencies: + faker "*" + "@types/graceful-fs@^4.1.3": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" @@ -2318,6 +2346,11 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" +faker@*: + version "6.6.6" + resolved "https://registry.yarnpkg.com/faker/-/faker-6.6.6.tgz#e9529da0109dca4c7c5dbfeaadbd9234af943033" + integrity sha512-9tCqYEDHI5RYFQigXFwF1hnCwcWCOJl/hmll0lr5D2Ljjb0o4wphb69wikeJDz5qCEzXCoPvG6ss5SDP6IfOdg== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -3290,6 +3323,13 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-yaml@4.1.0, js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" @@ -3298,13 +3338,6 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -3406,7 +3439,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.21: +lodash@4.17.21, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4406,6 +4439,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +swagger-ui-dist@5.4.2: + version "5.4.2" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.4.2.tgz#ff7b936bdfc84673a1823a0f05f3a933ba7ccd4c" + integrity sha512-vT5QxP/NOr9m4gLZl+SpavWI3M9Fdh30+Sdw9rEtZbkqNmNNEPhjXas2xTD9rsJYYdLzAiMfwXvtooWH3xbLJA== + symbol-observable@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"