From aa4dd5f3ac8fe405632171691265dd136fba7a24 Mon Sep 17 00:00:00 2001 From: Elvin Cheng Date: Sun, 9 Feb 2025 14:49:44 -0500 Subject: [PATCH] Implement signing field groups and assigned groups (#273) * Implement signing field groups and assigned groups * Refactor seeding * Fix lint * Fix frontend * eslint * Add field type * fix tsc * Fix test * Remove metadata? --- .../migration.sql | 133 +++ apps/server/prisma/schema.prisma | 191 +-- apps/server/prisma/seed.ts | 1062 ++++++++--------- apps/server/prisma/seed.types.ts | 68 ++ apps/server/prisma/seeder.ts | 345 ++++++ apps/server/src/app.module.ts | 6 +- .../assigned-group.controller.ts | 19 + .../assigned-group/assigned-group.errors.ts | 8 + .../assigned-group.module.ts} | 23 +- .../assigned-group.service.spec.ts} | 126 +- .../assigned-group/assigned-group.service.ts | 201 ++++ .../dto/create-assigned-group.dto.ts} | 34 +- .../dto/create-instance-box.dto.ts | 14 + .../dto/update-assigned-group-signer.dto.ts | 6 + .../entities/assigned-group.entity.ts | 109 ++ .../server/src/employees/employees.service.ts | 2 +- .../field-group/dto/create-field-group.dto.ts | 29 + .../dto/create-template-box.dto.ts | 29 + .../entities/field-group.entity.ts | 39 + .../entities/template-box.entity.ts | 30 + .../dto/create-form-instance.dto.ts | 6 +- .../dto/update-form-instance.dto.ts | 2 +- .../entities/form-instance.entity.ts | 14 +- .../form-instances/form-instance.errors.ts | 2 +- .../form-instances.controller.ts | 4 +- .../form-instances.service.spec.ts | 74 +- .../form-instances/form-instances.service.ts | 180 ++- .../dto/create-form-template.dto.ts | 6 +- .../dto/update-form-template.dto.ts | 2 +- .../entities/form-template.entity.ts | 22 +- .../form-templates/form-templates.service.ts | 84 +- apps/server/src/metadata.ts | 525 -------- .../src/positions/entities/position.entity.ts | 13 +- .../dto/create-signature-field.dto.ts | 18 - .../dto/update-signature-field.dto.ts | 6 - .../entities/signature-field.entity.ts | 27 - .../signature-fields.controller.spec.ts | 187 --- .../signature-fields.controller.ts | 138 --- .../signature-fields.errors.ts | 4 - .../signature-fields.module.ts | 12 - .../signature-fields.service.spec.ts | 277 ----- .../signature-fields.service.ts | 79 -- .../dto/update-signature-signer.dto.ts | 6 - .../signatures/dto/update-signature.dto.ts | 4 - .../signatures/entities/signature.entity.ts | 77 -- .../signatures/signatures.controller.spec.ts | 67 -- .../src/signatures/signatures.controller.ts | 16 - .../src/signatures/signatures.errors.ts | 8 - .../src/signatures/signatures.service.ts | 175 --- .../src/client/@tanstack/react-query.gen.ts | 277 ++--- apps/web/src/client/schemas.gen.ts | 387 +++--- apps/web/src/client/sdk.gen.ts | 191 +-- apps/web/src/client/types.gen.ts | 412 +++---- apps/web/src/components/AvatarMap.tsx | 2 - apps/web/src/components/FormCard.tsx | 24 +- apps/web/src/components/FormInstance.tsx | 22 +- apps/web/src/components/FormRow.tsx | 22 +- apps/web/src/components/OverviewRow.tsx | 2 +- .../CreateFormInstanceModal.tsx | 40 +- .../createFormInstance/SignatureDropdown.tsx | 35 +- .../components/createFormInstance/types.ts | 12 +- .../CreateFormTemplateModal.tsx | 67 +- .../{SignatureField.tsx => FieldGroup.tsx} | 42 +- .../FormTemplateButtons.tsx | 17 +- .../components/createFormTemplate/types.ts | 2 +- apps/web/src/hooks/useForm.ts | 10 +- apps/web/src/utils/formInstanceUtils.ts | 100 +- tsconfig.json | 1 - 68 files changed, 2720 insertions(+), 3454 deletions(-) create mode 100644 apps/server/prisma/migrations/20250209013341_implement_signing_group/migration.sql create mode 100644 apps/server/prisma/seed.types.ts create mode 100644 apps/server/prisma/seeder.ts create mode 100644 apps/server/src/assigned-group/assigned-group.controller.ts create mode 100644 apps/server/src/assigned-group/assigned-group.errors.ts rename apps/server/src/{signatures/signatures.module.ts => assigned-group/assigned-group.module.ts} (50%) rename apps/server/src/{signatures/signatures.service.spec.ts => assigned-group/assigned-group.service.spec.ts} (70%) create mode 100644 apps/server/src/assigned-group/assigned-group.service.ts rename apps/server/src/{signatures/dto/create-signature.dto.ts => assigned-group/dto/create-assigned-group.dto.ts} (73%) create mode 100644 apps/server/src/assigned-group/dto/create-instance-box.dto.ts create mode 100644 apps/server/src/assigned-group/dto/update-assigned-group-signer.dto.ts create mode 100644 apps/server/src/assigned-group/entities/assigned-group.entity.ts create mode 100644 apps/server/src/field-group/dto/create-field-group.dto.ts create mode 100644 apps/server/src/field-group/dto/create-template-box.dto.ts create mode 100644 apps/server/src/field-group/entities/field-group.entity.ts create mode 100644 apps/server/src/field-group/entities/template-box.entity.ts delete mode 100644 apps/server/src/metadata.ts delete mode 100644 apps/server/src/signature-fields/dto/create-signature-field.dto.ts delete mode 100644 apps/server/src/signature-fields/dto/update-signature-field.dto.ts delete mode 100644 apps/server/src/signature-fields/entities/signature-field.entity.ts delete mode 100644 apps/server/src/signature-fields/signature-fields.controller.spec.ts delete mode 100644 apps/server/src/signature-fields/signature-fields.controller.ts delete mode 100644 apps/server/src/signature-fields/signature-fields.errors.ts delete mode 100644 apps/server/src/signature-fields/signature-fields.module.ts delete mode 100644 apps/server/src/signature-fields/signature-fields.service.spec.ts delete mode 100644 apps/server/src/signature-fields/signature-fields.service.ts delete mode 100644 apps/server/src/signatures/dto/update-signature-signer.dto.ts delete mode 100644 apps/server/src/signatures/dto/update-signature.dto.ts delete mode 100644 apps/server/src/signatures/entities/signature.entity.ts delete mode 100644 apps/server/src/signatures/signatures.controller.spec.ts delete mode 100644 apps/server/src/signatures/signatures.controller.ts delete mode 100644 apps/server/src/signatures/signatures.errors.ts delete mode 100644 apps/server/src/signatures/signatures.service.ts rename apps/web/src/components/createFormTemplate/{SignatureField.tsx => FieldGroup.tsx} (68%) diff --git a/apps/server/prisma/migrations/20250209013341_implement_signing_group/migration.sql b/apps/server/prisma/migrations/20250209013341_implement_signing_group/migration.sql new file mode 100644 index 00000000..3153ba3b --- /dev/null +++ b/apps/server/prisma/migrations/20250209013341_implement_signing_group/migration.sql @@ -0,0 +1,133 @@ +/* + Warnings: + + - You are about to drop the `Signature` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `SignatureField` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- CreateEnum +CREATE TYPE "SignatureBoxFieldType" AS ENUM ('SIGNATURE', 'CHECKBOX', 'TEXT_FIELD'); + +-- DropForeignKey +ALTER TABLE "Signature" DROP CONSTRAINT "Signature_employeeId_fkey"; + +-- DropForeignKey +ALTER TABLE "Signature" DROP CONSTRAINT "Signature_formInstanceId_fkey"; + +-- DropForeignKey +ALTER TABLE "Signature" DROP CONSTRAINT "Signature_signerDepartmentId_fkey"; + +-- DropForeignKey +ALTER TABLE "Signature" DROP CONSTRAINT "Signature_signerEmployeeId_fkey"; + +-- DropForeignKey +ALTER TABLE "Signature" DROP CONSTRAINT "Signature_signerPositionId_fkey"; + +-- DropForeignKey +ALTER TABLE "Signature" DROP CONSTRAINT "Signature_signingEmployeeId_fkey"; + +-- DropForeignKey +ALTER TABLE "SignatureField" DROP CONSTRAINT "SignatureField_formTemplateId_fkey"; + +-- DropForeignKey +ALTER TABLE "_signerEmployeeList" DROP CONSTRAINT "_signerEmployeeList_A_fkey"; + +-- DropForeignKey +ALTER TABLE "_signerEmployeeList" DROP CONSTRAINT "_signerEmployeeList_B_fkey"; + +-- DropTable +DROP TABLE "Signature"; + +-- DropTable +DROP TABLE "SignatureField"; + +-- CreateTable +CREATE TABLE "FieldGroup" ( + "id" UUID NOT NULL, + "name" VARCHAR(255) NOT NULL, + "order" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "formTemplateId" UUID NOT NULL, + + CONSTRAINT "FieldGroup_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "TemplateBox" ( + "id" UUID NOT NULL, + "type" "SignatureBoxFieldType" NOT NULL, + "x_coordinate" INTEGER NOT NULL, + "y_coordinate" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "fieldGroupId" UUID NOT NULL, + + CONSTRAINT "TemplateBox_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "AssignedGroup" ( + "id" UUID NOT NULL, + "order" INTEGER NOT NULL, + "signed" BOOLEAN NOT NULL DEFAULT false, + "signedDocLink" VARCHAR(255), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "signerPositionId" UUID, + "signerDepartmentId" UUID, + "signerEmployeeId" UUID, + "signingEmployeeId" UUID, + "signerType" "SignerType" NOT NULL, + "formInstanceId" UUID NOT NULL, + "fieldGroupId" UUID NOT NULL, + + CONSTRAINT "AssignedGroup_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "InstanceBox" ( + "id" UUID NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "assignedGroupId" UUID NOT NULL, + "templateBoxId" UUID NOT NULL, + + CONSTRAINT "InstanceBox_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "FieldGroup" ADD CONSTRAINT "FieldGroup_formTemplateId_fkey" FOREIGN KEY ("formTemplateId") REFERENCES "FormTemplate"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "TemplateBox" ADD CONSTRAINT "TemplateBox_fieldGroupId_fkey" FOREIGN KEY ("fieldGroupId") REFERENCES "FieldGroup"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "AssignedGroup" ADD CONSTRAINT "AssignedGroup_signerPositionId_fkey" FOREIGN KEY ("signerPositionId") REFERENCES "Position"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "AssignedGroup" ADD CONSTRAINT "AssignedGroup_signerDepartmentId_fkey" FOREIGN KEY ("signerDepartmentId") REFERENCES "Department"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "AssignedGroup" ADD CONSTRAINT "AssignedGroup_signerEmployeeId_fkey" FOREIGN KEY ("signerEmployeeId") REFERENCES "Employee"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "AssignedGroup" ADD CONSTRAINT "AssignedGroup_signingEmployeeId_fkey" FOREIGN KEY ("signingEmployeeId") REFERENCES "Employee"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "AssignedGroup" ADD CONSTRAINT "AssignedGroup_formInstanceId_fkey" FOREIGN KEY ("formInstanceId") REFERENCES "FormInstance"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "AssignedGroup" ADD CONSTRAINT "AssignedGroup_fieldGroupId_fkey" FOREIGN KEY ("fieldGroupId") REFERENCES "FieldGroup"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "InstanceBox" ADD CONSTRAINT "InstanceBox_assignedGroupId_fkey" FOREIGN KEY ("assignedGroupId") REFERENCES "AssignedGroup"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "InstanceBox" ADD CONSTRAINT "InstanceBox_templateBoxId_fkey" FOREIGN KEY ("templateBoxId") REFERENCES "TemplateBox"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_signerEmployeeList" ADD CONSTRAINT "_signerEmployeeList_A_fkey" FOREIGN KEY ("A") REFERENCES "AssignedGroup"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_signerEmployeeList" ADD CONSTRAINT "_signerEmployeeList_B_fkey" FOREIGN KEY ("B") REFERENCES "Employee"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/apps/server/prisma/schema.prisma b/apps/server/prisma/schema.prisma index 1510271e..afbc64cb 100644 --- a/apps/server/prisma/schema.prisma +++ b/apps/server/prisma/schema.prisma @@ -18,6 +18,12 @@ enum SignerType { USER_LIST } +enum SignatureBoxFieldType { + SIGNATURE + CHECKBOX + TEXT_FIELD +} + enum EmployeeScope { BASE_USER ADMIN @@ -30,36 +36,11 @@ model Department { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - signatures Signature[] + assignedGroups AssignedGroup[] positions Position[] } -// `Employees` represent the people who work at the MFA. -// -// Each `Employee` corresponds to a user who holds a certain position. -model Employee { - id String @id @default(uuid()) @db.Uuid - firstName String @db.VarChar(255) - lastName String @db.VarChar(255) - email String @unique @db.VarChar(255) - signatureLink String @db.VarChar(255) - scope EmployeeScope @default(BASE_USER) - pswdHash String? @db.VarChar(255) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - refreshToken String? @db.Text() - - originatedForms FormInstance[] - - position Position @relation(fields: [positionId], references: [id]) - positionId String @db.Uuid - - signerEmployeeSignatures Signature[] @relation("signerEmployee") - signerEmployeeListSignatures Signature[] @relation("signerEmployeeList") - signingEmployeeSignatures Signature[] @relation("signingEmployee") -} - // `Positions` represent the various positions that employees at the MFA can hold. model Position { id String @id @default(uuid()) @db.Uuid @@ -68,8 +49,8 @@ model Position { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - signatures Signature[] - employees Employee[] + assignedGroups AssignedGroup[] + employees Employee[] department Department @relation(fields: [departmentId], references: [id]) departmentId String @db.Uuid @@ -77,25 +58,110 @@ model Position { @@unique([name, departmentId]) } -// `SignatureFields` represent each signature field that is required on a given form. -// A field may have a position specified, indicating that when a user creates an -// instance of the corresponding form, the field should default to the specified position. +// `Employees` represent the people who work at the MFA. +// +// Each `Employee` corresponds to a user who holds a certain position. +model Employee { + id String @id @default(uuid()) @db.Uuid + firstName String @db.VarChar(255) + lastName String @db.VarChar(255) + email String @unique @db.VarChar(255) + signatureLink String @db.VarChar(255) + scope EmployeeScope @default(BASE_USER) + pswdHash String? @db.VarChar(255) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + refreshToken String? @db.Text() + + originatedForms FormInstance[] + + position Position @relation(fields: [positionId], references: [id]) + positionId String @db.Uuid + + signerEmployeeAssignedGroups AssignedGroup[] @relation("signerEmployee") + signerEmployeeListAssignedGroups AssignedGroup[] @relation("signerEmployeeList") + signingEmployeeAssignedGroup AssignedGroup[] @relation("signingEmployee") +} + +// A `FormTemplate` is a reference for a form that is used when creating forms initiated by users. +model FormTemplate { + id String @id @default(uuid()) @db.Uuid + name String @db.VarChar(255) + formDocLink String @db.VarChar(255) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + fieldGroups FieldGroup[] + formInstances FormInstance[] +} + +// `FieldGroups` represent a group of signature fields to be assigned together +// that are required on a given form. +// When new form instances are created, new `AssignedGroups` are also created +// based on the `FieldGroups` found on a form template. // -// Each `SignatureField` also specifies its order on the form it is on. -// `SignatureFields` are used as subdocuments within `FormTemplates`. -model SignatureField { +// Each `FieldGroup` also specifies its order on the form it is on. +// `FieldGroups` are within `FormTemplates`. +model FieldGroup { id String @id @default(uuid()) @db.Uuid name String @db.VarChar(255) order Int @db.Integer createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + formTemplate FormTemplate @relation(fields: [formTemplateId], references: [id]) + formTemplateId String @db.Uuid + templateBoxes TemplateBox[] + assignedGroups AssignedGroup[] +} + +// `TemplateBox` represent the actual signature boxes that are to be filled by the employees. +model TemplateBox { + id String @id @default(uuid()) @db.Uuid + type SignatureBoxFieldType + x_coordinate Int @db.Integer + y_coordinate Int @db.Integer + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + fieldGroup FieldGroup @relation(fields: [fieldGroupId], references: [id]) + fieldGroupId String @db.Uuid + InstanceBox InstanceBox[] +} + +// `FormInstances` represent instances of forms created by employees. +// +// They are created based on a given `FormTemplate`, and contain a +// list of `Signatures` that are to be filled by the requested employees. +// +// A `FormInstance` should be marked completed when all of its signatures +// have been collected and marked as completed. +model FormInstance { + id String @id @default(uuid()) @db.Uuid + name String @db.VarChar(255) + formDocLink String @db.VarChar(255) + completed Boolean @default(false) + markedCompleted Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + completedAt DateTime? + markedCompletedAt DateTime? + + assignedGroups AssignedGroup[] + + originator Employee @relation(fields: [originatorId], references: [id]) + originatorId String @db.Uuid formTemplate FormTemplate @relation(fields: [formTemplateId], references: [id]) formTemplateId String @db.Uuid } -// `Signatures` represent the signatures required on a form. Each signature has a -// specified signer and its corresponding status which indicates if it has been signed. +// `AssignedGroups` represent groups of signatures required on a form to be signed by a +// specific employee, position, department, or an employee from a list. Each `AssignedGroup` has a +// corresponding status which indicates if it has been signed. +// +// `AssignedGroups` are derived from `FieldGroups` on a `FormTemplate` and are +// created when a new form instance is created. Each `AssignedGroup` has corresponding `InstanceBoxes` +// which are the actual signature boxes that are to be filled by the employees. // // A `Signature` will also carry over its order from the original `FormTemplate` order // from which the `FormInstance` that the `Signature` belongs to was derived. If a `Signature` @@ -104,7 +170,7 @@ model SignatureField { // // The signer position and the user signed by are populated when the signature has been // completed, and are derived from the person who signed at that moment in time. -model Signature { +model AssignedGroup { id String @id @default(uuid()) @db.Uuid order Int @db.Integer signed Boolean @default(false) @@ -123,44 +189,21 @@ model Signature { signingEmployeeId String? @db.Uuid signerType SignerType - formInstance FormInstance @relation(fields: [formInstanceId], references: [id]) - formInstanceId String @db.Uuid -} - -// `FormInstances` represent instances of forms created by employees. -// -// They are created based on a given `FormTemplate`, and contain a -// list of `Signatures` that are to be filled by the requested employees. -// -// A `FormInstance` should be marked completed when all of its signatures -// have been collected and marked as completed. -model FormInstance { - id String @id @default(uuid()) @db.Uuid - name String @db.VarChar(255) - formDocLink String @db.VarChar(255) - completed Boolean @default(false) - markedCompleted Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - completedAt DateTime? - markedCompletedAt DateTime? - - signatures Signature[] - - originator Employee @relation(fields: [originatorId], references: [id]) - originatorId String @db.Uuid - formTemplate FormTemplate @relation(fields: [formTemplateId], references: [id]) - formTemplateId String @db.Uuid + formInstance FormInstance @relation(fields: [formInstanceId], references: [id]) + formInstanceId String @db.Uuid + instanceBoxes InstanceBox[] + fieldGroup FieldGroup @relation(fields: [fieldGroupId], references: [id]) + fieldGroupId String @db.Uuid } -// A `FormTemplate` is a reference for a form that is used when creating forms initiated by users. -model FormTemplate { - id String @id @default(uuid()) @db.Uuid - name String @db.VarChar(255) - formDocLink String @db.VarChar(255) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt +// `InstanceBox` represent the signature boxes on an instantiated form that are to be filled by the employees. +model InstanceBox { + id String @id @default(uuid()) @db.Uuid + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt - signatureFields SignatureField[] - formInstances FormInstance[] + assignedGroup AssignedGroup @relation(fields: [assignedGroupId], references: [id]) + assignedGroupId String @db.Uuid + templateBox TemplateBox @relation(fields: [templateBoxId], references: [id]) + templateBoxId String @db.Uuid } diff --git a/apps/server/prisma/seed.ts b/apps/server/prisma/seed.ts index 9560cc61..e001f0eb 100644 --- a/apps/server/prisma/seed.ts +++ b/apps/server/prisma/seed.ts @@ -1,34 +1,35 @@ -import { PrismaClient, SignerType, EmployeeScope } from '@prisma/client'; +import { + SignatureBoxFieldType, + SignerType, + EmployeeScope, +} from '@prisma/client'; import { v4 as uuidv4 } from 'uuid'; - -const prisma = new PrismaClient(); - -/* -Hard coded UUIDs for: -- leadership team department -- chief of staff position -- chief financial officer position -- agg director position -- chief learning engagement position -- chief signature field -- manager signature field -- director signature field -*/ - +import { + DepartmentData, + EmployeeData, + FormInstanceData, + FormTemplateData, + PositionData, +} from './seed.types'; +import { Seeder } from './seeder'; + +// department ids const LEADERSHIP_TEAM_UUID = uuidv4(); +const TEST_TEAM_UUID = uuidv4(); + +// position ids const CHIEF_OF_STAFF_UUID = uuidv4(); const CHIEF_FIN_OFFICER_UUID = uuidv4(); const AGG_DIR_UUID = uuidv4(); const CHIEF_LEARNING_ENGAGEMENT_UUID = uuidv4(); -const TEST_TEAM_UUID = uuidv4(); -const TEST_TEAM_POSITION_UUID = uuidv4(); - +// employee ids const ANSHUL_SHIRUDE_UUID = uuidv4(); const ANGELA_WEIGL_UUID = uuidv4(); const KAI_ZHENG_UUID = uuidv4(); const IRIS_ZHANG_UUID = uuidv4(); +// form template ids const IT_EXIT_FORM_UUID = uuidv4(); const STAFFING_REQUISITION_UUID = uuidv4(); const NETWORK_ADD_CHANGE_UUID = uuidv4(); @@ -38,578 +39,469 @@ const VPN_REQUEST_UUID = uuidv4(); const DIGITAL_MFA_ID_BADGE_REQUEST_UUID = uuidv4(); const TRAVEL_AUTHORIZATION_UUID = uuidv4(); +// field group ids +const IT_EXIT_FORM_FIELD_GROUP_0_UUID = uuidv4(); +const IT_EXIT_FORM_FIELD_GROUP_1_UUID = uuidv4(); +const STAFFING_REQUISITION_FIELD_GROUP_0_UUID = uuidv4(); +const STAFFING_REQUISITION_FIELD_GROUP_1_UUID = uuidv4(); +const NETWORK_ADD_CHANGE_FIELD_GROUP_0_UUID = uuidv4(); +const MFA_ORACLE_LOGON_REQUEST_FIELD_GROUP_0_UUID = uuidv4(); +const MFA_ORACLE_LOGON_REQUEST_FIELD_GROUP_1_UUID = uuidv4(); +const HYBRID_AND_REMOTE_WORK_AGREEMENT_FIELD_GROUP_0_UUID = uuidv4(); +const VPN_REQUEST_FIELD_GROUP_0_UUID = uuidv4(); +const DIGITAL_MFA_ID_BADGE_REQUEST_FIELD_GROUP_0_UUID = uuidv4(); +const TRAVEL_AUTHORIZATION_FIELD_GROUP_0_UUID = uuidv4(); + +// links const DEV_FORM_DOC_LINK = 'http://localhost:3002/test.pdf'; const DEV_SIGNATURE_LINK = 'http://localhost:3002/signature.png'; -// type definition for employee data used in upsertEmployee -type EmployeeData = { - id: string; - firstName: string; - lastName: string; - email: string; - positionId: string; - signatureLink: string; - scope: EmployeeScope; -}; - -// update or insert employee to database based on the employee id -async function upsertEmployee(empData: EmployeeData) { - await prisma.employee.upsert({ - where: { id: empData.id }, - update: {}, - create: { - id: empData.id, - firstName: empData.firstName, - lastName: empData.lastName, - email: empData.email, - signatureLink: empData.signatureLink, - position: { - connect: { id: empData.positionId }, +const departments: DepartmentData[] = [ + { + id: LEADERSHIP_TEAM_UUID, + name: 'Leadership Team', + }, + { + id: TEST_TEAM_UUID, + name: 'Test Team', + }, +]; + +const positions: PositionData[] = [ + { + id: CHIEF_OF_STAFF_UUID, + name: 'Chief of Staff', + departmentId: LEADERSHIP_TEAM_UUID, + }, + { + id: CHIEF_FIN_OFFICER_UUID, + name: 'Chief Financial Officer', + departmentId: LEADERSHIP_TEAM_UUID, + }, + { + id: AGG_DIR_UUID, + name: 'AGG Director', + departmentId: LEADERSHIP_TEAM_UUID, + }, + { + id: CHIEF_LEARNING_ENGAGEMENT_UUID, + name: 'Chief of Learning & Community Engagement', + departmentId: LEADERSHIP_TEAM_UUID, + }, +]; + +const employees: EmployeeData[] = [ + { + id: IRIS_ZHANG_UUID, + firstName: 'Iris', + lastName: 'Zhang', + email: 'zhang.iri@northeastern.edu', + positionId: CHIEF_OF_STAFF_UUID, + signatureLink: DEV_SIGNATURE_LINK, + scope: EmployeeScope.BASE_USER, + }, + { + id: KAI_ZHENG_UUID, + firstName: 'Kai', + lastName: 'Zheng', + email: 'zheng.kaiy@northeastern.edu', + positionId: CHIEF_FIN_OFFICER_UUID, + signatureLink: DEV_SIGNATURE_LINK, + scope: EmployeeScope.BASE_USER, + }, + { + id: ANGELA_WEIGL_UUID, + firstName: 'Angela', + lastName: 'Weigl', + email: 'weigl.a@northeastern.edu', + positionId: AGG_DIR_UUID, + signatureLink: DEV_SIGNATURE_LINK, + scope: EmployeeScope.BASE_USER, + }, + { + id: ANSHUL_SHIRUDE_UUID, + firstName: 'Anshul', + lastName: 'Shirude', + email: 'shirude.a@northeastern.edu', + positionId: CHIEF_LEARNING_ENGAGEMENT_UUID, + signatureLink: DEV_SIGNATURE_LINK, + scope: EmployeeScope.BASE_USER, + }, +]; + +const formTemplates: FormTemplateData[] = [ + { + id: IT_EXIT_FORM_UUID, + name: 'IT Exit Form', + formDocLink: DEV_FORM_DOC_LINK, + fieldGroups: [ + { + id: IT_EXIT_FORM_FIELD_GROUP_0_UUID, + name: 'Chief', + order: 0, + templateBoxes: [ + { + id: uuidv4(), + type: SignatureBoxFieldType.SIGNATURE, + x_coordinate: 0, + y_coordinate: 0, + }, + ], }, - scope: empData.scope, - }, - }); -} - -// type definition for position data used in upsertPosition -type PositionData = { - id: string; - name: string; - departmentId: string; -}; - -// update or insert position into database based on position id -async function upsertPosition(data: PositionData) { - const { id, name, departmentId } = data; - - return prisma.position.upsert({ - where: { id }, - update: {}, - create: { - id, - name, - departmentId, - }, - }); -} - -// type definition for mapping signature field names (ex: 'Director', 'Manager') to their data -type SignatureFieldMap = { - [key: string]: { - id: string; - name: string; - order: number; - formTemplateId: string; - }; -}; - -// fetch or create signature field for the given form template based on form template id -async function fetchSignatureFields( - formTemplateId: string, -): Promise { - // hard coded signature fields - const signatureFieldNames = ['Chief', 'Manager', 'Director']; - const signatureFieldIds: { [key: string]: string } = { - Chief: uuidv4(), - Manager: uuidv4(), - Director: uuidv4(), - }; - - const signatureFieldsMap: SignatureFieldMap = {}; - - // fetch signaturefield, or create one if it doesn't already exist - for (const name of signatureFieldNames) { - const existingField = await prisma.signatureField.findFirst({ - where: { - name: name, - formTemplateId: formTemplateId, + { + id: IT_EXIT_FORM_FIELD_GROUP_1_UUID, + name: 'Chief Fin Officer', + order: 1, + templateBoxes: [ + { + id: uuidv4(), + type: SignatureBoxFieldType.SIGNATURE, + x_coordinate: 0, + y_coordinate: 0, + }, + ], }, - }); - - if (existingField) { - signatureFieldsMap[name] = existingField; - } else { - const createdField = await prisma.signatureField.create({ - data: { - id: signatureFieldIds[name], - name: name, - order: signatureFieldNames.indexOf(name) + 1, - formTemplateId: formTemplateId, - }, - }); - signatureFieldsMap[name] = createdField; - } - } - - return signatureFieldsMap; -} - -// type definition for mapping signature field names (ex: 'Director', 'Manager') to their data -type FormInstanceData = { - id: string; - name: string; - formDocLink: string; - originatorId: string; - formTemplateId: string; - signatures: any[]; -}; - -// upsert new form instances -async function upsertFormInstance(formInstanceData: FormInstanceData) { - const { id, name, formDocLink, originatorId, formTemplateId, signatures } = - formInstanceData; - await prisma.formInstance.upsert({ - where: { id }, - update: {}, - create: { - id, - name, - formDocLink, - originatorId, - formTemplateId, - signatures: { - create: signatures, + ], + }, + { + id: STAFFING_REQUISITION_UUID, + name: 'Staffing Requisition', + formDocLink: DEV_FORM_DOC_LINK, + fieldGroups: [ + { + id: STAFFING_REQUISITION_FIELD_GROUP_0_UUID, + name: 'Person 1', + order: 0, + templateBoxes: [ + { + id: uuidv4(), + type: SignatureBoxFieldType.SIGNATURE, + x_coordinate: 0, + y_coordinate: 0, + }, + ], }, - }, - }); -} - -/* main seeding function to upsert the following data: -- 1 form template -- 1 department (leadership team) -- 3 signature fields ('chief', 'director', 'manager') -- 4 positions (chief of staff, cheif financial officer, agg director, chief of learning) -- 4 employees, one for each of the above positions -*/ - -async function main() { - // form template - await prisma.formTemplate.upsert({ - where: { id: IT_EXIT_FORM_UUID }, - update: {}, - create: { - id: IT_EXIT_FORM_UUID, - name: 'IT Exit Form', - formDocLink: DEV_FORM_DOC_LINK, - }, - }); - - await prisma.formTemplate.upsert({ - where: { id: STAFFING_REQUISITION_UUID }, - update: {}, - create: { - id: STAFFING_REQUISITION_UUID, - name: 'Staffing Requisition', - formDocLink: DEV_FORM_DOC_LINK, - }, - }); - - await prisma.formTemplate.upsert({ - where: { id: NETWORK_ADD_CHANGE_UUID }, - update: {}, - create: { - id: NETWORK_ADD_CHANGE_UUID, - name: 'Network Add Change', - formDocLink: DEV_FORM_DOC_LINK, - }, - }); - - await prisma.formTemplate.upsert({ - where: { id: MFA_ORACLE_LOGON_REQUEST_UUID }, - update: {}, - create: { - id: MFA_ORACLE_LOGON_REQUEST_UUID, - name: 'MFA Oracle Logon Request', - formDocLink: DEV_FORM_DOC_LINK, - }, - }); - - await prisma.formTemplate.upsert({ - where: { id: HYBRID_AND_REMOTE_WORK_AGREEMENT_UUID }, - update: {}, - create: { - id: HYBRID_AND_REMOTE_WORK_AGREEMENT_UUID, - name: 'Hybrid and Remote Work Agreement', - formDocLink: DEV_FORM_DOC_LINK, - }, - }); - - await prisma.formTemplate.upsert({ - where: { id: VPN_REQUEST_UUID }, - update: {}, - create: { - id: VPN_REQUEST_UUID, - name: 'VPN Request', - formDocLink: DEV_FORM_DOC_LINK, - }, - }); - - await prisma.formTemplate.upsert({ - where: { id: DIGITAL_MFA_ID_BADGE_REQUEST_UUID }, - update: {}, - create: { - id: DIGITAL_MFA_ID_BADGE_REQUEST_UUID, - name: 'Digital MFA ID Badge Request', - formDocLink: DEV_FORM_DOC_LINK, - }, - }); - - await prisma.formTemplate.upsert({ - where: { id: TRAVEL_AUTHORIZATION_UUID }, - update: {}, - create: { - id: TRAVEL_AUTHORIZATION_UUID, - name: 'Travel Authorization', - formDocLink: DEV_FORM_DOC_LINK, - }, - }); - - const IT_EXIT_FORM_SIGNATURE_FIELDS = await fetchSignatureFields( - IT_EXIT_FORM_UUID, - ); - const STAFFING_REQUISITION_SIGNATURE_FIELDS = await fetchSignatureFields( - STAFFING_REQUISITION_UUID, - ); - const NETWORK_ADD_CHANGE_SIGNATURE_FIELDS = await fetchSignatureFields( - NETWORK_ADD_CHANGE_UUID, - ); - const MFA_ORACLE_LOGON_REQUEST_SIGNATURE_FIELDS = await fetchSignatureFields( - MFA_ORACLE_LOGON_REQUEST_UUID, - ); - const HYBRID_AND_REMOTE_WORK_AGREEMENT_SIGNATURE_FIELDS = - await fetchSignatureFields(HYBRID_AND_REMOTE_WORK_AGREEMENT_UUID); - const VPN_REQUEST_SIGNATURE_FIELDS = await fetchSignatureFields( - VPN_REQUEST_UUID, - ); - const DIGITAL_MFA_ID_BADGE_REQUEST_SIGNATURE_FIELDS = - await fetchSignatureFields(DIGITAL_MFA_ID_BADGE_REQUEST_UUID); - const TRAVEL_AUTHORIZATION_SIGNATURE_FIELDS = await fetchSignatureFields( - TRAVEL_AUTHORIZATION_UUID, - ); - - // leadership team department - const departmentLeadershipTeam = await prisma.department.upsert({ - where: { id: LEADERSHIP_TEAM_UUID }, - update: {}, - create: { - id: LEADERSHIP_TEAM_UUID, - name: 'Leadership Team', - }, - }); - - // leadership team positions - const positions = [ - { - id: CHIEF_OF_STAFF_UUID, - name: 'Chief of Staff', - departmentId: departmentLeadershipTeam.id, - signatureFields: [ - IT_EXIT_FORM_SIGNATURE_FIELDS['Chief'], - STAFFING_REQUISITION_SIGNATURE_FIELDS['Chief'], - NETWORK_ADD_CHANGE_SIGNATURE_FIELDS['Chief'], - MFA_ORACLE_LOGON_REQUEST_SIGNATURE_FIELDS['Chief'], - HYBRID_AND_REMOTE_WORK_AGREEMENT_SIGNATURE_FIELDS['Chief'], - VPN_REQUEST_SIGNATURE_FIELDS['Chief'], - DIGITAL_MFA_ID_BADGE_REQUEST_SIGNATURE_FIELDS['Chief'], - TRAVEL_AUTHORIZATION_SIGNATURE_FIELDS['Chief'], - ], - }, - { - id: CHIEF_FIN_OFFICER_UUID, - name: 'Chief Financial Officer', - departmentId: departmentLeadershipTeam.id, - signatureFields: [ - IT_EXIT_FORM_SIGNATURE_FIELDS['Chief'], - STAFFING_REQUISITION_SIGNATURE_FIELDS['Chief'], - NETWORK_ADD_CHANGE_SIGNATURE_FIELDS['Chief'], - MFA_ORACLE_LOGON_REQUEST_SIGNATURE_FIELDS['Chief'], - HYBRID_AND_REMOTE_WORK_AGREEMENT_SIGNATURE_FIELDS['Chief'], - VPN_REQUEST_SIGNATURE_FIELDS['Chief'], - DIGITAL_MFA_ID_BADGE_REQUEST_SIGNATURE_FIELDS['Chief'], - TRAVEL_AUTHORIZATION_SIGNATURE_FIELDS['Chief'], - ], - }, - { - id: AGG_DIR_UUID, - name: 'AGG Director', - departmentId: departmentLeadershipTeam.id, - signatureFields: [ - IT_EXIT_FORM_SIGNATURE_FIELDS['Director'], - STAFFING_REQUISITION_SIGNATURE_FIELDS['Director'], - NETWORK_ADD_CHANGE_SIGNATURE_FIELDS['Director'], - MFA_ORACLE_LOGON_REQUEST_SIGNATURE_FIELDS['Director'], - HYBRID_AND_REMOTE_WORK_AGREEMENT_SIGNATURE_FIELDS['Director'], - VPN_REQUEST_SIGNATURE_FIELDS['Director'], - DIGITAL_MFA_ID_BADGE_REQUEST_SIGNATURE_FIELDS['Director'], - TRAVEL_AUTHORIZATION_SIGNATURE_FIELDS['Director'], - ], - }, - { - id: CHIEF_LEARNING_ENGAGEMENT_UUID, - name: 'Chief of Learning & Community Engagement', - departmentId: departmentLeadershipTeam.id, - signatureFields: [ - IT_EXIT_FORM_SIGNATURE_FIELDS['Manager'], - STAFFING_REQUISITION_SIGNATURE_FIELDS['Manager'], - NETWORK_ADD_CHANGE_SIGNATURE_FIELDS['Manager'], - MFA_ORACLE_LOGON_REQUEST_SIGNATURE_FIELDS['Manager'], - HYBRID_AND_REMOTE_WORK_AGREEMENT_SIGNATURE_FIELDS['Manager'], - VPN_REQUEST_SIGNATURE_FIELDS['Manager'], - DIGITAL_MFA_ID_BADGE_REQUEST_SIGNATURE_FIELDS['Manager'], - TRAVEL_AUTHORIZATION_SIGNATURE_FIELDS['Manager'], - ], - }, - ]; - - for (const positionData of positions) { - await upsertPosition(positionData); - } - - // test team department - await prisma.department.upsert({ - where: { id: TEST_TEAM_UUID }, - update: {}, - create: { - id: TEST_TEAM_UUID, - name: 'Test Team', - }, - }); - - // test team positions - await upsertPosition({ - id: TEST_TEAM_POSITION_UUID, - name: 'Test Position', - departmentId: TEST_TEAM_UUID, - }); - - // employees - const employees = [ - { - id: IRIS_ZHANG_UUID, - firstName: 'Iris', - lastName: 'Zhang', - email: 'zhang.iri@northeastern.edu', - positionId: CHIEF_OF_STAFF_UUID, - signatureLink: DEV_SIGNATURE_LINK, - scope: EmployeeScope.BASE_USER, - }, - { - id: KAI_ZHENG_UUID, - firstName: 'Kai', - lastName: 'Zheng', - email: 'zheng.kaiy@northeastern.edu', - positionId: CHIEF_FIN_OFFICER_UUID, - signatureLink: DEV_SIGNATURE_LINK, - scope: EmployeeScope.BASE_USER, - }, - { - id: ANGELA_WEIGL_UUID, - firstName: 'Angela', - lastName: 'Weigl', - email: 'weigl.a@northeastern.edu', - positionId: AGG_DIR_UUID, - signatureLink: DEV_SIGNATURE_LINK, - scope: EmployeeScope.BASE_USER, - }, - { - id: ANSHUL_SHIRUDE_UUID, - firstName: 'Anshul', - lastName: 'Shirude', - email: 'shirude.a@northeastern.edu', - positionId: CHIEF_LEARNING_ENGAGEMENT_UUID, - signatureLink: DEV_SIGNATURE_LINK, - scope: EmployeeScope.BASE_USER, - }, - ]; - - for (const empData of employees) { - await upsertEmployee(empData); - } - - // form instances - const formInstances = [ - { - id: uuidv4(), - name: 'IT Exit Form Instance', - formDocLink: DEV_FORM_DOC_LINK, - originatorId: KAI_ZHENG_UUID, - formTemplateId: IT_EXIT_FORM_UUID, - signatures: [ - { - id: uuidv4(), - order: 0, - signerType: SignerType.USER_LIST, - signerEmployeeList: { - connect: [ - { id: IRIS_ZHANG_UUID }, - { id: KAI_ZHENG_UUID }, - { id: ANGELA_WEIGL_UUID }, - { id: ANSHUL_SHIRUDE_UUID }, - ], + { + id: STAFFING_REQUISITION_FIELD_GROUP_1_UUID, + name: 'Person 2', + order: 1, + templateBoxes: [ + { + id: uuidv4(), + type: SignatureBoxFieldType.SIGNATURE, + x_coordinate: 0, + y_coordinate: 0, }, - }, - { - id: uuidv4(), - order: 1, - signerType: 'POSITION', - signerPositionId: CHIEF_FIN_OFFICER_UUID, - }, - ], - }, - { - id: uuidv4(), - name: 'Staffing Requisition Form Instance', - formDocLink: DEV_FORM_DOC_LINK, - originatorId: IRIS_ZHANG_UUID, - formTemplateId: STAFFING_REQUISITION_UUID, - signatures: [ - { - id: uuidv4(), - order: 0, - signerType: 'USER', - signerEmployeeId: KAI_ZHENG_UUID, - }, - { - id: uuidv4(), - order: 1, - signerType: SignerType.USER_LIST, - signerEmployeeList: { - connect: [ - { id: IRIS_ZHANG_UUID }, - { id: KAI_ZHENG_UUID }, - { id: ANGELA_WEIGL_UUID }, - { id: ANSHUL_SHIRUDE_UUID }, - ], + ], + }, + ], + }, + { + id: NETWORK_ADD_CHANGE_UUID, + name: 'Network Add Change', + formDocLink: DEV_FORM_DOC_LINK, + fieldGroups: [ + { + id: NETWORK_ADD_CHANGE_FIELD_GROUP_0_UUID, + name: 'Chief', + order: 0, + templateBoxes: [ + { + id: uuidv4(), + type: 'SIGNATURE', + x_coordinate: 0, + y_coordinate: 0, }, - }, - ], - }, - { - id: uuidv4(), - name: 'Network Add Change Form Instance', - formDocLink: DEV_FORM_DOC_LINK, - originatorId: ANSHUL_SHIRUDE_UUID, - formTemplateId: NETWORK_ADD_CHANGE_UUID, - signatures: [ - { - id: uuidv4(), - order: 0, - signerType: 'DEPARTMENT', - signerDepartmentId: LEADERSHIP_TEAM_UUID, - }, - ], - }, - { - id: uuidv4(), - name: 'MFA Oracle Logon Request Form Instance', - formDocLink: DEV_FORM_DOC_LINK, - originatorId: ANGELA_WEIGL_UUID, - formTemplateId: MFA_ORACLE_LOGON_REQUEST_UUID, - signatures: [ - { - id: uuidv4(), - order: 0, - signerType: 'DEPARTMENT', - signerDepartmentId: LEADERSHIP_TEAM_UUID, - }, - ], - }, - { - id: uuidv4(), - name: 'Hybrid and Remote Work Agreement Form Instance', - formDocLink: DEV_FORM_DOC_LINK, - originatorId: ANSHUL_SHIRUDE_UUID, - formTemplateId: HYBRID_AND_REMOTE_WORK_AGREEMENT_UUID, - signatures: [ - { - id: uuidv4(), - order: 0, - signerType: 'USER', - signerEmployeeId: KAI_ZHENG_UUID, - }, - ], - }, - { - id: uuidv4(), - name: 'VPN Request Form Instance', - formDocLink: DEV_FORM_DOC_LINK, - originatorId: IRIS_ZHANG_UUID, - formTemplateId: VPN_REQUEST_UUID, - signatures: [ - { - id: uuidv4(), - order: 0, - signerType: 'USER', - signerEmployeeId: KAI_ZHENG_UUID, - }, - ], - }, - { - id: uuidv4(), - name: 'Digital MFA ID Badge Request Form Instance', - formDocLink: DEV_FORM_DOC_LINK, - originatorId: KAI_ZHENG_UUID, - formTemplateId: DIGITAL_MFA_ID_BADGE_REQUEST_UUID, - signatures: [ - { - id: uuidv4(), - order: 0, - signerType: 'DEPARTMENT', - signerDepartmentId: LEADERSHIP_TEAM_UUID, - }, - ], - }, - { - id: uuidv4(), - name: 'Travel Authorization Form Instance', - formDocLink: DEV_FORM_DOC_LINK, - originatorId: ANGELA_WEIGL_UUID, - formTemplateId: TRAVEL_AUTHORIZATION_UUID, - signatures: [ - { - id: uuidv4(), - order: 0, - signerType: 'POSITION', - signerPositionId: AGG_DIR_UUID, - }, - ], - }, - ]; - - for (const formInstance of formInstances) { - await upsertFormInstance(formInstance); - } -} - -// runs main seeding function -main() - .catch((e) => { - console.error(e); - process.exit(1); - }) - // fetch and log data to verify seeding - .finally(async () => { - const formTemplates = await prisma.formTemplate.findMany(); - console.log('Form Templates:', formTemplates); - - const departments = await prisma.department.findMany(); - console.log('Departments:', departments); - - const allPositions = await prisma.position.findMany(); - console.log('Positions:', JSON.stringify(allPositions, null, 2)); - - const allEmployees = await prisma.employee.findMany({ - include: { - position: true, + ], }, - }); - console.log('Employees:', allEmployees); - - const allSignatureFields = await prisma.signatureField.findMany(); - console.log('Signature Fields:', allSignatureFields); - - const allFormInstances = await prisma.formInstance.findMany(); - console.log('Form Instances:', allFormInstances); - - await prisma.$disconnect(); - }); + ], + }, + { + id: MFA_ORACLE_LOGON_REQUEST_UUID, + name: 'MFA Oracle Logon Request', + formDocLink: DEV_FORM_DOC_LINK, + fieldGroups: [ + { + id: MFA_ORACLE_LOGON_REQUEST_FIELD_GROUP_0_UUID, + name: 'Chief', + order: 0, + templateBoxes: [ + { + id: uuidv4(), + type: SignatureBoxFieldType.SIGNATURE, + x_coordinate: 0, + y_coordinate: 0, + }, + ], + }, + { + id: MFA_ORACLE_LOGON_REQUEST_FIELD_GROUP_1_UUID, + name: 'Chief', + order: 1, + templateBoxes: [ + { + id: uuidv4(), + type: SignatureBoxFieldType.SIGNATURE, + x_coordinate: 0, + y_coordinate: 0, + }, + ], + }, + ], + }, + { + id: HYBRID_AND_REMOTE_WORK_AGREEMENT_UUID, + name: 'Hybrid and Remote Work Agreement', + formDocLink: DEV_FORM_DOC_LINK, + fieldGroups: [ + { + id: HYBRID_AND_REMOTE_WORK_AGREEMENT_FIELD_GROUP_0_UUID, + name: 'Chief', + order: 0, + templateBoxes: [ + { + id: uuidv4(), + type: SignatureBoxFieldType.SIGNATURE, + x_coordinate: 0, + y_coordinate: 0, + }, + ], + }, + ], + }, + { + id: VPN_REQUEST_UUID, + name: 'VPN Request', + formDocLink: DEV_FORM_DOC_LINK, + fieldGroups: [ + { + id: VPN_REQUEST_FIELD_GROUP_0_UUID, + name: 'Chief', + order: 0, + templateBoxes: [ + { + id: uuidv4(), + type: SignatureBoxFieldType.SIGNATURE, + x_coordinate: 0, + y_coordinate: 0, + }, + ], + }, + ], + }, + { + id: DIGITAL_MFA_ID_BADGE_REQUEST_UUID, + name: 'Digital MFA ID Badge Request', + formDocLink: DEV_FORM_DOC_LINK, + fieldGroups: [ + { + id: DIGITAL_MFA_ID_BADGE_REQUEST_FIELD_GROUP_0_UUID, + name: 'Chief', + order: 0, + templateBoxes: [ + { + id: uuidv4(), + type: SignatureBoxFieldType.SIGNATURE, + x_coordinate: 0, + y_coordinate: 0, + }, + ], + }, + ], + }, + { + id: TRAVEL_AUTHORIZATION_UUID, + name: 'Travel Authorization', + formDocLink: DEV_FORM_DOC_LINK, + fieldGroups: [ + { + id: TRAVEL_AUTHORIZATION_FIELD_GROUP_0_UUID, + name: 'Chief', + order: 0, + templateBoxes: [ + { + id: uuidv4(), + type: SignatureBoxFieldType.SIGNATURE, + x_coordinate: 0, + y_coordinate: 0, + }, + ], + }, + ], + }, +]; + +const formInstances: FormInstanceData[] = [ + { + id: uuidv4(), + name: 'IT Exit Form Instance', + formDocLink: DEV_FORM_DOC_LINK, + originatorId: KAI_ZHENG_UUID, + formTemplateId: IT_EXIT_FORM_UUID, + assignedGroups: [ + { + id: uuidv4(), + fieldGroupId: IT_EXIT_FORM_FIELD_GROUP_0_UUID, + order: 0, + signerType: SignerType.USER_LIST, + signerEmployeeList: [ + { id: IRIS_ZHANG_UUID }, + { id: KAI_ZHENG_UUID }, + { id: ANGELA_WEIGL_UUID }, + { id: ANSHUL_SHIRUDE_UUID }, + ], + }, + { + id: uuidv4(), + fieldGroupId: IT_EXIT_FORM_FIELD_GROUP_1_UUID, + order: 1, + signerType: SignerType.POSITION, + signerPositionId: CHIEF_FIN_OFFICER_UUID, + }, + ], + }, + { + id: uuidv4(), + name: 'Staffing Requisition Form Instance', + formDocLink: DEV_FORM_DOC_LINK, + originatorId: IRIS_ZHANG_UUID, + formTemplateId: STAFFING_REQUISITION_UUID, + assignedGroups: [ + { + id: uuidv4(), + fieldGroupId: STAFFING_REQUISITION_FIELD_GROUP_0_UUID, + order: 0, + signerType: SignerType.USER, + signerEmployeeId: KAI_ZHENG_UUID, + }, + { + id: uuidv4(), + fieldGroupId: STAFFING_REQUISITION_FIELD_GROUP_1_UUID, + order: 1, + signerType: SignerType.USER_LIST, + signerEmployeeList: [ + { id: IRIS_ZHANG_UUID }, + { id: KAI_ZHENG_UUID }, + { id: ANGELA_WEIGL_UUID }, + { id: ANSHUL_SHIRUDE_UUID }, + ], + }, + ], + }, + { + id: uuidv4(), + name: 'Network Add Change Form Instance', + formDocLink: DEV_FORM_DOC_LINK, + originatorId: ANSHUL_SHIRUDE_UUID, + formTemplateId: NETWORK_ADD_CHANGE_UUID, + assignedGroups: [ + { + id: uuidv4(), + fieldGroupId: NETWORK_ADD_CHANGE_FIELD_GROUP_0_UUID, + order: 0, + signerType: SignerType.DEPARTMENT, + signerDepartmentId: LEADERSHIP_TEAM_UUID, + }, + ], + }, + { + id: uuidv4(), + name: 'MFA Oracle Logon Request Form Instance', + formDocLink: DEV_FORM_DOC_LINK, + originatorId: ANGELA_WEIGL_UUID, + formTemplateId: MFA_ORACLE_LOGON_REQUEST_UUID, + assignedGroups: [ + { + id: uuidv4(), + fieldGroupId: MFA_ORACLE_LOGON_REQUEST_FIELD_GROUP_0_UUID, + order: 0, + signerType: SignerType.DEPARTMENT, + signerDepartmentId: LEADERSHIP_TEAM_UUID, + }, + { + id: uuidv4(), + fieldGroupId: MFA_ORACLE_LOGON_REQUEST_FIELD_GROUP_1_UUID, + order: 1, + signerType: SignerType.DEPARTMENT, + signerDepartmentId: LEADERSHIP_TEAM_UUID, + }, + ], + }, + { + id: uuidv4(), + name: 'Hybrid and Remote Work Agreement Form Instance', + formDocLink: DEV_FORM_DOC_LINK, + originatorId: ANSHUL_SHIRUDE_UUID, + formTemplateId: HYBRID_AND_REMOTE_WORK_AGREEMENT_UUID, + assignedGroups: [ + { + id: uuidv4(), + fieldGroupId: HYBRID_AND_REMOTE_WORK_AGREEMENT_FIELD_GROUP_0_UUID, + order: 0, + signerType: SignerType.USER, + signerEmployeeId: KAI_ZHENG_UUID, + }, + ], + }, + { + id: uuidv4(), + name: 'VPN Request Form Instance', + formDocLink: DEV_FORM_DOC_LINK, + originatorId: IRIS_ZHANG_UUID, + formTemplateId: VPN_REQUEST_UUID, + assignedGroups: [ + { + id: uuidv4(), + fieldGroupId: VPN_REQUEST_FIELD_GROUP_0_UUID, + order: 0, + signerType: SignerType.USER, + signerEmployeeId: KAI_ZHENG_UUID, + }, + ], + }, + { + id: uuidv4(), + name: 'Digital MFA ID Badge Request Form Instance', + formDocLink: DEV_FORM_DOC_LINK, + originatorId: KAI_ZHENG_UUID, + formTemplateId: DIGITAL_MFA_ID_BADGE_REQUEST_UUID, + assignedGroups: [ + { + id: uuidv4(), + fieldGroupId: DIGITAL_MFA_ID_BADGE_REQUEST_FIELD_GROUP_0_UUID, + order: 0, + signerType: SignerType.DEPARTMENT, + signerDepartmentId: LEADERSHIP_TEAM_UUID, + }, + ], + }, + { + id: uuidv4(), + name: 'Travel Authorization Form Instance', + formDocLink: DEV_FORM_DOC_LINK, + originatorId: ANGELA_WEIGL_UUID, + formTemplateId: TRAVEL_AUTHORIZATION_UUID, + assignedGroups: [ + { + id: uuidv4(), + fieldGroupId: TRAVEL_AUTHORIZATION_FIELD_GROUP_0_UUID, + order: 0, + signerType: SignerType.POSITION, + signerPositionId: AGG_DIR_UUID, + }, + ], + }, +]; + +const seeder = new Seeder( + departments, + positions, + employees, + formTemplates, + formInstances, +); + +seeder.seed().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/apps/server/prisma/seed.types.ts b/apps/server/prisma/seed.types.ts new file mode 100644 index 00000000..56b6efd8 --- /dev/null +++ b/apps/server/prisma/seed.types.ts @@ -0,0 +1,68 @@ +import { + EmployeeScope, + SignatureBoxFieldType, + SignerType, +} from '@prisma/client'; +import { ConnectEmployeeDto } from '../src/assigned-group/dto/create-assigned-group.dto'; + +export type DepartmentData = { + id: string; + name: string; +}; + +export type PositionData = { + id: string; + name: string; + departmentId: string; +}; + +export type EmployeeData = { + id: string; + firstName: string; + lastName: string; + email: string; + positionId: string; + signatureLink: string; + scope: EmployeeScope; +}; + +export type TemplateBoxData = { + id: string; + type: SignatureBoxFieldType; + x_coordinate: number; + y_coordinate: number; +}; + +export type FieldGroupData = { + id: string; + name: string; + order: number; + templateBoxes: TemplateBoxData[]; +}; + +export type FormTemplateData = { + id: string; + name: string; + formDocLink: string; + fieldGroups: FieldGroupData[]; +}; + +export type AssignedGroupData = { + id: string; + order: number; + signerType: SignerType; + signerEmployeeId?: string; + signerPositionId?: string; + signerDepartmentId?: string; + signerEmployeeList?: ConnectEmployeeDto[]; + fieldGroupId: string; +}; + +export type FormInstanceData = { + id: string; + name: string; + formDocLink: string; + originatorId: string; + formTemplateId: string; + assignedGroups: AssignedGroupData[]; +}; diff --git a/apps/server/prisma/seeder.ts b/apps/server/prisma/seeder.ts new file mode 100644 index 00000000..b92e1a3b --- /dev/null +++ b/apps/server/prisma/seeder.ts @@ -0,0 +1,345 @@ +import { PrismaClient } from '@prisma/client'; +import { + DepartmentData, + PositionData, + EmployeeData, + FormTemplateData, + FormInstanceData, +} from './seed.types'; + +export class Seeder { + private prisma: PrismaClient; + private departments: DepartmentData[]; + private positions: PositionData[]; + private employees: EmployeeData[]; + private formTemplates: FormTemplateData[]; + private formInstances: FormInstanceData[]; + + constructor( + departments: DepartmentData[], + positions: PositionData[], + employees: EmployeeData[], + formTemplates: FormTemplateData[], + formInstances: FormInstanceData[], + ) { + this.prisma = new PrismaClient(); + this.departments = departments; + this.positions = positions; + this.employees = employees; + this.formTemplates = formTemplates; + this.formInstances = formInstances; + } + + async seed() { + for (const department of this.departments) { + await this.upsertDepartment(department); + } + + // make sure that departments specified in each position are valid + for (const position of this.positions) { + if (!this.departments.find((dept) => dept.id === position.departmentId)) { + throw new Error('Department not found for position'); + } + } + for (const position of this.positions) { + await this.upsertPosition(position); + } + + // make sure that positions specified in each employee are valid + for (const employee of this.employees) { + if (!this.positions.find((pos) => pos.id === employee.positionId)) { + throw new Error('Position not found for employee'); + } + } + for (const employee of this.employees) { + await this.upsertEmployee(employee); + } + + for (const formTemplate of this.formTemplates) { + await this.upsertFormTemplate(formTemplate); + } + + // make sure that form templates specified in each form instance are valid + for (const formInstance of this.formInstances) { + if ( + !this.formTemplates.find( + (temp) => temp.id === formInstance.formTemplateId, + ) + ) { + throw new Error('Form template not found for form instance'); + } + } + // make sure that originator ids are all valid employees + for (const formInstance of this.formInstances) { + if (!this.employees.find((emp) => emp.id === formInstance.originatorId)) { + throw new Error('Originator not found for form instance'); + } + } + // make sure assigned group signer ids are all valid employees, positions, or departments + for (const formInstance of this.formInstances) { + for (const assignedGroup of formInstance.assignedGroups) { + if (assignedGroup.signerType === 'USER') { + if (!assignedGroup.signerEmployeeId) { + throw new Error('Missing signer employee id'); + } + if ( + !this.employees.find( + (emp) => emp.id === assignedGroup.signerEmployeeId, + ) + ) { + throw new Error('Signer employee not found for assigned group'); + } + } else if (assignedGroup.signerType === 'POSITION') { + if (!assignedGroup.signerPositionId) { + throw new Error('Missing signer position id'); + } + if ( + !this.positions.find( + (pos) => pos.id === assignedGroup.signerPositionId, + ) + ) { + throw new Error('Signer position not found for assigned group'); + } + } else if (assignedGroup.signerType === 'DEPARTMENT') { + if (!assignedGroup.signerDepartmentId) { + throw new Error('Missing signer department id'); + } + if ( + !this.departments.find( + (dept) => dept.id === assignedGroup.signerDepartmentId, + ) + ) { + throw new Error('Signer department not found for assigned group'); + } + } else if (assignedGroup.signerType === 'USER_LIST') { + if (!assignedGroup.signerEmployeeList) { + throw new Error('Missing signer employee list'); + } + for (const emp of assignedGroup.signerEmployeeList) { + if (!this.employees.find((e) => e.id === emp.id)) { + throw new Error('Signer employee not found in list'); + } + } + } + } + } + for (const formInstance of this.formInstances) { + await this.upsertFormInstance(formInstance); + } + + this.logSeededData(); + this.closeConnection(); + } + + private async upsertPosition(data: PositionData) { + const { id, name, departmentId } = data; + + return await this.prisma.position.upsert({ + where: { id }, + update: {}, + create: { + id, + name, + departmentId, + }, + }); + } + + private async upsertDepartment(data: DepartmentData) { + const { id, name } = data; + + return await this.prisma.department.upsert({ + where: { id }, + update: {}, + create: { + id, + name, + }, + }); + } + + private async upsertEmployee(data: EmployeeData) { + const { id, firstName, lastName, email, signatureLink, positionId, scope } = + data; + + return await this.prisma.employee.upsert({ + where: { id: id }, + update: {}, + create: { + id: id, + firstName: firstName, + lastName: lastName, + email: email, + signatureLink: signatureLink, + position: { + connect: { id: positionId }, + }, + scope: scope, + }, + }); + } + + private async upsertFormTemplate(data: FormTemplateData) { + const { id, name, formDocLink, fieldGroups } = data; + + await this.prisma.formTemplate.upsert({ + where: { id }, + update: {}, + create: { + id, + name, + formDocLink, + fieldGroups: { + create: fieldGroups.map((fieldGroup) => { + return { + id: fieldGroup.id, + name: fieldGroup.name, + order: fieldGroup.order, + templateBoxes: { + create: fieldGroup.templateBoxes.map((box) => { + return { + id: box.id, + type: box.type, + x_coordinate: box.x_coordinate, + y_coordinate: box.y_coordinate, + }; + }), + }, + }; + }), + }, + }, + }); + } + + // Upsert a new form instance based on an existing form template + private async upsertFormInstance(data: FormInstanceData) { + const formTemplate = await this.prisma.formTemplate.findUnique({ + where: { id: data.formTemplateId }, + include: { + fieldGroups: { + include: { + templateBoxes: true, + }, + }, + }, + }); + + if (!formTemplate) { + throw new Error('Form template not found'); + } + + return await this.prisma.formInstance.upsert({ + where: { id: data.id }, + update: {}, + create: { + id: data.id, + name: data.name, + formDocLink: data.formDocLink, + originatorId: data.originatorId, + formTemplateId: data.formTemplateId, + assignedGroups: { + create: data.assignedGroups.map((assignedGroup) => { + return { + id: assignedGroup.id, + order: assignedGroup.order, + signerType: assignedGroup.signerType, + signerEmployee: assignedGroup.signerEmployeeId + ? { + connect: { id: assignedGroup.signerEmployeeId }, + } + : undefined, + signerPosition: assignedGroup.signerPositionId + ? { + connect: { id: assignedGroup.signerPositionId }, + } + : undefined, + signerDepartment: assignedGroup.signerDepartmentId + ? { + connect: { id: assignedGroup.signerDepartmentId }, + } + : undefined, + signerEmployeeList: { + connect: assignedGroup.signerEmployeeList?.map((dto) => ({ + id: dto.id, + })), + }, + instanceBoxes: { + create: formTemplate.fieldGroups + .find( + (fieldGroup) => + fieldGroup.id === assignedGroup.fieldGroupId, + ) + ?.templateBoxes.map((box) => { + return { + templateBoxId: box.id, + }; + }), + }, + fieldGroup: { + connect: { id: assignedGroup.fieldGroupId }, + }, + }; + }), + }, + }, + include: { + assignedGroups: { + include: { + signerEmployee: true, + signerDepartment: true, + signerPosition: true, + signerEmployeeList: true, + }, + }, + }, + }); + } + + private async logSeededData() { + const departments = await this.prisma.department.findMany(); + console.log('Departments:', departments); + + const allPositions = await this.prisma.position.findMany(); + console.log('Positions:', JSON.stringify(allPositions, null, 2)); + + const allEmployees = await this.prisma.employee.findMany({ + include: { + position: true, + }, + }); + console.log('Employees:', allEmployees); + + const formTemplates = await this.prisma.formTemplate.findMany({ + include: { + fieldGroups: { + include: { + templateBoxes: true, + }, + }, + }, + }); + console.log('Form Templates:', formTemplates); + + const allFormInstances = await this.prisma.formInstance.findMany({ + include: { + assignedGroups: { + include: { + signerEmployee: true, + signerDepartment: true, + signerPosition: true, + signerEmployeeList: true, + instanceBoxes: true, + }, + }, + }, + }); + console.log('Form Instances:', allFormInstances); + + await this.prisma.$disconnect(); + } + + async closeConnection() { + await this.prisma.$disconnect(); + } +} diff --git a/apps/server/src/app.module.ts b/apps/server/src/app.module.ts index 5ec27268..e23a2663 100644 --- a/apps/server/src/app.module.ts +++ b/apps/server/src/app.module.ts @@ -4,8 +4,6 @@ import { AppService } from './app.service'; import { PrismaModule } from './prisma/prisma.module'; import { EmployeesModule } from './employees/employees.module'; import { PositionsModule } from './positions/positions.module'; -import { SignatureFieldsModule } from './signature-fields/signature-fields.module'; -import { SignaturesModule } from './signatures/signatures.module'; import { FormInstancesModule } from './form-instances/form-instances.module'; import { FormTemplatesModule } from './form-templates/form-templates.module'; import { DepartmentsModule } from './departments/departments.module'; @@ -13,14 +11,14 @@ import { ConfigModule } from '@nestjs/config'; import { AuthModule } from './auth/auth.module'; import { JwtModule } from '@nestjs/jwt'; import { PassportModule } from '@nestjs/passport'; +import { AssignedGroupModule } from './assigned-group/assigned-group.module'; @Module({ imports: [ PrismaModule, EmployeesModule, PositionsModule, - SignatureFieldsModule, - SignaturesModule, + AssignedGroupModule, FormInstancesModule, FormTemplatesModule, DepartmentsModule, diff --git a/apps/server/src/assigned-group/assigned-group.controller.ts b/apps/server/src/assigned-group/assigned-group.controller.ts new file mode 100644 index 00000000..38ec0362 --- /dev/null +++ b/apps/server/src/assigned-group/assigned-group.controller.ts @@ -0,0 +1,19 @@ +import { Body, Controller, Param, Patch } from '@nestjs/common'; +import { AssignedGroupService } from './assigned-group.service'; +import { UpdateAssignedGroupSignerDto } from './dto/update-assigned-group-signer.dto'; + +@Controller('signatures') +export class AssignedGroupController { + constructor(private readonly assignedGroupService: AssignedGroupService) {} + + @Patch(':id/signer') + updateAssignedGroupSigner( + @Param('id') assignedGroupId: string, + @Body() updateAssignedGroupSigner: UpdateAssignedGroupSignerDto, + ) { + return this.assignedGroupService.updateSigner( + assignedGroupId, + updateAssignedGroupSigner, + ); + } +} diff --git a/apps/server/src/assigned-group/assigned-group.errors.ts b/apps/server/src/assigned-group/assigned-group.errors.ts new file mode 100644 index 00000000..d47ffe10 --- /dev/null +++ b/apps/server/src/assigned-group/assigned-group.errors.ts @@ -0,0 +1,8 @@ +export enum AssignedGroupErrorMessage { + ASSIGNED_GROUP_NOT_FOUND = 'Assigned group could not be found with this id', + ASSIGNED_GROUP_NOT_FOUND_CLIENT = 'Assigned group could not be found', + ASSIGNED_GROUP_NOT_NEXT = 'Assigned group is not the next one to be signed', + EMPLOYEE_CANNOT_SIGN = 'Employee cannot sign for this Assigned group', + MISSING_SIGNER_TYPE = 'Missing signer type', + MISSING_SIGNER = 'Missing signer for specified signer type', +} diff --git a/apps/server/src/signatures/signatures.module.ts b/apps/server/src/assigned-group/assigned-group.module.ts similarity index 50% rename from apps/server/src/signatures/signatures.module.ts rename to apps/server/src/assigned-group/assigned-group.module.ts index 9d46db8d..c6228b22 100644 --- a/apps/server/src/signatures/signatures.module.ts +++ b/apps/server/src/assigned-group/assigned-group.module.ts @@ -1,21 +1,26 @@ import { Module } from '@nestjs/common'; -import { SignaturesService } from './signatures.service'; -import { SignaturesController } from './signatures.controller'; -import { LoggerModule } from '../logger/logger.module'; import { PrismaModule } from '../prisma/prisma.module'; +import { FormTemplatesModule } from '../form-templates/form-templates.module'; +import { PositionsModule } from '../positions/positions.module'; +import { LoggerModule } from '../logger/logger.module'; import { EmployeesModule } from '../employees/employees.module'; +import { PostmarkModule } from '../postmark/postmark.module'; +import { AssignedGroupController } from './assigned-group.controller'; +import { AssignedGroupService } from './assigned-group.service'; import { DepartmentsModule } from '../departments/departments.module'; -import { PositionsModule } from '../positions/positions.module'; @Module({ - controllers: [SignaturesController], - providers: [SignaturesService], + controllers: [AssignedGroupController], + providers: [AssignedGroupService], + exports: [AssignedGroupService], imports: [ - LoggerModule, PrismaModule, + FormTemplatesModule, + PositionsModule, + LoggerModule, EmployeesModule, DepartmentsModule, - PositionsModule, + PostmarkModule, ], }) -export class SignaturesModule {} +export class AssignedGroupModule {} diff --git a/apps/server/src/signatures/signatures.service.spec.ts b/apps/server/src/assigned-group/assigned-group.service.spec.ts similarity index 70% rename from apps/server/src/signatures/signatures.service.spec.ts rename to apps/server/src/assigned-group/assigned-group.service.spec.ts index e7a96c30..6fc080f1 100644 --- a/apps/server/src/signatures/signatures.service.spec.ts +++ b/apps/server/src/assigned-group/assigned-group.service.spec.ts @@ -1,14 +1,14 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { SignaturesService } from './signatures.service'; import { EmployeesService } from '../employees/employees.service'; import { PrismaService } from '../prisma/prisma.service'; import { Prisma, SignerType } from '@prisma/client'; -import { ConnectEmployeeDto } from './dto/create-signature.dto'; import { DepartmentsService } from '../departments/departments.service'; import { PositionsService } from '../positions/positions.service'; +import { AssignedGroupService } from './assigned-group.service'; +import { ConnectEmployeeDto } from './dto/create-assigned-group.dto'; -const signaturePositionSigner = { - id: 'signature-id', +let assignedGroupPositionSigner = { + id: 'assignedGroup-id', formInstanceId: 'form-instance-id', signerType: SignerType.POSITION, signerPositionId: 'position-id', @@ -19,8 +19,8 @@ const signaturePositionSigner = { updatedAt: new Date(1672531200), }; -const signatureDepartmentSigner = { - id: 'signature-id', +let assignedGroupDepartmentSigner = { + id: 'assignedGroup-id', formInstanceId: 'form-instance-id', signerType: SignerType.DEPARTMENT, signerPositionId: null, @@ -32,11 +32,11 @@ const signatureDepartmentSigner = { }; const db = { - signature: { - findFirstOrThrow: jest.fn().mockResolvedValue(signaturePositionSigner), + assignedGroup: { + findFirstOrThrow: jest.fn().mockResolvedValue(assignedGroupPositionSigner), update: jest.fn().mockImplementation((args) => { - const val = { - ...signaturePositionSigner, + let val = { + ...assignedGroupPositionSigner, ...args.data, }; if (args.data.signerEmployeeList.set.length === 0) { @@ -60,13 +60,13 @@ const db = { }, }; -describe('SignaturesService', () => { - let service: SignaturesService; +describe('AssignedGroupService', () => { + let service: AssignedGroupService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ - SignaturesService, + AssignedGroupService, EmployeesService, DepartmentsService, PositionsService, @@ -77,7 +77,7 @@ describe('SignaturesService', () => { ], }).compile(); - service = module.get(SignaturesService); + service = module.get(AssignedGroupService); }); it('should be defined', () => { @@ -86,7 +86,9 @@ describe('SignaturesService', () => { describe('updateSigner', () => { beforeEach(() => { - db.signature.findFirstOrThrow.mockResolvedValue(signaturePositionSigner); + db.assignedGroup.findFirstOrThrow.mockResolvedValue( + assignedGroupPositionSigner, + ); db.employee.findFirstOrThrow = jest.fn(async (val) => { if (val.where.id === 'unknown') { @@ -117,18 +119,18 @@ describe('SignaturesService', () => { describe('success', () => { it('should update the signer to a position', async () => { - db.signature.findFirstOrThrow.mockResolvedValue( - signatureDepartmentSigner, + db.assignedGroup.findFirstOrThrow.mockResolvedValue( + assignedGroupDepartmentSigner, ); const updateSignatureSignerDto = { signerType: SignerType.POSITION, signerPositionId: 'position-id', - signerDepartmentId: null, - signerEmployeeId: null, + signerDepartmentId: undefined, + signerEmployeeId: undefined, }; - const updatedSignature = await service.updateSigner( - 'signature-id', + let updatedSignature = await service.updateSigner( + 'assignedGroup-id', updateSignatureSignerDto, ); expect(updatedSignature.signerType).toEqual(SignerType.POSITION); @@ -139,18 +141,18 @@ describe('SignaturesService', () => { }); it('should update the signer to a department', async () => { - db.signature.findFirstOrThrow.mockResolvedValue( - signaturePositionSigner, + db.assignedGroup.findFirstOrThrow.mockResolvedValue( + assignedGroupPositionSigner, ); const updateSignatureSignerDto = { signerType: SignerType.DEPARTMENT, - signerPositionId: null, + signerPositionId: undefined, signerDepartmentId: 'department-id', - signerEmployeeId: null, + signerEmployeeId: undefined, }; - const updatedSignature = await service.updateSigner( - 'signature-id', + let updatedSignature = await service.updateSigner( + 'assignedGroup-id', updateSignatureSignerDto, ); expect(updatedSignature.signerType).toEqual(SignerType.DEPARTMENT); @@ -161,18 +163,18 @@ describe('SignaturesService', () => { }); it('should update the signer to an employee', async () => { - db.signature.findFirstOrThrow.mockResolvedValue( - signaturePositionSigner, + db.assignedGroup.findFirstOrThrow.mockResolvedValue( + assignedGroupPositionSigner, ); const updateSignatureSignerDto = { signerType: SignerType.USER, - signerPositionId: null, - signerDepartmentId: null, + signerPositionId: undefined, + signerDepartmentId: undefined, signerEmployeeId: 'employee-id', }; - const updatedSignature = await service.updateSigner( - 'signature-id', + let updatedSignature = await service.updateSigner( + 'assignedGroup-id', updateSignatureSignerDto, ); expect(updatedSignature.signerType).toEqual(SignerType.USER); @@ -183,22 +185,22 @@ describe('SignaturesService', () => { }); it('should update the signer to a list of employees', async () => { - db.signature.findFirstOrThrow.mockResolvedValue( - signaturePositionSigner, + db.assignedGroup.findFirstOrThrow.mockResolvedValue( + assignedGroupPositionSigner, ); const updateSignatureSignerDto = { signerType: SignerType.USER_LIST, - signerPositionId: null, - signerDepartmentId: null, - signerEmployeeId: null, + signerPositionId: undefined, + signerDepartmentId: undefined, + signerEmployeeId: undefined, signerEmployeeList: [ { id: 'employee-id-1' }, { id: 'employee-id-2' }, ], }; - const updatedSignature = await service.updateSigner( - 'signature-id', + let updatedSignature = await service.updateSigner( + 'assignedGroup-id', updateSignatureSignerDto, ); expect(updatedSignature.signerType).toEqual(SignerType.USER_LIST); @@ -213,8 +215,8 @@ describe('SignaturesService', () => { }); describe('error', () => { - it('should throw if signature not found', async () => { - db.signature.findFirstOrThrow.mockRejectedValue( + it('should throw if assignedGroup not found', async () => { + db.assignedGroup.findFirstOrThrow.mockRejectedValue( new Prisma.PrismaClientKnownRequestError('', { code: 'P2025', clientVersion: '', @@ -225,11 +227,11 @@ describe('SignaturesService', () => { const updateSignatureSignerDto = { signerType: SignerType.POSITION, signerPositionId: 'position-id', - signerDepartmentId: null, - signerEmployeeId: null, + signerDepartmentId: undefined, + signerEmployeeId: undefined, }; expect( - service.updateSigner('signature-id', updateSignatureSignerDto), + service.updateSigner('assignedGroup-id', updateSignatureSignerDto), ).rejects.toThrowError(); }); it('should throw if invalid position specified', async () => { @@ -247,58 +249,58 @@ describe('SignaturesService', () => { signerPositionId: 'unknown', }; expect( - service.updateSigner('signature-id', updateSignatureSignerDto), + service.updateSigner('assignedGroup-id', updateSignatureSignerDto), ).rejects.toThrowError(); }); it('should throw if invalid department specified', async () => { const updateSignatureSignerDto = { signerType: SignerType.DEPARTMENT, - signerPositionId: null, + signerPositionId: undefined, signerDepartmentId: 'unknown', - signerEmployeeId: null, + signerEmployeeId: undefined, }; expect( - service.updateSigner('signature-id', updateSignatureSignerDto), + service.updateSigner('assignedGroup-id', updateSignatureSignerDto), ).rejects.toThrowError(); }); it('should throw if invalid employee specified', async () => { const updateSignatureSignerDto = { signerType: SignerType.USER, - signerPositionId: null, - signerDepartmentId: null, + signerPositionId: undefined, + signerDepartmentId: undefined, signerEmployeeId: 'unknown', }; expect( - service.updateSigner('signature-id', updateSignatureSignerDto), + service.updateSigner('assignedGroup-id', updateSignatureSignerDto), ).rejects.toThrowError(); }); it('should throw if invalid employee in list of employees', async () => { const updateSignatureSignerDto = { signerType: SignerType.USER_LIST, - signerPositionId: null, - signerDepartmentId: null, - signerEmployeeId: null, + signerPositionId: undefined, + signerDepartmentId: undefined, + signerEmployeeId: undefined, signerEmployeeList: [{ id: 'employee-id-1' }, { id: 'unknown' }], }; expect( - service.updateSigner('signature-id', updateSignatureSignerDto), + service.updateSigner('assignedGroup-id', updateSignatureSignerDto), ).rejects.toThrowError(); }); }); }); describe('findOne', () => { - it('should find one signature', async () => { - expect(service.findOne('signature-id')).resolves.toEqual( - signaturePositionSigner, + it('should find one assignedGroup', async () => { + expect(service.findOne('assignedGroup-id')).resolves.toEqual( + assignedGroupPositionSigner, ); }); - it('should throw an error if signature is not found', async () => { - db.signature.findFirstOrThrow = jest.fn().mockRejectedValue( + it('should throw an error if assignedGroup is not found', async () => { + db.assignedGroup.findFirstOrThrow = jest.fn().mockRejectedValue( new Prisma.PrismaClientKnownRequestError('', { code: 'P2025', clientVersion: '', @@ -306,7 +308,7 @@ describe('SignaturesService', () => { batchRequestIdx: undefined, }), ); - expect(service.findOne('signature-id')).rejects.toThrowError(); + expect(service.findOne('assignedGroup-id')).rejects.toThrowError(); }); }); }); diff --git a/apps/server/src/assigned-group/assigned-group.service.ts b/apps/server/src/assigned-group/assigned-group.service.ts new file mode 100644 index 00000000..6fdf3c0a --- /dev/null +++ b/apps/server/src/assigned-group/assigned-group.service.ts @@ -0,0 +1,201 @@ +import { BadRequestException, Injectable } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { PositionsService } from '../positions/positions.service'; +import { UpdateAssignedGroupSignerDto } from './dto/update-assigned-group-signer.dto'; +import { DepartmentsErrorMessage } from '../departments/departments.errors'; +import { EmployeeErrorMessage } from '../employees/employees.errors'; +import { PositionsErrorMessage } from '../positions/positions.errors'; +import { AssignedGroupErrorMessage } from './assigned-group.errors'; +import { DepartmentsService } from '../departments/departments.service'; +import { EmployeesService } from '../employees/employees.service'; + +@Injectable() +export class AssignedGroupService { + constructor( + private positionsService: PositionsService, + private departmentsService: DepartmentsService, + private employeeService: EmployeesService, + private prismaService: PrismaService, + ) {} + + async updateSigner( + assignedGroupId: string, + updateAssignedGroupSignerDto: UpdateAssignedGroupSignerDto, + ) { + try { + await this.findOne(assignedGroupId); + } catch (e) { + throw new BadRequestException( + AssignedGroupErrorMessage.ASSIGNED_GROUP_NOT_FOUND, + ); + } + + switch (updateAssignedGroupSignerDto.signerType) { + case 'POSITION': + if (!updateAssignedGroupSignerDto.signerPositionId) { + throw new BadRequestException( + AssignedGroupErrorMessage.MISSING_SIGNER, + ); + } + try { + await this.positionsService.findOne( + updateAssignedGroupSignerDto.signerPositionId, + ); + } catch (e) { + throw new BadRequestException( + PositionsErrorMessage.POSITION_NOT_FOUND, + ); + } + + return await this.prismaService.assignedGroup.update({ + where: { + id: assignedGroupId, + }, + data: { + signerType: 'POSITION', + signerPositionId: updateAssignedGroupSignerDto.signerPositionId, + signerDepartmentId: null, + signerEmployeeId: null, + signerEmployeeList: { + set: [], + }, + }, + include: { + signerEmployee: true, + signerPosition: true, + signerDepartment: true, + signerEmployeeList: true, + }, + }); + case 'DEPARTMENT': + if (!updateAssignedGroupSignerDto.signerDepartmentId) { + throw new BadRequestException( + AssignedGroupErrorMessage.MISSING_SIGNER, + ); + } + try { + await this.departmentsService.findOne( + updateAssignedGroupSignerDto.signerDepartmentId, + ); + } catch (e) { + throw new BadRequestException( + DepartmentsErrorMessage.DEPARTMENT_NOT_FOUND, + ); + } + + return await this.prismaService.assignedGroup.update({ + where: { + id: assignedGroupId, + }, + data: { + signerType: 'DEPARTMENT', + signerPositionId: null, + signerDepartmentId: updateAssignedGroupSignerDto.signerDepartmentId, + signerEmployeeId: null, + signerEmployeeList: { + set: [], + }, + }, + include: { + signerEmployee: true, + signerPosition: true, + signerDepartment: true, + signerEmployeeList: true, + }, + }); + case 'USER': + if (!updateAssignedGroupSignerDto.signerEmployeeId) { + throw new BadRequestException( + AssignedGroupErrorMessage.MISSING_SIGNER, + ); + } + try { + await this.employeeService.findOne( + updateAssignedGroupSignerDto.signerEmployeeId, + ); + } catch (e) { + throw new BadRequestException( + EmployeeErrorMessage.EMPLOYEE_NOT_FOUND, + ); + } + + return await this.prismaService.assignedGroup.update({ + where: { + id: assignedGroupId, + }, + data: { + signerType: 'USER', + signerPositionId: null, + signerDepartmentId: null, + signerEmployeeId: updateAssignedGroupSignerDto.signerEmployeeId, + signerEmployeeList: { + set: [], + }, + }, + include: { + signerEmployee: true, + signerPosition: true, + signerDepartment: true, + signerEmployeeList: true, + }, + }); + case 'USER_LIST': + if (!updateAssignedGroupSignerDto.signerEmployeeList) { + throw new BadRequestException( + AssignedGroupErrorMessage.MISSING_SIGNER, + ); + } + + for (const employee of updateAssignedGroupSignerDto.signerEmployeeList) { + try { + await this.employeeService.findOne(employee.id); + } catch (e) { + throw new BadRequestException( + EmployeeErrorMessage.EMPLOYEE_NOT_FOUND, + ); + } + } + + return await this.prismaService.assignedGroup.update({ + where: { + id: assignedGroupId, + }, + data: { + signerType: 'USER_LIST', + signerPositionId: null, + signerDepartmentId: null, + signerEmployeeId: null, + signerEmployeeList: { + set: updateAssignedGroupSignerDto.signerEmployeeList, + }, + }, + include: { + signerEmployee: true, + signerPosition: true, + signerDepartment: true, + signerEmployeeList: true, + }, + }); + default: + throw new BadRequestException( + AssignedGroupErrorMessage.MISSING_SIGNER_TYPE, + ); + } + } + async findOne(id: string) { + const assignedGroup = + await this.prismaService.assignedGroup.findFirstOrThrow({ + where: { + id: id, + }, + include: { + signerPosition: true, + signerDepartment: true, + signerEmployee: true, + signerEmployeeList: true, + instanceBoxes: true, + }, + }); + return assignedGroup; + } +} diff --git a/apps/server/src/signatures/dto/create-signature.dto.ts b/apps/server/src/assigned-group/dto/create-assigned-group.dto.ts similarity index 73% rename from apps/server/src/signatures/dto/create-signature.dto.ts rename to apps/server/src/assigned-group/dto/create-assigned-group.dto.ts index b124c577..52dd1d34 100644 --- a/apps/server/src/signatures/dto/create-signature.dto.ts +++ b/apps/server/src/assigned-group/dto/create-assigned-group.dto.ts @@ -1,23 +1,24 @@ import { ApiProperty } from '@nestjs/swagger'; import { SignerType } from '@prisma/client'; +import { Type } from 'class-transformer'; import { IsString, IsNotEmpty, - IsNumber, + IsInt, IsArray, ValidateNested, + IsOptional, } from 'class-validator'; -import { Type } from 'class-transformer'; export class ConnectEmployeeDto { @IsString() @IsNotEmpty() @ApiProperty() - id: string; // Use an existing Employee ID to connect + id: string; } -export class CreateSignatureDto { - @IsNumber() +export class CreateAssignedGroupDto { + @IsInt() @IsNotEmpty() @ApiProperty() order: number; @@ -25,26 +26,31 @@ export class CreateSignatureDto { @IsString() @IsNotEmpty() @ApiProperty() - signerEmployeeId: string | null; + fieldGroupId: string; @IsString() @IsNotEmpty() + @ApiProperty({ enum: SignerType }) + signerType: SignerType; + + @IsString() + @IsOptional() @ApiProperty() - signerPositionId: string | null; + signerEmployeeId?: string; @IsString() - @IsNotEmpty() + @IsOptional() @ApiProperty() - signerDepartmentId: string | null; + signerPositionId?: string; + + @IsString() + @IsOptional() + @ApiProperty() + signerDepartmentId?: string; @IsArray() @ValidateNested({ each: true }) @Type(() => ConnectEmployeeDto) @ApiProperty({ type: [ConnectEmployeeDto] }) signerEmployeeList: ConnectEmployeeDto[]; // Update to an array of IDs for connecting - - @IsString() - @IsNotEmpty() - @ApiProperty({ enum: SignerType }) - signerType: SignerType; } diff --git a/apps/server/src/assigned-group/dto/create-instance-box.dto.ts b/apps/server/src/assigned-group/dto/create-instance-box.dto.ts new file mode 100644 index 00000000..9012c19c --- /dev/null +++ b/apps/server/src/assigned-group/dto/create-instance-box.dto.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsNotEmpty } from 'class-validator'; + +export class CreateInstanceBoxDto { + @IsString() + @IsNotEmpty() + @ApiProperty() + assignedGroupId: string; + + @IsString() + @IsNotEmpty() + @ApiProperty() + templateBoxId: string; +} diff --git a/apps/server/src/assigned-group/dto/update-assigned-group-signer.dto.ts b/apps/server/src/assigned-group/dto/update-assigned-group-signer.dto.ts new file mode 100644 index 00000000..6c016346 --- /dev/null +++ b/apps/server/src/assigned-group/dto/update-assigned-group-signer.dto.ts @@ -0,0 +1,6 @@ +import { OmitType, PartialType } from '@nestjs/swagger'; +import { CreateAssignedGroupDto } from './create-assigned-group.dto'; + +export class UpdateAssignedGroupSignerDto extends PartialType( + OmitType(CreateAssignedGroupDto, ['order'] as const), +) {} diff --git a/apps/server/src/assigned-group/entities/assigned-group.entity.ts b/apps/server/src/assigned-group/entities/assigned-group.entity.ts new file mode 100644 index 00000000..78135f06 --- /dev/null +++ b/apps/server/src/assigned-group/entities/assigned-group.entity.ts @@ -0,0 +1,109 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { AssignedGroup, SignerType } from '@prisma/client'; +import { IsDate, IsOptional, IsString } from 'class-validator'; +import { EmployeeBaseEntity } from '../../employees/entities/employee.entity'; +import { PositionBaseEntity } from '../../positions/entities/position.entity'; +import { DepartmentEntity } from '../../departments/entities/department.entity'; +import { FieldGroupBaseEntity } from '../../field-group/entities/field-group.entity'; + +export class AssignedGroupBaseEntity implements AssignedGroup { + @ApiProperty() + id: string; + + @ApiProperty() + fieldGroupId: string; + + @ApiProperty() + order: number; + + @ApiProperty() + signed: boolean; + + @IsString() + @IsOptional() + @ApiPropertyOptional() + signedDocLink: string | null; + + @IsDate() + @ApiProperty() + createdAt: Date; + + @IsDate() + @ApiProperty() + updatedAt: Date; + + @IsString() + @IsOptional() + @ApiPropertyOptional() + signerPositionId: string | null; + + @IsString() + @IsOptional() + @ApiPropertyOptional() + signerDepartmentId: string | null; + + @IsString() + @IsOptional() + @ApiPropertyOptional() + signerEmployeeId: string | null; + + @IsString() + @IsOptional() + @ApiPropertyOptional() + signingEmployeeId: string | null; + + @ApiProperty({ enum: SignerType }) + signerType: SignerType; + + @IsString() + @ApiProperty() + formInstanceId: string; + + constructor(partial: Partial) { + Object.assign(this, partial); + } +} + +export class AssignedGroupEntity extends AssignedGroupBaseEntity { + @ApiProperty() + fieldGroup: FieldGroupBaseEntity; + + @IsOptional() + @ApiPropertyOptional() + signingEmployee: EmployeeBaseEntity | null; + + @IsOptional() + @ApiPropertyOptional() + signerPosition: PositionBaseEntity | null; + + @IsOptional() + @ApiPropertyOptional() + signerDepartment: DepartmentEntity | null; + + @IsOptional() + @ApiPropertyOptional() + signerEmployee: EmployeeBaseEntity | null; + + @IsOptional() + @ApiPropertyOptional({ + type: EmployeeBaseEntity, + isArray: true, + }) + signerEmployeeList: EmployeeBaseEntity[] | null; + + constructor(partial: Partial) { + super(partial); + if (partial.fieldGroup) { + partial.fieldGroup = new FieldGroupBaseEntity(partial.fieldGroup); + } + if (partial.signingEmployee) { + partial.signingEmployee = new EmployeeBaseEntity(partial.signingEmployee); + } + if (partial.signerEmployeeList) { + partial.signerEmployeeList = partial.signerEmployeeList.map( + (employee) => new EmployeeBaseEntity(employee), + ); + } + Object.assign(this, partial); + } +} diff --git a/apps/server/src/employees/employees.service.ts b/apps/server/src/employees/employees.service.ts index 6e5f457f..c0cb4c19 100644 --- a/apps/server/src/employees/employees.service.ts +++ b/apps/server/src/employees/employees.service.ts @@ -152,7 +152,7 @@ export class EmployeesService { position: { include: { department: true, - signatures: true, + assignedGroups: true, }, }, }, diff --git a/apps/server/src/field-group/dto/create-field-group.dto.ts b/apps/server/src/field-group/dto/create-field-group.dto.ts new file mode 100644 index 00000000..04c2723a --- /dev/null +++ b/apps/server/src/field-group/dto/create-field-group.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { + ArrayMinSize, + IsArray, + IsInt, + IsNotEmpty, + IsString, +} from 'class-validator'; +import { CreateTemplateBoxDto } from './create-template-box.dto'; + +export class CreateFieldGroupDto { + @IsString() + @IsNotEmpty() + @ApiProperty() + name: string; + + @IsInt() + @IsNotEmpty() + @ApiProperty() + order: number; + + @IsArray() + @ArrayMinSize(1) + @ApiProperty({ + isArray: true, + type: CreateTemplateBoxDto, + }) + templateBoxes: CreateTemplateBoxDto[]; +} diff --git a/apps/server/src/field-group/dto/create-template-box.dto.ts b/apps/server/src/field-group/dto/create-template-box.dto.ts new file mode 100644 index 00000000..c38fbca4 --- /dev/null +++ b/apps/server/src/field-group/dto/create-template-box.dto.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString, IsNotEmpty, IsInt } from 'class-validator'; + +export class CreateTemplateBoxDto { + @IsString() + @IsNotEmpty() + @ApiProperty() + name: string; + + @ApiProperty({ + enum: ['signature', 'checkbox'], + }) + type: any; + + @IsInt() + @IsNotEmpty() + @ApiProperty() + x_coordinate: number; + + @IsInt() + @IsNotEmpty() + @ApiProperty() + y_coordinate: number; + + @IsString() + @IsNotEmpty() + @ApiProperty() + fieldGroupId: string; +} diff --git a/apps/server/src/field-group/entities/field-group.entity.ts b/apps/server/src/field-group/entities/field-group.entity.ts new file mode 100644 index 00000000..357e1139 --- /dev/null +++ b/apps/server/src/field-group/entities/field-group.entity.ts @@ -0,0 +1,39 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { FieldGroup } from '@prisma/client'; +import { TemplateBoxBaseEntity } from './template-box.entity'; + +export class FieldGroupBaseEntity implements FieldGroup { + @ApiProperty() + id: string; + + @ApiProperty() + name: string; + + @ApiProperty() + order: number; + + @ApiProperty() + createdAt: Date; + + @ApiProperty() + updatedAt: Date; + + @ApiProperty() + formTemplateId: string; + + @ApiProperty({ + isArray: true, + type: TemplateBoxBaseEntity, + }) + templateBoxes: TemplateBoxBaseEntity[]; + + constructor(partial: Partial) { + if (partial.templateBoxes) { + partial.templateBoxes = partial.templateBoxes.map( + (templateBox) => new TemplateBoxBaseEntity(templateBox), + ); + } + + Object.assign(this, partial); + } +} diff --git a/apps/server/src/field-group/entities/template-box.entity.ts b/apps/server/src/field-group/entities/template-box.entity.ts new file mode 100644 index 00000000..a5b0de47 --- /dev/null +++ b/apps/server/src/field-group/entities/template-box.entity.ts @@ -0,0 +1,30 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { SignatureBoxFieldType, TemplateBox } from '@prisma/client'; +import { Exclude } from 'class-transformer'; + +export class TemplateBoxBaseEntity implements TemplateBox { + @ApiProperty() + id: string; + + @ApiProperty({ enum: SignatureBoxFieldType }) + type: SignatureBoxFieldType; + + @ApiProperty() + x_coordinate: number; + + @ApiProperty() + y_coordinate: number; + + @Exclude() + fieldGroupId: string; + + @ApiProperty() + createdAt: Date; + + @ApiProperty() + updatedAt: Date; + + constructor(partial: Partial) { + Object.assign(this, partial); + } +} diff --git a/apps/server/src/form-instances/dto/create-form-instance.dto.ts b/apps/server/src/form-instances/dto/create-form-instance.dto.ts index 9e9504c8..526a62a7 100644 --- a/apps/server/src/form-instances/dto/create-form-instance.dto.ts +++ b/apps/server/src/form-instances/dto/create-form-instance.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { CreateSignatureDto } from '../../signatures/dto/create-signature.dto'; import { ArrayMinSize, IsArray, IsNotEmpty, IsString } from 'class-validator'; +import { CreateAssignedGroupDto } from '../../assigned-group/dto/create-assigned-group.dto'; export class CreateFormInstanceDto { @IsString() @@ -12,9 +12,9 @@ export class CreateFormInstanceDto { @ArrayMinSize(1) @ApiProperty({ isArray: true, - type: CreateSignatureDto, + type: CreateAssignedGroupDto, }) - signatures: CreateSignatureDto[]; + assignedGroups: CreateAssignedGroupDto[]; @IsString() @IsNotEmpty() diff --git a/apps/server/src/form-instances/dto/update-form-instance.dto.ts b/apps/server/src/form-instances/dto/update-form-instance.dto.ts index 8c668653..0e19d06e 100644 --- a/apps/server/src/form-instances/dto/update-form-instance.dto.ts +++ b/apps/server/src/form-instances/dto/update-form-instance.dto.ts @@ -3,7 +3,7 @@ import { CreateFormInstanceDto } from './create-form-instance.dto'; export class UpdateFormInstanceDto extends PartialType( OmitType(CreateFormInstanceDto, [ - 'signatures', + 'assignedGroups', 'originatorId', 'formTemplateId', ] as const), diff --git a/apps/server/src/form-instances/entities/form-instance.entity.ts b/apps/server/src/form-instances/entities/form-instance.entity.ts index 027e14ca..d1465650 100644 --- a/apps/server/src/form-instances/entities/form-instance.entity.ts +++ b/apps/server/src/form-instances/entities/form-instance.entity.ts @@ -2,8 +2,8 @@ import { ApiProperty } from '@nestjs/swagger'; import { FormInstance } from '@prisma/client'; import { FormTemplateBaseEntity } from '../../form-templates/entities/form-template.entity'; import { Exclude } from 'class-transformer'; -import { SignatureEntity } from '../../signatures/entities/signature.entity'; import { EmployeeEntity } from '../../employees/entities/employee.entity'; +import { AssignedGroupEntity } from '../../assigned-group/entities/assigned-group.entity'; export class FormInstanceBaseEntity implements FormInstance { @ApiProperty() @@ -46,7 +46,7 @@ export class FormInstanceBaseEntity implements FormInstance { formTemplate: FormTemplateBaseEntity; @Exclude() - signatures: SignatureEntity[]; + assignedGroups: AssignedGroupEntity[]; constructor(partial: Partial) { Object.assign(this, partial); @@ -99,9 +99,9 @@ export class FormInstanceEntity implements FormInstance { @ApiProperty({ isArray: true, - type: SignatureEntity, + type: AssignedGroupEntity, }) - signatures: SignatureEntity[]; + assignedGroups: AssignedGroupEntity[]; constructor(partial: Partial) { if (partial.originator) { @@ -113,9 +113,9 @@ export class FormInstanceEntity implements FormInstance { if (partial.originator) { partial.originator = new EmployeeEntity(partial.originator); } - if (partial.signatures) { - partial.signatures = partial.signatures.map( - (signature) => new SignatureEntity(signature), + if (partial.assignedGroups) { + partial.assignedGroups = partial.assignedGroups.map( + (assignedGroup) => new AssignedGroupEntity(assignedGroup), ); } diff --git a/apps/server/src/form-instances/form-instance.errors.ts b/apps/server/src/form-instances/form-instance.errors.ts index 1d088065..6ca3367c 100644 --- a/apps/server/src/form-instances/form-instance.errors.ts +++ b/apps/server/src/form-instances/form-instance.errors.ts @@ -1,7 +1,7 @@ export enum FormInstanceErrorMessage { FORM_INSTANCE_NOT_FOUND = 'Form instance could not be found with this id', FORM_INSTANCE_NOT_FOUND_CLIENT = 'Form instance could not be found', - FORM_INSTANCE_INVALID_NUMBER_OF_SIGNATURES = 'Invalid number of signatures specified', + FORM_INSTANCE_INVALID_NUMBER_OF_ASSIGNED_GROUPS = 'Invalid number of assigned groups specified', FORM_INSTANCE_NOT_COMPLETED = 'Form instance has not been completely signed', FORM_INSTANCE_INVALID_MARKED_COMPLETED = 'Form instance cannot be marked completed', } diff --git a/apps/server/src/form-instances/form-instances.controller.ts b/apps/server/src/form-instances/form-instances.controller.ts index 0016fb7e..d3dc6a15 100644 --- a/apps/server/src/form-instances/form-instances.controller.ts +++ b/apps/server/src/form-instances/form-instances.controller.ts @@ -188,13 +188,13 @@ export class FormInstancesController { async signFormInstance( @AuthUser() currentUser: UserEntity, @Param('formInstanceId') formInstanceId: string, - @Param('signatureId') signatureId: string, + @Param('assignedGroupId') assignedGroupId: string, ) { try { const updatedFormInstance = await this.formInstancesService.signFormInstance( formInstanceId, - signatureId, + assignedGroupId, currentUser, ); return new FormInstanceEntity(updatedFormInstance); diff --git a/apps/server/src/form-instances/form-instances.service.spec.ts b/apps/server/src/form-instances/form-instances.service.spec.ts index d9c44a45..6ae4dc66 100644 --- a/apps/server/src/form-instances/form-instances.service.spec.ts +++ b/apps/server/src/form-instances/form-instances.service.spec.ts @@ -3,7 +3,7 @@ import { FormInstancesService } from './form-instances.service'; import { PrismaService } from '../prisma/prisma.service'; import { FormTemplatesService } from '../form-templates/form-templates.service'; import { PositionsService } from '../positions/positions.service'; -import { Prisma, SignerType } from '@prisma/client'; +import { Prisma, SignatureBoxFieldType, SignerType } from '@prisma/client'; import { FormTemplateErrorMessage } from '../form-templates/form-templates.errors'; import { FormInstanceErrorMessage } from './form-instance.errors'; import { PositionsErrorMessage } from '../positions/positions.errors'; @@ -98,6 +98,27 @@ const formTemplate = { }, ], formInstances: [], + fieldGroups: [ + { + id: 'fieldGroupId', + name: 'Field-Group-1', + order: 0, + formTemplateId: formTemplateId, + templateBoxes: [ + { + id: 'templateBoxId', + type: SignatureBoxFieldType.TEXT_FIELD, + x_coordinate: 0, + y_coordinate: 0, + createdAt: new Date(1672531200), + updatedAt: new Date(1672531200), + fieldGroupId: 'fieldGroupId', + }, + ], + createdAt: new Date(1672531200), + updatedAt: new Date(1672531200), + }, + ], createdAt: new Date(1672531200), updatedAt: new Date(1672531200), }; @@ -219,6 +240,9 @@ const db = { formTemplate: { findFirstOrThrow: jest.fn().mockResolvedValue(formTemplate), }, + fieldGroup: { + findMany: jest.fn().mockResolvedValue(formTemplate.fieldGroups), + }, }; const mockPostmarkService = { @@ -278,12 +302,13 @@ describe('FormInstancesService', () => { it('should successfully create a form instance', () => { const createFormInstanceDto = { name: formInstance1Name, - signatures: [ + assignedGroups: [ { + fieldGroupId: 'fieldGroupId', order: 0, signerPositionId: positionId3, - signerEmployeeId: null, - signerDepartmentId: null, + signerEmployeeId: undefined, + signerDepartmentId: undefined, signerEmployeeList: [], signerType: SignerType.POSITION, }, @@ -309,12 +334,13 @@ describe('FormInstancesService', () => { it('should fail when invalid form template is specified', () => { const createFormInstanceDto = { name: formInstance1Name, - signatures: [ + assignedGroups: [ { + fieldGroupId: 'assignedGroupId', order: 0, signerPositionId: positionId3, - signerEmployeeId: null, - signerDepartmentId: null, + signerEmployeeId: undefined, + signerDepartmentId: undefined, signerEmployeeList: [], signerType: SignerType.POSITION, }, @@ -343,7 +369,7 @@ describe('FormInstancesService', () => { it('should fail when number of signatures does not equal number of signature fields', () => { const createFormInstanceDto = { name: formInstance1Name, - signatures: [], + assignedGroups: [], originatorId: 'originator-id', formTemplateId: 'form-template-id', formDocLink: 'form-doc-link', @@ -351,7 +377,7 @@ describe('FormInstancesService', () => { expect(service.create(createFormInstanceDto)).rejects.toThrowError( new Error( - FormInstanceErrorMessage.FORM_INSTANCE_INVALID_NUMBER_OF_SIGNATURES, + FormInstanceErrorMessage.FORM_INSTANCE_INVALID_NUMBER_OF_ASSIGNED_GROUPS, ), ); }); @@ -359,12 +385,13 @@ describe('FormInstancesService', () => { it('should throw an error when an invalid position id is specified when signer type is position', () => { const createFormInstanceDto = { name: formInstance1Name, - signatures: [ + assignedGroups: [ { + fieldGroupId: 'fieldGroupId', order: 0, signerPositionId: 'invalid-position-id', - signerEmployeeId: null, - signerDepartmentId: null, + signerEmployeeId: undefined, + signerDepartmentId: undefined, signerEmployeeList: [], signerType: SignerType.POSITION, }, @@ -388,12 +415,13 @@ describe('FormInstancesService', () => { it('should throw an error when an invalid employee id is specified when signer type is employee', () => { const createFormInstanceDto = { name: formInstance1Name, - signatures: [ + assignedGroups: [ { + fieldGroupId: 'fieldGroupId', order: 0, - signerPositionId: null, + signerPositionId: undefined, signerEmployeeId: 'invalid-employee-id', - signerDepartmentId: null, + signerDepartmentId: undefined, signerEmployeeList: [], signerType: SignerType.USER, }, @@ -417,11 +445,12 @@ describe('FormInstancesService', () => { it('should throw an error when an invalid department id is specified when signer type is department', () => { const createFormInstanceDto = { name: formInstance1Name, - signatures: [ + assignedGroups: [ { + fieldGroupId: 'fieldGroupId', order: 0, - signerPositionId: null, - signerEmployeeId: null, + signerPositionId: undefined, + signerEmployeeId: undefined, signerDepartmentId: 'invalid-department-id', signerEmployeeList: [], signerType: SignerType.DEPARTMENT, @@ -446,12 +475,13 @@ describe('FormInstancesService', () => { it('should throw an error when an invalid employee id is specified in the employee list when signer type is employee list', () => { const createFormInstanceDto = { name: formInstance1Name, - signatures: [ + assignedGroups: [ { + fieldGroupId: 'fieldGroupId', order: 0, - signerPositionId: null, - signerEmployeeId: null, - signerDepartmentId: null, + signerPositionId: undefined, + signerEmployeeId: undefined, + signerDepartmentId: undefined, signerEmployeeList: [{ id: 'invalid-employee-id' }], signerType: SignerType.USER_LIST, }, diff --git a/apps/server/src/form-instances/form-instances.service.ts b/apps/server/src/form-instances/form-instances.service.ts index 9dda6ab7..f77c4f55 100644 --- a/apps/server/src/form-instances/form-instances.service.ts +++ b/apps/server/src/form-instances/form-instances.service.ts @@ -11,7 +11,6 @@ import { EmployeesService } from '../employees/employees.service'; import { FormInstance } from '@prisma/client'; import { FormTemplateErrorMessage } from '../form-templates/form-templates.errors'; import { FormInstanceErrorMessage } from './form-instance.errors'; -import { SignatureErrorMessage } from '../signatures/signatures.errors'; import { EmployeeErrorMessage } from '../employees/employees.errors'; import { PositionsErrorMessage } from '../positions/positions.errors'; import { DepartmentsErrorMessage } from '../departments/departments.errors'; @@ -20,7 +19,8 @@ import { PostmarkService } from '../postmark/postmark.service'; import { PositionsService } from '../positions/positions.service'; import { DepartmentsService } from '../departments/departments.service'; import { SignerType } from '@prisma/client'; -import { CreateSignatureDto } from '../signatures/dto/create-signature.dto'; +import { AssignedGroupErrorMessage } from '../assigned-group/assigned-group.errors'; +import { CreateAssignedGroupDto } from '../assigned-group/dto/create-assigned-group.dto'; @Injectable() export class FormInstancesService { @@ -33,9 +33,11 @@ export class FormInstancesService { private postmarkService: PostmarkService, ) {} - async checkValidSignaturesSigner(signatures: CreateSignatureDto[]) { - for (let i = 0; i < signatures.length; i++) { - const signature = signatures[i]; + async checkValidAssignedGroupsSigner( + assignedGroups: CreateAssignedGroupDto[], + ) { + for (let i = 0; i < assignedGroups.length; i++) { + const signature = assignedGroups[i]; if (signature.signerType === SignerType.USER) { const employeeId = signature.signerEmployeeId as string; @@ -98,31 +100,53 @@ export class FormInstancesService { ); } - // check that the form template and form instance have the same number of signatures + // check that the form template and form instance have the same number of groups if ( - createFormInstanceDto.signatures.length !== - formTemplate.signatureFields.length + createFormInstanceDto.assignedGroups.length !== + formTemplate.fieldGroups.length ) { throw new BadRequestException( - FormInstanceErrorMessage.FORM_INSTANCE_INVALID_NUMBER_OF_SIGNATURES, + FormInstanceErrorMessage.FORM_INSTANCE_INVALID_NUMBER_OF_ASSIGNED_GROUPS, ); } + // need mapping of each assignedGroup to template boxes + const fieldGroups = await this.prisma.fieldGroup.findMany({ + where: { + id: { + in: formTemplate.fieldGroups.map((group) => group.id), + }, + }, + include: { + templateBoxes: true, + }, + }); // check that the assigned signer exists for each signature - await this.checkValidSignaturesSigner(createFormInstanceDto.signatures); + await this.checkValidAssignedGroupsSigner( + createFormInstanceDto.assignedGroups, + ); const newFormInstance = await this.prisma.formInstance.create({ data: { name: createFormInstanceDto.name, formDocLink: createFormInstanceDto.formDocLink, - signatures: { - create: createFormInstanceDto.signatures.map((signature) => ({ - ...signature, + assignedGroups: { + create: createFormInstanceDto.assignedGroups.map((assignedGroup) => ({ + ...assignedGroup, signerEmployeeList: { - connect: signature.signerEmployeeList.map((user) => ({ + connect: assignedGroup.signerEmployeeList.map((user) => ({ id: user.id, })), }, + instanceBoxes: { + create: fieldGroups + .find( + (fieldGroup) => fieldGroup.id === assignedGroup.fieldGroupId, + ) + ?.templateBoxes.map((templateBox) => ({ + templateBoxId: templateBox.id, + })), + }, })), }, originator: { @@ -147,7 +171,7 @@ export class FormInstancesService { }, }, formTemplate: true, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -158,6 +182,12 @@ export class FormInstancesService { signerEmployee: true, signerEmployeeList: true, signingEmployee: true, + instanceBoxes: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, @@ -189,7 +219,7 @@ export class FormInstancesService { const formInstances = await this.prisma.formInstance.findMany({ where: { - signatures: { + assignedGroups: { some: { OR: [ { @@ -227,7 +257,7 @@ export class FormInstancesService { }, }, formTemplate: true, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -238,6 +268,12 @@ export class FormInstancesService { signerEmployee: true, signerEmployeeList: true, signingEmployee: true, + instanceBoxes: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, @@ -269,7 +305,7 @@ export class FormInstancesService { }, }, formTemplate: true, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -280,6 +316,12 @@ export class FormInstancesService { signerEmployee: true, signerEmployeeList: true, signingEmployee: true, + instanceBoxes: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, @@ -306,7 +348,7 @@ export class FormInstancesService { }, }, formTemplate: true, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -317,6 +359,12 @@ export class FormInstancesService { signerEmployee: true, signerEmployeeList: true, signingEmployee: true, + instanceBoxes: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, @@ -345,7 +393,7 @@ export class FormInstancesService { }, }, formTemplate: true, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -356,6 +404,12 @@ export class FormInstancesService { signerEmployee: true, signerEmployeeList: true, signingEmployee: true, + instanceBoxes: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, @@ -389,7 +443,7 @@ export class FormInstancesService { }, }, formTemplate: true, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -400,6 +454,12 @@ export class FormInstancesService { signerEmployee: true, signerEmployeeList: true, signingEmployee: true, + instanceBoxes: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, @@ -419,7 +479,7 @@ export class FormInstancesService { include: { originator: true, formTemplate: true, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -427,6 +487,15 @@ export class FormInstancesService { }, }, signingEmployee: true, + signerDepartment: true, + signerEmployee: true, + signerEmployeeList: true, + instanceBoxes: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, @@ -436,13 +505,13 @@ export class FormInstancesService { /** * Sign a form instance. * @param formInstanceId the form instance id - * @param signatureId the signature id + * @param assignedGroupId the assigned group id id * @param currentUser the current user to sign the form * @returns the updated form instance, hydrated */ async signFormInstance( formInstanceId: string, - signatureId: string, + assignedGroupId: string, currentUser: UserEntity, ) { const formInstance = await this.findOne(formInstanceId); @@ -461,46 +530,56 @@ export class FormInstancesService { where: { id: employee.positionId }, }); - const signatureIndex = formInstance.signatures.findIndex( - (sig) => sig.id === signatureId, + const assignedGroupIndex = formInstance.assignedGroups.findIndex( + (assignedGroup) => assignedGroup.id === assignedGroupId, ); - if (signatureIndex === -1) { - throw new NotFoundException(SignatureErrorMessage.SIGNATURE_NOT_FOUND); + if (assignedGroupIndex === -1) { + throw new NotFoundException( + AssignedGroupErrorMessage.ASSIGNED_GROUP_NOT_FOUND, + ); } - for (let i = 0; i < signatureIndex; i++) { - if (!formInstance.signatures[i].signed) { - throw new BadRequestException(SignatureErrorMessage.SIGNATURE_NOT_NEXT); + for (let i = 0; i < assignedGroupIndex; i++) { + if (!formInstance.assignedGroups[i].signed) { + throw new BadRequestException( + AssignedGroupErrorMessage.ASSIGNED_GROUP_NOT_NEXT, + ); } } - const signature = formInstance.signatures[signatureIndex]; + const assignedGroup = formInstance.assignedGroups[assignedGroupIndex]; if ( - (signature.signerType === SignerType.USER && - signature.signerEmployeeId !== employee.id) || - (signature.signerType === SignerType.POSITION && - signature.signerPositionId !== employee.positionId) || - (signature.signerType === SignerType.DEPARTMENT && - signature.signerDepartmentId !== position.departmentId) || - (signature.signerType === SignerType.USER_LIST && - signature.signerEmployeeList && - !signature.signerEmployeeList.some((user) => user.id === employee.id)) + (assignedGroup.signerType === SignerType.USER && + assignedGroup.signerEmployeeId !== employee.id) || + (assignedGroup.signerType === SignerType.POSITION && + assignedGroup.signerPositionId !== employee.positionId) || + (assignedGroup.signerType === SignerType.DEPARTMENT && + assignedGroup.signerDepartmentId !== position.departmentId) || + (assignedGroup.signerType === SignerType.USER_LIST && + assignedGroup.signerEmployeeList && + !assignedGroup.signerEmployeeList.some( + (user) => user.id === employee.id, + )) ) { - throw new BadRequestException(SignatureErrorMessage.EMPLOYEE_CANNOT_SIGN); + throw new BadRequestException( + AssignedGroupErrorMessage.EMPLOYEE_CANNOT_SIGN, + ); } - const updatedSignature = await this.prisma.signature.update({ - where: { id: signatureId }, + const updatedAssignedGroup = await this.prisma.assignedGroup.update({ + where: { id: assignedGroupId }, data: { signed: true, signingEmployeeId: employee.id }, }); - formInstance.signatures[signatureIndex] = { - ...formInstance.signatures[signatureIndex], - ...updatedSignature, + formInstance.assignedGroups[assignedGroupIndex] = { + ...formInstance.assignedGroups[assignedGroupIndex], + ...updatedAssignedGroup, }; - const allSigned = formInstance.signatures.every((sig) => sig.signed); + const allSigned = formInstance.assignedGroups.every( + (assignedGroup) => assignedGroup.signed, + ); let updatedFormInstance = (await this.findOne( formInstanceId, @@ -511,7 +590,7 @@ export class FormInstancesService { where: { id: formInstanceId }, data: { completed: true, completedAt: new Date() }, include: { - signatures: { + assignedGroups: { include: { signerPosition: true, signingEmployee: true }, }, }, @@ -525,7 +604,8 @@ export class FormInstancesService { ); } else { // Notify next user that form is ready to sign - const nextUserToSignId = formInstance.signatures[signatureIndex + 1]; + const nextUserToSignId = + formInstance.assignedGroups[assignedGroupIndex + 1]; if (nextUserToSignId.signerType === SignerType.USER) { this.postmarkService.sendReadyForSignatureToUserEmail( diff --git a/apps/server/src/form-templates/dto/create-form-template.dto.ts b/apps/server/src/form-templates/dto/create-form-template.dto.ts index 28b8fb2a..f307ff72 100644 --- a/apps/server/src/form-templates/dto/create-form-template.dto.ts +++ b/apps/server/src/form-templates/dto/create-form-template.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { CreateSignatureFieldDto } from '../../signature-fields/dto/create-signature-field.dto'; import { ArrayMinSize, IsArray, IsNotEmpty, IsString } from 'class-validator'; +import { CreateFieldGroupDto } from '../../field-group/dto/create-field-group.dto'; export class CreateFormTemplateDto { @IsString() @@ -17,7 +17,7 @@ export class CreateFormTemplateDto { @ArrayMinSize(1) @ApiProperty({ isArray: true, - type: CreateSignatureFieldDto, + type: CreateFieldGroupDto, }) - signatureFields: CreateSignatureFieldDto[]; + fieldGroups: CreateFieldGroupDto[]; } diff --git a/apps/server/src/form-templates/dto/update-form-template.dto.ts b/apps/server/src/form-templates/dto/update-form-template.dto.ts index 13bbe671..b24e0c22 100644 --- a/apps/server/src/form-templates/dto/update-form-template.dto.ts +++ b/apps/server/src/form-templates/dto/update-form-template.dto.ts @@ -2,5 +2,5 @@ import { OmitType, PartialType } from '@nestjs/swagger'; import { CreateFormTemplateDto } from './create-form-template.dto'; export class UpdateFormTemplateDto extends PartialType( - OmitType(CreateFormTemplateDto, ['signatureFields'] as const), + OmitType(CreateFormTemplateDto, ['fieldGroups'] as const), ) {} diff --git a/apps/server/src/form-templates/entities/form-template.entity.ts b/apps/server/src/form-templates/entities/form-template.entity.ts index 5ae38360..9d039f14 100644 --- a/apps/server/src/form-templates/entities/form-template.entity.ts +++ b/apps/server/src/form-templates/entities/form-template.entity.ts @@ -1,11 +1,8 @@ import { ApiProperty } from '@nestjs/swagger'; import { FormTemplate } from '@prisma/client'; import { Exclude } from 'class-transformer'; -import { SignatureFieldEntity } from './../../signature-fields/entities/signature-field.entity'; -import { - FormInstanceBaseEntity, - FormInstanceEntity, -} from './../../form-instances/entities/form-instance.entity'; +import { FormInstanceEntity } from './../../form-instances/entities/form-instance.entity'; +import { FieldGroupBaseEntity } from '../../field-group/entities/field-group.entity'; export class FormTemplateBaseEntity implements FormTemplate { @ApiProperty() @@ -29,22 +26,25 @@ export class FormTemplateBaseEntity implements FormTemplate { } export class FormTemplateEntity extends FormTemplateBaseEntity { - @ApiProperty() - signatureFields: SignatureFieldEntity[]; + @ApiProperty({ + isArray: true, + type: FieldGroupBaseEntity, + }) + fieldGroups: FieldGroupBaseEntity[]; @ApiProperty() formInstances: FormInstanceEntity[]; constructor(partial: Partial) { super(partial); - if (partial.signatureFields) { - partial.signatureFields = partial.signatureFields.map( - (signatureField) => new SignatureFieldEntity(signatureField), + if (partial.fieldGroups) { + partial.fieldGroups = partial.fieldGroups.map( + (fieldGroup) => new FieldGroupBaseEntity(fieldGroup), ); } if (partial.formInstances) { partial.formInstances = partial.formInstances.map( - (formInstance) => new FormInstanceBaseEntity(formInstance), + (formInstance) => new FormInstanceEntity(formInstance), ); } Object.assign(this, partial); diff --git a/apps/server/src/form-templates/form-templates.service.ts b/apps/server/src/form-templates/form-templates.service.ts index b8fd9f36..5bdd98b2 100644 --- a/apps/server/src/form-templates/form-templates.service.ts +++ b/apps/server/src/form-templates/form-templates.service.ts @@ -17,10 +17,31 @@ export class FormTemplatesService { data: { name: createFormTemplateDto.name, formDocLink: createFormTemplateDto.formDocLink, - signatureFields: { create: createFormTemplateDto.signatureFields }, + fieldGroups: { + create: createFormTemplateDto.fieldGroups.map((fieldGroup) => { + return { + name: fieldGroup.name, + order: fieldGroup.order, + templateBoxes: { + create: fieldGroup.templateBoxes.map((templateBox) => { + return { + name: templateBox.name, + type: templateBox.type, + x_coordinate: templateBox.x_coordinate, + y_coordinate: templateBox.y_coordinate, + }; + }), + }, + }; + }), + }, }, include: { - signatureFields: true, + fieldGroups: { + include: { + templateBoxes: true, + }, + }, formInstances: { include: { formTemplate: true, @@ -33,7 +54,7 @@ export class FormTemplatesService { }, }, }, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -44,6 +65,11 @@ export class FormTemplatesService { signerEmployee: true, signerEmployeeList: true, signingEmployee: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, @@ -63,7 +89,11 @@ export class FormTemplatesService { ? await this.prisma.formTemplate.findMany({ take: limit, include: { - signatureFields: true, + fieldGroups: { + include: { + templateBoxes: true, + }, + }, formInstances: { include: { formTemplate: true, @@ -76,7 +106,7 @@ export class FormTemplatesService { }, }, }, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -87,6 +117,11 @@ export class FormTemplatesService { signerEmployee: true, signerEmployeeList: true, signingEmployee: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, @@ -95,7 +130,11 @@ export class FormTemplatesService { }) : await this.prisma.formTemplate.findMany({ include: { - signatureFields: true, + fieldGroups: { + include: { + templateBoxes: true, + }, + }, formInstances: { include: { formTemplate: true, @@ -108,7 +147,7 @@ export class FormTemplatesService { }, }, }, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -119,6 +158,11 @@ export class FormTemplatesService { signerEmployee: true, signerEmployeeList: true, signingEmployee: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, @@ -139,7 +183,11 @@ export class FormTemplatesService { id: id, }, include: { - signatureFields: true, + fieldGroups: { + include: { + templateBoxes: true, + }, + }, formInstances: { include: { formTemplate: true, @@ -152,7 +200,7 @@ export class FormTemplatesService { }, }, }, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -163,6 +211,11 @@ export class FormTemplatesService { signerEmployee: true, signerEmployeeList: true, signingEmployee: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, @@ -190,7 +243,11 @@ export class FormTemplatesService { formDocLink: updateFormTemplateDto.formDocLink, }, include: { - signatureFields: true, + fieldGroups: { + include: { + templateBoxes: true, + }, + }, formInstances: { include: { formTemplate: true, @@ -203,7 +260,7 @@ export class FormTemplatesService { }, }, }, - signatures: { + assignedGroups: { include: { signerPosition: { include: { @@ -214,6 +271,11 @@ export class FormTemplatesService { signerEmployee: true, signerEmployeeList: true, signingEmployee: true, + fieldGroup: { + include: { + templateBoxes: true, + }, + }, }, }, }, diff --git a/apps/server/src/metadata.ts b/apps/server/src/metadata.ts deleted file mode 100644 index 24609201..00000000 --- a/apps/server/src/metadata.ts +++ /dev/null @@ -1,525 +0,0 @@ -import { EmployeeScope } from '@prisma/client'; - -/* eslint-disable */ -export default async () => { - const t = { - ['./departments/entities/department.entity']: await import( - './departments/entities/department.entity' - ), - ['./employees/entities/employee.entity']: await import( - './employees/entities/employee.entity' - ), - ['./positions/entities/position.entity']: await import( - './positions/entities/position.entity' - ), - ['./signatures/dto/create-signature.dto']: await import( - './signatures/dto/create-signature.dto' - ), - ['./signature-fields/dto/create-signature-field.dto']: await import( - './signature-fields/dto/create-signature-field.dto' - ), - ['./signature-fields/entities/signature-field.entity']: await import( - './signature-fields/entities/signature-field.entity' - ), - ['./form-instances/entities/form-instance.entity']: await import( - './form-instances/entities/form-instance.entity' - ), - ['./form-templates/entities/form-template.entity']: await import( - './form-templates/entities/form-template.entity' - ), - ['./signatures/entities/signature.entity']: await import( - './signatures/entities/signature.entity' - ), - }; - return { - '@nestjs/swagger': { - models: [ - [ - import('./auth/entities/jwt.entity'), - { - JwtEntity: { access_token: { required: true, type: () => String } }, - }, - ], - [ - import('./departments/entities/department.entity'), - { - DepartmentEntity: { - id: { required: true, type: () => String }, - name: { required: true, type: () => String }, - createdAt: { required: true, type: () => Date }, - updatedAt: { required: true, type: () => Date }, - }, - }, - ], - [ - import('./positions/entities/position.entity'), - { - PositionBaseEntity: { - id: { required: true, type: () => String }, - name: { required: true, type: () => String }, - single: { required: true, type: () => Boolean }, - departmentId: { required: true, type: () => String }, - department: { - required: true, - type: () => - t['./departments/entities/department.entity'] - .DepartmentEntity, - }, - createdAt: { required: true, type: () => Date }, - updatedAt: { required: true, type: () => Date }, - }, - PositionEntity: { - employees: { - required: true, - type: () => [ - t['./employees/entities/employee.entity'].EmployeeBaseEntity, - ], - }, - }, - }, - ], - [ - import('./employees/entities/employee.entity'), - { - EmployeeBaseEntity: { - id: { required: true, type: () => String }, - firstName: { required: true, type: () => String }, - lastName: { required: true, type: () => String }, - positionId: { required: true, type: () => String }, - email: { required: true, type: () => String }, - pswdHash: { required: true, type: () => String, nullable: true }, - createdAt: { required: true, type: () => Date }, - updatedAt: { required: true, type: () => Date }, - }, - EmployeeEntity: { - position: { - required: true, - type: () => - t['./positions/entities/position.entity'].PositionBaseEntity, - }, - }, - }, - ], - [ - import('./employees/dto/create-employee.dto'), - { - CreateEmployeeDto: { - firstName: { required: true, type: () => String }, - lastName: { required: true, type: () => String }, - positionId: { required: true, type: () => String }, - email: { required: true, type: () => String }, - password: { required: true, type: () => String, minLength: 5 }, - scope: { required: true, type: () => EmployeeScope }, - }, - }, - ], - [ - import('./employees/dto/update-employee.dto'), - { UpdateEmployeeDto: {} }, - ], - [ - import('./auth/entities/user.entity'), - { - UserEntity: { - id: { required: true, type: () => String }, - email: { required: true, type: () => String }, - }, - }, - ], - [ - import('./positions/dto/create-position.dto'), - { - CreatePositionDto: { - name: { required: true, type: () => String }, - departmentId: { required: true, type: () => String }, - }, - }, - ], - [ - import('./positions/dto/update-position.dto'), - { UpdatePositionDto: {} }, - ], - [ - import('./signature-fields/dto/create-signature-field.dto'), - { - CreateSignatureFieldDto: { - name: { required: true, type: () => String }, - order: { required: true, type: () => Number }, - signerPositionId: { required: false, type: () => String }, - formTemplateId: { required: true, type: () => String }, - }, - }, - ], - [ - import('./signature-fields/dto/update-signature-field.dto'), - { UpdateSignatureFieldDto: {} }, - ], - [ - import('./signature-fields/entities/signature-field.entity'), - { - SignatureFieldEntity: { - id: { required: true, type: () => String }, - name: { required: true, type: () => String }, - order: { required: true, type: () => Number }, - signerPositionId: { - required: true, - type: () => String, - nullable: true, - }, - signerPosition: { - required: true, - type: () => - t['./positions/entities/position.entity'].PositionBaseEntity, - nullable: true, - }, - formTemplateId: { required: true, type: () => String }, - createdAt: { required: true, type: () => Date }, - updatedAt: { required: true, type: () => Date }, - }, - }, - ], - [ - import('./signatures/dto/create-signature.dto'), - { - CreateSignatureDto: { - order: { required: true, type: () => Number }, - signerPositionId: { required: true, type: () => String }, - }, - }, - ], - [ - import('./form-instances/dto/create-form-instance.dto'), - { - CreateFormInstanceDto: { - name: { required: true, type: () => String }, - signatures: { - required: true, - type: () => [ - t['./signatures/dto/create-signature.dto'].CreateSignatureDto, - ], - }, - originatorId: { required: true, type: () => String }, - formTemplateId: { required: true, type: () => String }, - }, - }, - ], - [ - import('./form-instances/dto/update-form-instance.dto'), - { UpdateFormInstanceDto: {} }, - ], - [ - import('./form-templates/dto/create-form-template.dto'), - { - CreateFormTemplateDto: { - name: { required: true, type: () => String }, - formDocLink: { required: true, type: () => String }, - signatureFields: { - required: true, - type: () => [ - t['./signature-fields/dto/create-signature-field.dto'] - .CreateSignatureFieldDto, - ], - }, - }, - }, - ], - [ - import('./form-templates/dto/update-form-template.dto'), - { UpdateFormTemplateDto: {} }, - ], - [ - import('./form-templates/entities/form-template.entity'), - { - FormTemplateBaseEntity: { - id: { required: true, type: () => String }, - name: { required: true, type: () => String }, - formDocLink: { required: true, type: () => String }, - createdAt: { required: true, type: () => Date }, - updatedAt: { required: true, type: () => Date }, - }, - FormTemplateEntity: { - signatureFields: { - required: true, - type: () => [ - t['./signature-fields/entities/signature-field.entity'] - .SignatureFieldEntity, - ], - }, - formInstances: { - required: true, - type: () => [ - t['./form-instances/entities/form-instance.entity'] - .FormInstanceEntity, - ], - }, - }, - }, - ], - [ - import('./signatures/entities/signature.entity'), - { - SignatureEntity: { - id: { required: true, type: () => String }, - order: { required: true, type: () => Number }, - signed: { required: true, type: () => Boolean }, - signedDocLink: { - required: true, - type: () => String, - nullable: true, - }, - createdAt: { required: true, type: () => Date }, - updatedAt: { required: true, type: () => Date }, - signerPositionId: { required: true, type: () => String }, - signerPosition: { - required: true, - type: () => - t['./positions/entities/position.entity'].PositionBaseEntity, - }, - userSignedById: { - required: true, - type: () => String, - nullable: true, - }, - userSignedBy: { - required: true, - type: () => - t['./employees/entities/employee.entity'].EmployeeBaseEntity, - nullable: true, - }, - formInstanceId: { required: true, type: () => String }, - }, - }, - ], - [ - import('./form-instances/entities/form-instance.entity'), - { - FormInstanceEntity: { - id: { required: true, type: () => String }, - name: { required: true, type: () => String }, - formDocLink: { required: true, type: () => String }, - completed: { required: true, type: () => Boolean }, - createdAt: { required: true, type: () => Date }, - updatedAt: { required: true, type: () => Date }, - originatorId: { required: true, type: () => String }, - originator: { required: true }, - formTemplateId: { required: true, type: () => String }, - formTemplate: { - required: true, - type: () => - t['./form-templates/entities/form-template.entity'] - .FormTemplateBaseEntity, - }, - signatures: { - required: true, - type: () => [ - t['./signatures/entities/signature.entity'].SignatureEntity, - ], - }, - }, - }, - ], - [ - import('./departments/dto/create-department.dto'), - { - CreateDepartmentDto: { - name: { required: true, type: () => String }, - }, - }, - ], - [ - import('./departments/dto/update-department.dto'), - { UpdateDepartmentDto: {} }, - ], - [ - import('./auth/dto/login.dto'), - { - LoginDto: { - email: { required: true, type: () => String }, - pass: { required: true, type: () => String }, - }, - }, - ], - [ - import('./signatures/dto/update-signature.dto'), - { UpdateSignatureDto: {} }, - ], - ], - controllers: [ - [ - import('./app.controller'), - { - AppController: { - getHello: { type: String }, - login: {}, - logout: {}, - }, - }, - ], - [ - import('./employees/employees.controller'), - { - EmployeesController: { - create: { - type: t['./employees/entities/employee.entity'].EmployeeEntity, - }, - findAll: { - type: [ - t['./employees/entities/employee.entity'].EmployeeEntity, - ], - }, - findMe: { - type: t['./employees/entities/employee.entity'].EmployeeEntity, - }, - findOne: { - type: t['./employees/entities/employee.entity'].EmployeeEntity, - }, - update: { - type: t['./employees/entities/employee.entity'].EmployeeEntity, - }, - remove: {}, - }, - }, - ], - [ - import('./positions/positions.controller'), - { - PositionsController: { - create: { - type: t['./positions/entities/position.entity'].PositionEntity, - }, - findAll: { - type: [ - t['./positions/entities/position.entity'].PositionEntity, - ], - }, - findOne: { - type: t['./positions/entities/position.entity'].PositionEntity, - }, - update: { - type: t['./positions/entities/position.entity'].PositionEntity, - }, - remove: {}, - }, - }, - ], - [ - import('./signature-fields/signature-fields.controller'), - { - SignatureFieldsController: { - create: { - type: t['./signature-fields/entities/signature-field.entity'] - .SignatureFieldEntity, - }, - findAll: { - type: [ - t['./signature-fields/entities/signature-field.entity'] - .SignatureFieldEntity, - ], - }, - findOne: { - type: t['./signature-fields/entities/signature-field.entity'] - .SignatureFieldEntity, - }, - update: { - type: t['./signature-fields/entities/signature-field.entity'] - .SignatureFieldEntity, - }, - remove: {}, - }, - }, - ], - [ - import('./form-instances/form-instances.controller'), - { - FormInstancesController: { - create: { - type: t['./form-instances/entities/form-instance.entity'] - .FormInstanceEntity, - }, - findAll: { - type: [ - t['./form-instances/entities/form-instance.entity'] - .FormInstanceEntity, - ], - }, - findAllAssignedToCurrentEmployee: { - type: [ - t['./form-instances/entities/form-instance.entity'] - .FormInstanceEntity, - ], - }, - findAllCreatedByCurrentEmployee: { - type: [ - t['./form-instances/entities/form-instance.entity'] - .FormInstanceEntity, - ], - }, - findOne: { - type: t['./form-instances/entities/form-instance.entity'] - .FormInstanceEntity, - }, - update: { - type: t['./form-instances/entities/form-instance.entity'] - .FormInstanceEntity, - }, - remove: {}, - signFormInstance: { type: Object }, - }, - }, - ], - [ - import('./form-templates/form-templates.controller'), - { - FormTemplatesController: { - create: { - type: t['./form-templates/entities/form-template.entity'] - .FormTemplateEntity, - }, - findAll: { - type: [ - t['./form-templates/entities/form-template.entity'] - .FormTemplateEntity, - ], - }, - findOne: { - type: t['./form-templates/entities/form-template.entity'] - .FormTemplateEntity, - }, - update: { - type: t['./form-templates/entities/form-template.entity'] - .FormTemplateEntity, - }, - remove: {}, - }, - }, - ], - [ - import('./departments/departments.controller'), - { - DepartmentsController: { - create: { - type: t['./departments/entities/department.entity'] - .DepartmentEntity, - }, - findAll: { - type: [ - t['./departments/entities/department.entity'] - .DepartmentEntity, - ], - }, - findOne: { - type: t['./departments/entities/department.entity'] - .DepartmentEntity, - }, - update: { - type: t['./departments/entities/department.entity'] - .DepartmentEntity, - }, - remove: {}, - }, - }, - ], - ], - }, - }; -}; diff --git a/apps/server/src/positions/entities/position.entity.ts b/apps/server/src/positions/entities/position.entity.ts index 21810229..54198c04 100644 --- a/apps/server/src/positions/entities/position.entity.ts +++ b/apps/server/src/positions/entities/position.entity.ts @@ -38,13 +38,10 @@ export class PositionBaseEntity implements Position { } export class PositionEntity extends PositionBaseEntity { - @IsOptional() - // @ApiProperty({ - // isArray: true, - // required: false, - // type: EmployeeBaseEntity, - // }) - employees?: EmployeeBaseEntity[]; + @ApiProperty({ + type: EmployeeBaseEntity, + }) + employees: EmployeeBaseEntity[]; constructor(partial: Partial) { super(partial); @@ -52,7 +49,7 @@ export class PositionEntity extends PositionBaseEntity { partial.employees = partial.employees.map( (employee) => new EmployeeBaseEntity(employee), ); - Object.assign(this, partial); } + Object.assign(this, partial); } } diff --git a/apps/server/src/signature-fields/dto/create-signature-field.dto.ts b/apps/server/src/signature-fields/dto/create-signature-field.dto.ts deleted file mode 100644 index 2ce0c90a..00000000 --- a/apps/server/src/signature-fields/dto/create-signature-field.dto.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { IsInt, IsNotEmpty, IsOptional, IsString } from 'class-validator'; - -export class CreateSignatureFieldDto { - @IsString() - @IsNotEmpty() - @ApiProperty() - name: string; - - @IsInt() - @ApiProperty() - order: number; - - @IsString() - @IsOptional() - @ApiPropertyOptional() - formTemplateId: string; -} diff --git a/apps/server/src/signature-fields/dto/update-signature-field.dto.ts b/apps/server/src/signature-fields/dto/update-signature-field.dto.ts deleted file mode 100644 index 8cfe7d41..00000000 --- a/apps/server/src/signature-fields/dto/update-signature-field.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { PartialType } from '@nestjs/swagger'; -import { CreateSignatureFieldDto } from './create-signature-field.dto'; - -export class UpdateSignatureFieldDto extends PartialType( - CreateSignatureFieldDto, -) {} diff --git a/apps/server/src/signature-fields/entities/signature-field.entity.ts b/apps/server/src/signature-fields/entities/signature-field.entity.ts deleted file mode 100644 index 83c1d7bd..00000000 --- a/apps/server/src/signature-fields/entities/signature-field.entity.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { SignatureField } from '@prisma/client'; -import { ApiProperty } from '@nestjs/swagger'; -import { Exclude } from 'class-transformer'; - -export class SignatureFieldEntity implements SignatureField { - @ApiProperty() - id: string; - - @ApiProperty() - name: string; - - @ApiProperty() - order: number; - - @Exclude() - formTemplateId: string; - - @Exclude() - createdAt: Date; - - @Exclude() - updatedAt: Date; - - constructor(partial: Partial) { - Object.assign(this, partial); - } -} diff --git a/apps/server/src/signature-fields/signature-fields.controller.spec.ts b/apps/server/src/signature-fields/signature-fields.controller.spec.ts deleted file mode 100644 index fb8de77c..00000000 --- a/apps/server/src/signature-fields/signature-fields.controller.spec.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { SignatureFieldsController } from './signature-fields.controller'; -import { SignatureFieldsService } from './signature-fields.service'; -import { LoggerServiceImpl } from '../logger/logger.service'; -import { SignatureFieldEntity } from './entities/signature-field.entity'; -import { NotFoundException } from '@nestjs/common'; -import { SignatureFieldErrorMessage } from './signature-fields.errors'; -import { CreateSignatureFieldDto } from './dto/create-signature-field.dto'; - -describe('SignatureFieldsController', () => { - let controller: SignatureFieldsController; - let service: SignatureFieldsService; - - const mockSignatureFieldService = { - create: jest.fn(), - findAll: jest.fn(), - findOne: jest.fn(), - update: jest.fn(), - remove: jest.fn(), - }; - - const mockLoggerService = { - error: jest.fn(), - }; - - const signatureField1 = { - id: '1', - name: 'Signature Field 1', - order: 1, - createdAt: new Date(), - updatedAt: new Date(), - formTemplateId: '123', - }; - - const signatureField2 = { - id: '2', - name: 'Signature Field 2', - order: 2, - createdAt: new Date(), - updatedAt: new Date(), - formTemplateId: '456', - }; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [SignatureFieldsController], - providers: [ - { - provide: SignatureFieldsService, - useValue: mockSignatureFieldService, - }, - { provide: LoggerServiceImpl, useValue: mockLoggerService }, - ], - }).compile(); - - controller = module.get( - SignatureFieldsController, - ); - service = module.get(SignatureFieldsService); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); - - describe('create', () => { - const createSignatureFieldDTO = { - name: 'Signature Field 1', - order: 1, - formTemplateId: '3', - }; - - const createdSignatureField = { - ...createSignatureFieldDTO, - id: '123', - createdAt: new Date(), - updatedAt: new Date(), - }; - - it('should call service.create and return the created signature field', async () => { - mockSignatureFieldService.create.mockResolvedValue(createdSignatureField); - - const result = await controller.create(createSignatureFieldDTO); - - expect(service.create).toHaveBeenCalledWith(createSignatureFieldDTO); - expect(result).toEqual(new SignatureFieldEntity(createdSignatureField)); - }); - - it('should throw an error for invalid data', async () => { - mockSignatureFieldService.create.mockRejectedValue( - new Error('Validation error'), - ); - - await expect( - controller.create({ - name: '', - order: -1, - formTemplateId: '', - }), - ).rejects.toThrow(); - }); - }); - - describe('findAll', () => { - it('should call service.findAll and return the fetched signature fields', async () => { - const signatureFields = [signatureField1, signatureField2]; - - mockSignatureFieldService.findAll.mockResolvedValue(signatureFields); - const result = await service.findAll(); - - expect(result).toEqual(signatureFields); - expect(mockSignatureFieldService.findAll).toHaveBeenCalled(); - }); - - it('should call service.findAll and return the fetched signature fields with a limit param when provided', async () => { - mockSignatureFieldService.findAll.mockResolvedValue([signatureField1]); - - const result = await controller.findAll(1); - - expect(service.findAll).toHaveBeenCalledWith(1); - expect(result).toEqual([new SignatureFieldEntity(signatureField1)]); - }); - }); - - describe('findOne', () => { - const createSignatureFieldDTO: CreateSignatureFieldDto = { - name: 'Signature Field 1', - order: 1, - formTemplateId: '3', - }; - - const createdSignatureField = { - ...createSignatureFieldDTO, - id: '123', - createdAt: new Date(), - updatedAt: new Date(), - }; - - it('should call service.create and return the created signature field', async () => { - mockSignatureFieldService.create.mockResolvedValue(createdSignatureField); - - const result = await controller.create(createSignatureFieldDTO); - - expect(service.create).toHaveBeenCalledWith(createSignatureFieldDTO); - expect(result).toEqual(new SignatureFieldEntity(createdSignatureField)); - }); - - it('should throw NotFoundException if signature field is not found', async () => { - mockSignatureFieldService.findOne.mockResolvedValue(null); - - await expect(controller.findOne('fakeId')).rejects.toThrow( - new NotFoundException( - SignatureFieldErrorMessage.SIGNATURE_FIELD_NOT_FOUND_CLIENT, - ), - ); - - expect(mockLoggerService.error).toHaveBeenCalledWith( - SignatureFieldErrorMessage.SIGNATURE_FIELD_NOT_FOUND, - ); - }); - }); - - describe('update', () => { - it('should call service.update and return the updated signature field', async () => { - const updateData = { name: 'UpdatedField' }; - const updatedSignatureField = { ...signatureField1, ...updateData }; - - mockSignatureFieldService.update.mockResolvedValue(updatedSignatureField); - - const result = await controller.update('1', updateData); - - expect(service.update).toHaveBeenCalledWith('1', updateData); - expect(result).toEqual(new SignatureFieldEntity(updatedSignatureField)); - }); - }); - - describe('remove', () => { - it('should delete the signature field', async () => { - mockSignatureFieldService.remove.mockResolvedValue(undefined); - - const result = await controller.remove('1'); - - expect(service.remove).toHaveBeenLastCalledWith('1'); - expect(result).toBeUndefined(); - }); - }); -}); diff --git a/apps/server/src/signature-fields/signature-fields.controller.ts b/apps/server/src/signature-fields/signature-fields.controller.ts deleted file mode 100644 index 3da705ab..00000000 --- a/apps/server/src/signature-fields/signature-fields.controller.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { - Controller, - Post, - Body, - Query, - Get, - Param, - NotFoundException, - Patch, - Delete, -} from '@nestjs/common'; -import { SignatureFieldsService } from './signature-fields.service'; -import { - ApiBadRequestResponse, - ApiCreatedResponse, - ApiForbiddenResponse, - ApiNotFoundResponse, - ApiOkResponse, - ApiTags, - ApiUnprocessableEntityResponse, -} from '@nestjs/swagger'; -import { SignatureFieldEntity } from './entities/signature-field.entity'; -import { AppErrorMessage } from '../app.errors'; -import { CreateSignatureFieldDto } from './dto/create-signature-field.dto'; -import { SignatureFieldErrorMessage } from './signature-fields.errors'; -import { UpdateSignatureFieldDto } from './dto/update-signature-field.dto'; -import { Prisma } from '@prisma/client'; -import { LoggerServiceImpl } from '../logger/logger.service'; - -@ApiTags('signature-fields') -@Controller('signature-fields') -export class SignatureFieldsController { - constructor( - private readonly signatureFieldsService: SignatureFieldsService, - private readonly loggerService: LoggerServiceImpl, - ) {} - - @Post() - @ApiCreatedResponse({ type: SignatureFieldEntity }) - @ApiForbiddenResponse({ description: AppErrorMessage.FORBIDDEN }) - @ApiUnprocessableEntityResponse({ - description: AppErrorMessage.UNPROCESSABLE_ENTITY, - }) - async create(@Body() createSignatureFieldDto: CreateSignatureFieldDto) { - const newSignatureField = await this.signatureFieldsService.create( - createSignatureFieldDto, - ); - - return new SignatureFieldEntity(newSignatureField); - } - - @Get() - @ApiOkResponse({ type: [SignatureFieldEntity] }) - @ApiForbiddenResponse({ description: AppErrorMessage.FORBIDDEN }) - @ApiBadRequestResponse({ description: AppErrorMessage.UNPROCESSABLE_ENTITY }) - async findAll(@Query('limit') limit?: number) { - const signatureFields = await this.signatureFieldsService.findAll(limit); - return signatureFields.map( - (signatureField) => new SignatureFieldEntity(signatureField), - ); - } - - @Get(':id') - @ApiOkResponse({ type: SignatureFieldEntity }) - @ApiForbiddenResponse({ description: AppErrorMessage.FORBIDDEN }) - @ApiNotFoundResponse({ description: AppErrorMessage.NOT_FOUND }) - @ApiBadRequestResponse({ description: AppErrorMessage.UNPROCESSABLE_ENTITY }) - async findOne(@Param('id') id: string) { - const signatureField = await this.signatureFieldsService.findOne(id); - - if (signatureField == null) { - this.loggerService.error( - SignatureFieldErrorMessage.SIGNATURE_FIELD_NOT_FOUND, - ); - throw new NotFoundException( - SignatureFieldErrorMessage.SIGNATURE_FIELD_NOT_FOUND_CLIENT, - ); - } - - return new SignatureFieldEntity(signatureField); - } - - @Patch(':id') - @ApiOkResponse({ type: SignatureFieldEntity }) - @ApiForbiddenResponse({ description: AppErrorMessage.FORBIDDEN }) - @ApiNotFoundResponse({ description: AppErrorMessage.NOT_FOUND }) - @ApiUnprocessableEntityResponse({ - description: AppErrorMessage.UNPROCESSABLE_ENTITY, - }) - @ApiBadRequestResponse({ description: AppErrorMessage.UNPROCESSABLE_ENTITY }) - async update( - @Param('id') id: string, - @Body() updateSignatureFieldDto: UpdateSignatureFieldDto, - ) { - try { - const updatedSignatureField = await this.signatureFieldsService.update( - id, - updateSignatureFieldDto, - ); - return new SignatureFieldEntity(updatedSignatureField); - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - if (e.code === 'P2025') { - this.loggerService.error( - SignatureFieldErrorMessage.SIGNATURE_FIELD_NOT_FOUND, - ); - throw new NotFoundException( - SignatureFieldErrorMessage.SIGNATURE_FIELD_NOT_FOUND_CLIENT, - ); - } - } - throw e; - } - } - - @Delete(':id') - @ApiOkResponse() - @ApiForbiddenResponse({ description: AppErrorMessage.FORBIDDEN }) - @ApiNotFoundResponse({ description: AppErrorMessage.NOT_FOUND }) - @ApiBadRequestResponse({ description: AppErrorMessage.UNPROCESSABLE_ENTITY }) - async remove(@Param('id') id: string) { - try { - await this.signatureFieldsService.remove(id); - } catch (e) { - if (e instanceof Prisma.PrismaClientKnownRequestError) { - if (e.code === 'P2025') { - this.loggerService.error( - SignatureFieldErrorMessage.SIGNATURE_FIELD_NOT_FOUND, - ); - throw new NotFoundException( - SignatureFieldErrorMessage.SIGNATURE_FIELD_NOT_FOUND_CLIENT, - ); - } - } - throw e; - } - } -} diff --git a/apps/server/src/signature-fields/signature-fields.errors.ts b/apps/server/src/signature-fields/signature-fields.errors.ts deleted file mode 100644 index 820c0363..00000000 --- a/apps/server/src/signature-fields/signature-fields.errors.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum SignatureFieldErrorMessage { - SIGNATURE_FIELD_NOT_FOUND = 'Signature field could not be found with this id', - SIGNATURE_FIELD_NOT_FOUND_CLIENT = 'Signature field could not be found', -} diff --git a/apps/server/src/signature-fields/signature-fields.module.ts b/apps/server/src/signature-fields/signature-fields.module.ts deleted file mode 100644 index aac99dcc..00000000 --- a/apps/server/src/signature-fields/signature-fields.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Module } from '@nestjs/common'; -import { SignatureFieldsService } from './signature-fields.service'; -import { SignatureFieldsController } from './signature-fields.controller'; -import { PrismaModule } from '../prisma/prisma.module'; -import { LoggerModule } from '../logger/logger.module'; - -@Module({ - controllers: [SignatureFieldsController], - providers: [SignatureFieldsService], - imports: [PrismaModule, LoggerModule], -}) -export class SignatureFieldsModule {} diff --git a/apps/server/src/signature-fields/signature-fields.service.spec.ts b/apps/server/src/signature-fields/signature-fields.service.spec.ts deleted file mode 100644 index f2593904..00000000 --- a/apps/server/src/signature-fields/signature-fields.service.spec.ts +++ /dev/null @@ -1,277 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { SignatureFieldsService } from './signature-fields.service'; -import { PrismaService } from '../prisma/prisma.service'; -import { NotFoundException } from '@nestjs/common'; - -describe('SignatureFieldsService', () => { - let service: SignatureFieldsService; - - const mockPrismaService = { - signatureField: { - create: jest.fn(), - findMany: jest.fn(), - findFirstOrThrow: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - }, - }; - - const signatureField1 = { - id: '1', - name: 'Signature Field 1', - order: 1, - createdAt: new Date(), - updatedAt: new Date(), - formTemplateId: '123', - }; - - const signatureField2 = { - id: '2', - name: 'Signature Field 2', - order: 2, - createdAt: new Date(), - updatedAt: new Date(), - formTemplateId: '456', - }; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - SignatureFieldsService, - { provide: PrismaService, useValue: mockPrismaService }, - ], - }).compile(); - - service = module.get(SignatureFieldsService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); - - describe('create', () => { - it('should create a new signature field', async () => { - const createSignatureFieldDTO = { - name: 'Signature Field 1', - order: 1, - formTemplateId: '3', - }; - - const createdSignatureField = { - ...createSignatureFieldDTO, - id: '123', - createdAt: new Date(), - updatedAt: new Date(), - }; - - mockPrismaService.signatureField.create.mockResolvedValue( - createdSignatureField, - ); - - const result = await service.create(createSignatureFieldDTO); - - expect(result).toEqual( - expect.objectContaining({ - ...createSignatureFieldDTO, - createdAt: expect.any(Date), - updatedAt: expect.any(Date), - }), - ); - - expect(mockPrismaService.signatureField.create).toHaveBeenCalledWith({ - data: createSignatureFieldDTO, - }); - }); - }); - - describe('findAll', () => { - it('should return all signature fields when no limit is provided', async () => { - const signatureFields = [signatureField1, signatureField2]; - - mockPrismaService.signatureField.findMany.mockResolvedValue( - signatureFields, - ); - - const result = await service.findAll(); - - expect(result).toEqual(signatureFields); - - expect(mockPrismaService.signatureField.findMany).toHaveBeenCalledWith({ - take: undefined, - }); - }); - - it('should return a specific number of signature fields when a limit is provided', async () => { - const signatureFields = [signatureField1]; - - mockPrismaService.signatureField.findMany.mockResolvedValue( - signatureFields, - ); - - const result = await service.findAll(1); - - expect(result).toEqual(signatureFields); - - expect(mockPrismaService.signatureField.findMany).toHaveBeenCalledWith({ - take: 1, - }); - }); - - it('should return an empty arra if no signature fields are found', async () => { - mockPrismaService.signatureField.findMany.mockResolvedValue([]); - - const result = await service.findAll(); - - expect(result).toEqual([]); - expect(mockPrismaService.signatureField.findMany).toHaveBeenCalled(); - }); - }); - - describe('findOne', () => { - it('should find a signature field by id', async () => { - const id = '1'; - - mockPrismaService.signatureField.findFirstOrThrow.mockResolvedValue( - signatureField1, - ); - - const result = await service.findOne(id); - - expect( - mockPrismaService.signatureField.findFirstOrThrow, - ).toHaveBeenCalledWith({ - where: { id }, - }); - - expect(result).toEqual( - expect.objectContaining({ - ...signatureField1, - createdAt: expect.any(Date), - updatedAt: expect.any(Date), - }), - ); - }); - - it('should throw an error if the signature field is not found', async () => { - const id = 'fakeId'; - - mockPrismaService.signatureField.findFirstOrThrow.mockRejectedValue( - new NotFoundException(), - ); - - await expect(service.findOne(id)).rejects.toThrow(NotFoundException); - }); - }); - - describe('update', () => { - const id = '1'; - const updateSignatureFieldDto = { - name: 'Signature Field 1 Updated', - order: 2, - formTemplateId: '789', - }; - - it('shoud update a signature field by id', async () => { - const updatedSignatureField = { - id: '1', - ...updateSignatureFieldDto, - createdAt: new Date(), - updatedAt: new Date(), - }; - - mockPrismaService.signatureField.update.mockResolvedValue( - updatedSignatureField, - ); - - const result = await service.update(id, updateSignatureFieldDto); - - expect(mockPrismaService.signatureField.update).toHaveBeenCalledWith({ - where: { id }, - data: updateSignatureFieldDto, - }); - - expect(result).toEqual( - expect.objectContaining({ - ...updatedSignatureField, - createdAt: expect.any(Date), - updatedAt: expect.any(Date), - }), - ); - }); - - it('should update the updatedAt field after updating', async () => { - const beforeUpdate = signatureField1.updatedAt; - const updatedSignatureField = { - ...signatureField1, - updatedAt: new Date(beforeUpdate.getTime() + 1000), - }; - - mockPrismaService.signatureField.update.mockResolvedValue( - updatedSignatureField, - ); - - const result = await service.update( - signatureField1.id, - updateSignatureFieldDto, - ); - - expect(result.updatedAt.getTime()).toBeGreaterThan( - beforeUpdate.getTime(), - ); - }); - - it('should throw an error if updating fails', async () => { - mockPrismaService.signatureField.update.mockRejectedValue( - new Error('Update failed'), - ); - - await expect(service.update(id, updateSignatureFieldDto)).rejects.toThrow( - 'Update failed', - ); - }); - - it('should throw an error if updating fails from trying to update nonexistent signature field', async () => { - mockPrismaService.signatureField.update.mockRejectedValue( - new NotFoundException(), - ); - - const id = 'Fake Signature Field'; - - await expect(service.update(id, updateSignatureFieldDto)).rejects.toThrow( - NotFoundException, - ); - }); - }); - - describe('remove', () => { - const id = '1'; - - it('should remove a signature field by id', async () => { - mockPrismaService.signatureField.delete.mockResolvedValue({}); - - await service.remove(id); - - expect(mockPrismaService.signatureField.delete).toHaveBeenCalledWith({ - where: { id }, - }); - }); - - it('should throw an error if deltion fails', async () => { - mockPrismaService.signatureField.delete.mockRejectedValue( - new Error('Delete failed'), - ); - - await expect(service.remove(id)).rejects.toThrow('Delete failed'); - }); - - it('should throw an error if remove fails from trying to remove nonexistent signature field', async () => { - mockPrismaService.signatureField.delete.mockRejectedValue( - new NotFoundException(), - ); - - const id = 'Fake Signature Field'; - - await expect(service.remove(id)).rejects.toThrow(NotFoundException); - }); - }); -}); diff --git a/apps/server/src/signature-fields/signature-fields.service.ts b/apps/server/src/signature-fields/signature-fields.service.ts deleted file mode 100644 index 1eaf0342..00000000 --- a/apps/server/src/signature-fields/signature-fields.service.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '../prisma/prisma.service'; -import { CreateSignatureFieldDto } from './dto/create-signature-field.dto'; -import { UpdateSignatureFieldDto } from './dto/update-signature-field.dto'; - -@Injectable() -export class SignatureFieldsService { - constructor(private prisma: PrismaService) {} - - /** - * Create a new signature field. - * @param createSignatureFieldDto create signature field dto - * @returns the created signature field, hydrated - */ - async create(createSignatureFieldDto: CreateSignatureFieldDto) { - const newSignatureField = await this.prisma.signatureField.create({ - data: { - name: createSignatureFieldDto.name, - order: createSignatureFieldDto.order, - formTemplateId: createSignatureFieldDto.formTemplateId, - }, - }); - return newSignatureField; - } - - /** - * Retrieve all signature fields. - * @param limit the number of signature fields we want to retrieve (optional) - * @returns all signature fields, hydrated - */ - async findAll(limit?: number) { - const signatureFields = await this.prisma.signatureField.findMany({ - take: limit, - }); - return signatureFields; - } - - /** - * Retrieve a signature field by id. - * @param id the signature field id - * @returns the selected signature field, hydrated - */ - async findOne(id: string) { - const signatureField = await this.prisma.signatureField.findFirstOrThrow({ - where: { - id: id, - }, - }); - return signatureField; - } - - /** - * Update a signature field. - * @param id the signature field id - * @param updateSignatureFieldDto update signature field dto - * @returns the updated signature field, hydrated - */ - async update(id: string, updateSignatureFieldDto: UpdateSignatureFieldDto) { - const updatedSignatureField = this.prisma.signatureField.update({ - where: { - id: id, - }, - data: updateSignatureFieldDto, - }); - return updatedSignatureField; - } - - /** - * Remove a signature field. - * @param id the signature field id - */ - async remove(id: string) { - await this.prisma.signatureField.delete({ - where: { - id: id, - }, - }); - } -} diff --git a/apps/server/src/signatures/dto/update-signature-signer.dto.ts b/apps/server/src/signatures/dto/update-signature-signer.dto.ts deleted file mode 100644 index a208b0ab..00000000 --- a/apps/server/src/signatures/dto/update-signature-signer.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { OmitType, PartialType } from '@nestjs/swagger'; -import { CreateSignatureDto } from './create-signature.dto'; - -export class UpdateSignatureSignerDto extends PartialType( - OmitType(CreateSignatureDto, ['order'] as const), -) {} diff --git a/apps/server/src/signatures/dto/update-signature.dto.ts b/apps/server/src/signatures/dto/update-signature.dto.ts deleted file mode 100644 index c2706a8e..00000000 --- a/apps/server/src/signatures/dto/update-signature.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/swagger'; -import { CreateSignatureDto } from './create-signature.dto'; - -export class UpdateSignatureDto extends PartialType(CreateSignatureDto) {} diff --git a/apps/server/src/signatures/entities/signature.entity.ts b/apps/server/src/signatures/entities/signature.entity.ts deleted file mode 100644 index d9a1e6ef..00000000 --- a/apps/server/src/signatures/entities/signature.entity.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { SignerType, Signature } from '@prisma/client'; -import { EmployeeBaseEntity } from '../../employees/entities/employee.entity'; -import { PositionBaseEntity } from '../../positions/entities/position.entity'; -import { Exclude } from 'class-transformer'; -import { DepartmentEntity } from '../../departments/entities/department.entity'; -import { IsEnum } from 'class-validator'; - -export class SignatureEntity implements Signature { - @ApiProperty() - id: string; - - @ApiProperty() - order: number; - - @ApiProperty() - signed: boolean; - - @ApiProperty() - signedDocLink: string | null; - - @ApiProperty() - createdAt: Date; - - @ApiProperty() - updatedAt: Date; - - @ApiProperty() - signerPositionId: string | null; - - @ApiProperty() - signerPosition: PositionBaseEntity | null; - - @ApiProperty() - signerDepartmentId: string | null; - - @ApiProperty() - signerDepartment: DepartmentEntity | null; - - @ApiProperty() - signerEmployeeId: string | null; - - @ApiProperty() - signerEmployee: EmployeeBaseEntity | null; - - @ApiProperty() - signerEmployeeList: EmployeeBaseEntity[]; - - @ApiProperty() - signingEmployeeId: string | null; - - @ApiProperty() - signingEmployee: EmployeeBaseEntity | null; - - @IsEnum(SignerType) - @ApiProperty({ enum: SignerType }) - signerType: SignerType; - - @Exclude() - formInstanceId: string; - - constructor(partial: Partial) { - if (partial.signerPosition) { - partial.signerPosition = new PositionBaseEntity(partial.signerPosition); - } - if (partial.signerDepartment) { - partial.signerDepartment = new DepartmentEntity(partial.signerDepartment); - } - if (partial.signerEmployee) { - partial.signerEmployee = new EmployeeBaseEntity(partial.signerEmployee); - } - if (partial.signingEmployee) { - partial.signingEmployee = new EmployeeBaseEntity(partial.signingEmployee); - } - Object.assign(this, partial); - } -} diff --git a/apps/server/src/signatures/signatures.controller.spec.ts b/apps/server/src/signatures/signatures.controller.spec.ts deleted file mode 100644 index 1d284831..00000000 --- a/apps/server/src/signatures/signatures.controller.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { SignaturesController } from './signatures.controller'; -import { SignaturesService } from './signatures.service'; -import { PrismaService } from '../prisma/prisma.service'; -import { EmployeesService } from '../employees/employees.service'; -import { DepartmentsService } from '../departments/departments.service'; -import { PositionsService } from '../positions/positions.service'; -import { UpdateSignatureSignerDto } from './dto/update-signature-signer.dto'; -import { SignerType } from '@prisma/client'; - -const mockSignatureService = { - updateSigner: async ( - signatureId: string, - updateSignatureSignerDto: UpdateSignatureSignerDto, - ) => { - return { - id: signatureId, - formInstanceId: 'form-instance-id', - order: 1, - signed: false, - signedDocLink: null, - signingEmployeeId: null, - signerEmployeeList: [], - signerEmployeeId: updateSignatureSignerDto.signerEmployeeId ?? null, - signerDepartmentId: updateSignatureSignerDto.signerDepartmentId ?? null, - signerPositionId: updateSignatureSignerDto.signerPositionId ?? null, - signerType: updateSignatureSignerDto.signerType ?? SignerType.POSITION, - createdAt: new Date(1672531200), - updatedAt: new Date(1672531200), - }; - }, -}; - -describe('SignaturesController', () => { - let controller: SignaturesController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [SignaturesController], - providers: [ - { - provide: SignaturesService, - useValue: mockSignatureService, - }, - EmployeesService, - DepartmentsService, - PositionsService, - PrismaService, - ], - }).compile(); - - controller = module.get(SignaturesController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); - - it('should update signer', async () => { - const response = await controller.updateSignatureSigner('signature-id', { - signerType: SignerType.DEPARTMENT, - signerDepartmentId: 'department-id', - }); - expect(response.signerType).toEqual(SignerType.DEPARTMENT); - expect(response.signerDepartmentId).toEqual('department-id'); - }); -}); diff --git a/apps/server/src/signatures/signatures.controller.ts b/apps/server/src/signatures/signatures.controller.ts deleted file mode 100644 index 7350f27d..00000000 --- a/apps/server/src/signatures/signatures.controller.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Body, Controller, Param, Patch } from '@nestjs/common'; -import { SignaturesService } from './signatures.service'; -import { UpdateSignatureSignerDto } from './dto/update-signature-signer.dto'; - -@Controller('signatures') -export class SignaturesController { - constructor(private readonly signaturesService: SignaturesService) {} - - @Patch(':id/signer') - updateSignatureSigner( - @Param('id') id: string, - @Body() updateSignatureSignerDto: UpdateSignatureSignerDto, - ) { - return this.signaturesService.updateSigner(id, updateSignatureSignerDto); - } -} diff --git a/apps/server/src/signatures/signatures.errors.ts b/apps/server/src/signatures/signatures.errors.ts deleted file mode 100644 index 30d3bcc9..00000000 --- a/apps/server/src/signatures/signatures.errors.ts +++ /dev/null @@ -1,8 +0,0 @@ -export enum SignatureErrorMessage { - SIGNATURE_NOT_FOUND = 'Signature could not be found with this id', - SIGNATURE_NOT_FOUND_CLIENT = 'Signature could not be found', - SIGNATURE_NOT_NEXT = 'Signature is not the next one to be signed', - EMPLOYEE_CANNOT_SIGN = 'Employee cannot sign for this signature', - MISSING_SIGNER_TYPE = 'Missing signer type', - MISSING_SIGNER = 'Missing signer for specified signer type', -} diff --git a/apps/server/src/signatures/signatures.service.ts b/apps/server/src/signatures/signatures.service.ts deleted file mode 100644 index 1354a904..00000000 --- a/apps/server/src/signatures/signatures.service.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { BadRequestException, Injectable } from '@nestjs/common'; -import { UpdateSignatureSignerDto } from './dto/update-signature-signer.dto'; -import { SignatureErrorMessage } from './signatures.errors'; -import { PrismaService } from '../prisma/prisma.service'; -import { EmployeesService } from '../employees/employees.service'; -import { EmployeeErrorMessage } from '../employees/employees.errors'; -import { DepartmentsService } from '../departments/departments.service'; -import { DepartmentsErrorMessage } from '../departments/departments.errors'; -import { PositionsService } from '../positions/positions.service'; -import { PositionsErrorMessage } from '../positions/positions.errors'; -import { SignerType } from '@prisma/client'; - -@Injectable() -export class SignaturesService { - constructor( - private prisma: PrismaService, - private employeeService: EmployeesService, - private departmentService: DepartmentsService, - private positionService: PositionsService, - ) {} - - async updateSigner( - signatureId: string, - updateSignatureSignerDto: UpdateSignatureSignerDto, - ) { - try { - await this.findOne(signatureId); - } catch (e) { - throw new BadRequestException(SignatureErrorMessage.SIGNATURE_NOT_FOUND); - } - - switch (updateSignatureSignerDto.signerType) { - case SignerType.POSITION: - if (!updateSignatureSignerDto.signerPositionId) { - throw new BadRequestException(SignatureErrorMessage.MISSING_SIGNER); - } - try { - await this.positionService.findOne( - updateSignatureSignerDto.signerPositionId, - ); - } catch (e) { - throw new BadRequestException( - PositionsErrorMessage.POSITION_NOT_FOUND, - ); - } - - return await this.prisma.signature.update({ - where: { - id: signatureId, - }, - data: { - signerType: SignerType.POSITION, - signerPositionId: updateSignatureSignerDto.signerPositionId, - signerDepartmentId: null, - signerEmployeeId: null, - signerEmployeeList: { - set: [], - }, - }, - include: { - signerEmployeeList: true, - }, - }); - case SignerType.DEPARTMENT: - if (!updateSignatureSignerDto.signerDepartmentId) { - throw new BadRequestException(SignatureErrorMessage.MISSING_SIGNER); - } - try { - await this.departmentService.findOne( - updateSignatureSignerDto.signerDepartmentId, - ); - } catch (e) { - throw new BadRequestException( - DepartmentsErrorMessage.DEPARTMENT_NOT_FOUND, - ); - } - - return await this.prisma.signature.update({ - where: { - id: signatureId, - }, - data: { - signerType: 'DEPARTMENT', - signerPositionId: null, - signerDepartmentId: updateSignatureSignerDto.signerDepartmentId, - signerEmployeeId: null, - signerEmployeeList: { - set: [], - }, - }, - include: { - signerEmployeeList: true, - }, - }); - case SignerType.USER: - if (!updateSignatureSignerDto.signerEmployeeId) { - throw new BadRequestException(SignatureErrorMessage.MISSING_SIGNER); - } - try { - await this.employeeService.findOne( - updateSignatureSignerDto.signerEmployeeId, - ); - } catch (e) { - throw new BadRequestException( - EmployeeErrorMessage.EMPLOYEE_NOT_FOUND, - ); - } - - return await this.prisma.signature.update({ - where: { - id: signatureId, - }, - data: { - signerType: SignerType.USER, - signerPositionId: null, - signerDepartmentId: null, - signerEmployeeId: updateSignatureSignerDto.signerEmployeeId, - signerEmployeeList: { - set: [], - }, - }, - include: { - signerEmployeeList: true, - }, - }); - case SignerType.USER_LIST: - if (!updateSignatureSignerDto.signerEmployeeList) { - throw new BadRequestException(SignatureErrorMessage.MISSING_SIGNER); - } - - for (const employee of updateSignatureSignerDto.signerEmployeeList) { - try { - await this.employeeService.findOne(employee.id); - } catch (e) { - throw new BadRequestException( - EmployeeErrorMessage.EMPLOYEE_NOT_FOUND, - ); - } - } - - return await this.prisma.signature.update({ - where: { - id: signatureId, - }, - data: { - signerType: SignerType.USER_LIST, - signerPositionId: null, - signerDepartmentId: null, - signerEmployeeId: null, - signerEmployeeList: { - set: updateSignatureSignerDto.signerEmployeeList, - }, - }, - include: { - signerEmployeeList: true, - }, - }); - default: - throw new BadRequestException( - SignatureErrorMessage.MISSING_SIGNER_TYPE, - ); - } - } - async findOne(id: string) { - const signature = await this.prisma.signature.findFirstOrThrow({ - where: { - id: id, - }, - include: { - signerEmployeeList: true, - }, - }); - return signature; - } -} diff --git a/apps/web/src/client/@tanstack/react-query.gen.ts b/apps/web/src/client/@tanstack/react-query.gen.ts index a335438a..0b02980f 100644 --- a/apps/web/src/client/@tanstack/react-query.gen.ts +++ b/apps/web/src/client/@tanstack/react-query.gen.ts @@ -21,12 +21,12 @@ import { positionsControllerFindOne, positionsControllerUpdate, positionsControllerFindOneByNameInDepartment, - signatureFieldsControllerFindAll, - signatureFieldsControllerCreate, - signatureFieldsControllerRemove, - signatureFieldsControllerFindOne, - signatureFieldsControllerUpdate, - signaturesControllerUpdateSignatureSigner, + assignedGroupControllerUpdateAssignedGroupSigner, + formTemplatesControllerFindAll, + formTemplatesControllerCreate, + formTemplatesControllerRemove, + formTemplatesControllerFindOne, + formTemplatesControllerUpdate, departmentsControllerFindAll, departmentsControllerCreate, departmentsControllerRemove, @@ -42,11 +42,6 @@ import { formInstancesControllerUpdate, formInstancesControllerSignFormInstance, formInstancesControllerCompleteFormInstance, - formTemplatesControllerFindAll, - formTemplatesControllerCreate, - formTemplatesControllerRemove, - formTemplatesControllerFindOne, - formTemplatesControllerUpdate, } from '../sdk.gen'; import { queryOptions, @@ -79,15 +74,15 @@ import type { PositionsControllerUpdateData, PositionsControllerUpdateResponse, PositionsControllerFindOneByNameInDepartmentData, - SignatureFieldsControllerFindAllData, - SignatureFieldsControllerCreateData, - SignatureFieldsControllerCreateResponse, - SignatureFieldsControllerRemoveData, - SignatureFieldsControllerFindOneData, - SignatureFieldsControllerUpdateData, - SignatureFieldsControllerUpdateResponse, - SignaturesControllerUpdateSignatureSignerData, - SignaturesControllerUpdateSignatureSignerResponse, + AssignedGroupControllerUpdateAssignedGroupSignerData, + AssignedGroupControllerUpdateAssignedGroupSignerResponse, + FormTemplatesControllerFindAllData, + FormTemplatesControllerCreateData, + FormTemplatesControllerCreateResponse, + FormTemplatesControllerRemoveData, + FormTemplatesControllerFindOneData, + FormTemplatesControllerUpdateData, + FormTemplatesControllerUpdateResponse, DepartmentsControllerFindAllData, DepartmentsControllerCreateData, DepartmentsControllerCreateResponse, @@ -109,13 +104,6 @@ import type { FormInstancesControllerSignFormInstanceResponse, FormInstancesControllerCompleteFormInstanceData, FormInstancesControllerCompleteFormInstanceResponse, - FormTemplatesControllerFindAllData, - FormTemplatesControllerCreateData, - FormTemplatesControllerCreateResponse, - FormTemplatesControllerRemoveData, - FormTemplatesControllerFindOneData, - FormTemplatesControllerUpdateData, - FormTemplatesControllerUpdateResponse, } from '../types.gen'; import { client as _heyApiClient } from '../client.gen'; @@ -628,16 +616,38 @@ export const positionsControllerFindOneByNameInDepartmentOptions = ( }); }; -export const signatureFieldsControllerFindAllQueryKey = ( - options: Options, -) => [createQueryKey('signatureFieldsControllerFindAll', options)]; +export const assignedGroupControllerUpdateAssignedGroupSignerMutation = ( + options?: Partial< + Options + >, +) => { + const mutationOptions: UseMutationOptions< + AssignedGroupControllerUpdateAssignedGroupSignerResponse, + DefaultError, + Options + > = { + mutationFn: async (localOptions) => { + const { data } = await assignedGroupControllerUpdateAssignedGroupSigner({ + ...options, + ...localOptions, + throwOnError: true, + }); + return data; + }, + }; + return mutationOptions; +}; + +export const formTemplatesControllerFindAllQueryKey = ( + options?: Options, +) => [createQueryKey('formTemplatesControllerFindAll', options)]; -export const signatureFieldsControllerFindAllOptions = ( - options: Options, +export const formTemplatesControllerFindAllOptions = ( + options?: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { - const { data } = await signatureFieldsControllerFindAll({ + const { data } = await formTemplatesControllerFindAll({ ...options, ...queryKey[0], signal, @@ -645,20 +655,20 @@ export const signatureFieldsControllerFindAllOptions = ( }); return data; }, - queryKey: signatureFieldsControllerFindAllQueryKey(options), + queryKey: formTemplatesControllerFindAllQueryKey(options), }); }; -export const signatureFieldsControllerCreateQueryKey = ( - options: Options, -) => [createQueryKey('signatureFieldsControllerCreate', options)]; +export const formTemplatesControllerCreateQueryKey = ( + options: Options, +) => [createQueryKey('formTemplatesControllerCreate', options)]; -export const signatureFieldsControllerCreateOptions = ( - options: Options, +export const formTemplatesControllerCreateOptions = ( + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { - const { data } = await signatureFieldsControllerCreate({ + const { data } = await formTemplatesControllerCreate({ ...options, ...queryKey[0], signal, @@ -666,20 +676,20 @@ export const signatureFieldsControllerCreateOptions = ( }); return data; }, - queryKey: signatureFieldsControllerCreateQueryKey(options), + queryKey: formTemplatesControllerCreateQueryKey(options), }); }; -export const signatureFieldsControllerCreateMutation = ( - options?: Partial>, +export const formTemplatesControllerCreateMutation = ( + options?: Partial>, ) => { const mutationOptions: UseMutationOptions< - SignatureFieldsControllerCreateResponse, + FormTemplatesControllerCreateResponse, DefaultError, - Options + Options > = { mutationFn: async (localOptions) => { - const { data } = await signatureFieldsControllerCreate({ + const { data } = await formTemplatesControllerCreate({ ...options, ...localOptions, throwOnError: true, @@ -690,16 +700,16 @@ export const signatureFieldsControllerCreateMutation = ( return mutationOptions; }; -export const signatureFieldsControllerRemoveMutation = ( - options?: Partial>, +export const formTemplatesControllerRemoveMutation = ( + options?: Partial>, ) => { const mutationOptions: UseMutationOptions< unknown, DefaultError, - Options + Options > = { mutationFn: async (localOptions) => { - const { data } = await signatureFieldsControllerRemove({ + const { data } = await formTemplatesControllerRemove({ ...options, ...localOptions, throwOnError: true, @@ -710,16 +720,16 @@ export const signatureFieldsControllerRemoveMutation = ( return mutationOptions; }; -export const signatureFieldsControllerFindOneQueryKey = ( - options: Options, -) => [createQueryKey('signatureFieldsControllerFindOne', options)]; +export const formTemplatesControllerFindOneQueryKey = ( + options: Options, +) => [createQueryKey('formTemplatesControllerFindOne', options)]; -export const signatureFieldsControllerFindOneOptions = ( - options: Options, +export const formTemplatesControllerFindOneOptions = ( + options: Options, ) => { return queryOptions({ queryFn: async ({ queryKey, signal }) => { - const { data } = await signatureFieldsControllerFindOne({ + const { data } = await formTemplatesControllerFindOne({ ...options, ...queryKey[0], signal, @@ -727,40 +737,20 @@ export const signatureFieldsControllerFindOneOptions = ( }); return data; }, - queryKey: signatureFieldsControllerFindOneQueryKey(options), + queryKey: formTemplatesControllerFindOneQueryKey(options), }); }; -export const signatureFieldsControllerUpdateMutation = ( - options?: Partial>, -) => { - const mutationOptions: UseMutationOptions< - SignatureFieldsControllerUpdateResponse, - DefaultError, - Options - > = { - mutationFn: async (localOptions) => { - const { data } = await signatureFieldsControllerUpdate({ - ...options, - ...localOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; -}; - -export const signaturesControllerUpdateSignatureSignerMutation = ( - options?: Partial>, +export const formTemplatesControllerUpdateMutation = ( + options?: Partial>, ) => { const mutationOptions: UseMutationOptions< - SignaturesControllerUpdateSignatureSignerResponse, + FormTemplatesControllerUpdateResponse, DefaultError, - Options + Options > = { mutationFn: async (localOptions) => { - const { data } = await signaturesControllerUpdateSignatureSigner({ + const { data } = await formTemplatesControllerUpdate({ ...options, ...localOptions, throwOnError: true, @@ -1133,126 +1123,3 @@ export const formInstancesControllerCompleteFormInstanceMutation = ( }; return mutationOptions; }; - -export const formTemplatesControllerFindAllQueryKey = ( - options?: Options, -) => [createQueryKey('formTemplatesControllerFindAll', options)]; - -export const formTemplatesControllerFindAllOptions = ( - options?: Options, -) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await formTemplatesControllerFindAll({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: formTemplatesControllerFindAllQueryKey(options), - }); -}; - -export const formTemplatesControllerCreateQueryKey = ( - options: Options, -) => [createQueryKey('formTemplatesControllerCreate', options)]; - -export const formTemplatesControllerCreateOptions = ( - options: Options, -) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await formTemplatesControllerCreate({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: formTemplatesControllerCreateQueryKey(options), - }); -}; - -export const formTemplatesControllerCreateMutation = ( - options?: Partial>, -) => { - const mutationOptions: UseMutationOptions< - FormTemplatesControllerCreateResponse, - DefaultError, - Options - > = { - mutationFn: async (localOptions) => { - const { data } = await formTemplatesControllerCreate({ - ...options, - ...localOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; -}; - -export const formTemplatesControllerRemoveMutation = ( - options?: Partial>, -) => { - const mutationOptions: UseMutationOptions< - unknown, - DefaultError, - Options - > = { - mutationFn: async (localOptions) => { - const { data } = await formTemplatesControllerRemove({ - ...options, - ...localOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; -}; - -export const formTemplatesControllerFindOneQueryKey = ( - options: Options, -) => [createQueryKey('formTemplatesControllerFindOne', options)]; - -export const formTemplatesControllerFindOneOptions = ( - options: Options, -) => { - return queryOptions({ - queryFn: async ({ queryKey, signal }) => { - const { data } = await formTemplatesControllerFindOne({ - ...options, - ...queryKey[0], - signal, - throwOnError: true, - }); - return data; - }, - queryKey: formTemplatesControllerFindOneQueryKey(options), - }); -}; - -export const formTemplatesControllerUpdateMutation = ( - options?: Partial>, -) => { - const mutationOptions: UseMutationOptions< - FormTemplatesControllerUpdateResponse, - DefaultError, - Options - > = { - mutationFn: async (localOptions) => { - const { data } = await formTemplatesControllerUpdate({ - ...options, - ...localOptions, - throwOnError: true, - }); - return data; - }, - }; - return mutationOptions; -}; diff --git a/apps/web/src/client/schemas.gen.ts b/apps/web/src/client/schemas.gen.ts index 94706d63..1327ce24 100644 --- a/apps/web/src/client/schemas.gen.ts +++ b/apps/web/src/client/schemas.gen.ts @@ -317,6 +317,12 @@ export const PositionEntitySchema = { department: { $ref: '#/components/schemas/DepartmentEntity', }, + employees: { + type: 'array', + items: { + $ref: '#/components/schemas/EmployeeBaseEntity', + }, + }, departmentId: { type: 'string', }, @@ -328,18 +334,13 @@ export const PositionEntitySchema = { format: 'date-time', type: 'string', }, - employees: { - type: 'array', - items: { - $ref: '#/components/schemas/EmployeeBaseEntity', - }, - }, }, required: [ 'id', 'name', 'single', 'department', + 'employees', 'departmentId', 'createdAt', 'updatedAt', @@ -358,188 +359,182 @@ export const UpdatePositionDtoSchema = { }, } as const; -export const CreateSignatureFieldDtoSchema = { +export const ConnectEmployeeDtoSchema = { type: 'object', properties: { - name: { - type: 'string', - }, - order: { - type: 'number', - }, - formTemplateId: { + id: { type: 'string', }, }, - required: ['name', 'order'], + required: ['id'], } as const; -export const SignatureFieldEntitySchema = { +export const UpdateAssignedGroupSignerDtoSchema = { type: 'object', properties: { - id: { + fieldGroupId: { type: 'string', }, - name: { + signerType: { type: 'string', + enum: ['POSITION', 'DEPARTMENT', 'USER', 'USER_LIST'], }, - order: { - type: 'number', - }, - formTemplateId: { + signerEmployeeId: { type: 'string', }, - createdAt: { - format: 'date-time', + signerPositionId: { type: 'string', }, - updatedAt: { - format: 'date-time', + signerDepartmentId: { type: 'string', }, + signerEmployeeList: { + type: 'array', + items: { + $ref: '#/components/schemas/ConnectEmployeeDto', + }, + }, }, - required: ['id', 'name', 'order', 'formTemplateId', 'createdAt', 'updatedAt'], } as const; -export const UpdateSignatureFieldDtoSchema = { +export const CreateTemplateBoxDtoSchema = { type: 'object', properties: { name: { type: 'string', }, - order: { + type: { + type: 'string', + enum: ['signature', 'checkbox'], + }, + x_coordinate: { type: 'number', }, - formTemplateId: { + y_coordinate: { + type: 'number', + }, + fieldGroupId: { type: 'string', }, }, + required: ['name', 'type', 'x_coordinate', 'y_coordinate', 'fieldGroupId'], } as const; -export const ConnectEmployeeDtoSchema = { +export const CreateFieldGroupDtoSchema = { type: 'object', properties: { - id: { + name: { type: 'string', }, + order: { + type: 'number', + }, + templateBoxes: { + type: 'array', + items: { + $ref: '#/components/schemas/CreateTemplateBoxDto', + }, + }, }, - required: ['id'], + required: ['name', 'order', 'templateBoxes'], } as const; -export const UpdateSignatureSignerDtoSchema = { +export const CreateFormTemplateDtoSchema = { type: 'object', properties: { - signerEmployeeId: { + name: { type: 'string', - nullable: true, }, - signerPositionId: { - type: 'string', - nullable: true, - }, - signerDepartmentId: { + formDocLink: { type: 'string', - nullable: true, }, - signerEmployeeList: { + fieldGroups: { type: 'array', items: { - $ref: '#/components/schemas/ConnectEmployeeDto', + $ref: '#/components/schemas/CreateFieldGroupDto', }, }, - signerType: { - type: 'string', - enum: ['POSITION', 'DEPARTMENT', 'USER', 'USER_LIST'], - }, }, + required: ['name', 'formDocLink', 'fieldGroups'], } as const; -export const CreateDepartmentDtoSchema = { +export const TemplateBoxBaseEntitySchema = { type: 'object', properties: { - name: { + id: { type: 'string', }, - }, - required: ['name'], -} as const; - -export const UpdateDepartmentDtoSchema = { - type: 'object', - properties: { - name: { + type: { type: 'string', + enum: ['SIGNATURE', 'CHECKBOX', 'TEXT_FIELD'], }, - }, -} as const; - -export const CreateSignatureDtoSchema = { - type: 'object', - properties: { - order: { + x_coordinate: { type: 'number', }, - signerEmployeeId: { - type: 'string', - nullable: true, + y_coordinate: { + type: 'number', }, - signerPositionId: { + createdAt: { + format: 'date-time', type: 'string', - nullable: true, }, - signerDepartmentId: { + updatedAt: { + format: 'date-time', type: 'string', - nullable: true, }, - signerEmployeeList: { - type: 'array', - items: { - $ref: '#/components/schemas/ConnectEmployeeDto', - }, - }, - signerType: { + fieldGroupId: { type: 'string', - enum: ['POSITION', 'DEPARTMENT', 'USER', 'USER_LIST'], }, }, required: [ - 'order', - 'signerEmployeeId', - 'signerPositionId', - 'signerDepartmentId', - 'signerEmployeeList', - 'signerType', + 'id', + 'type', + 'x_coordinate', + 'y_coordinate', + 'createdAt', + 'updatedAt', + 'fieldGroupId', ], } as const; -export const CreateFormInstanceDtoSchema = { +export const FieldGroupBaseEntitySchema = { type: 'object', properties: { + id: { + type: 'string', + }, name: { type: 'string', }, - signatures: { - type: 'array', - items: { - $ref: '#/components/schemas/CreateSignatureDto', - }, + order: { + type: 'number', }, - originatorId: { + createdAt: { + format: 'date-time', type: 'string', }, - formTemplateId: { + updatedAt: { + format: 'date-time', type: 'string', }, - formDocLink: { + formTemplateId: { type: 'string', }, + templateBoxes: { + type: 'array', + items: { + $ref: '#/components/schemas/TemplateBoxBaseEntity', + }, + }, }, required: [ + 'id', 'name', - 'signatures', - 'originatorId', + 'order', + 'createdAt', + 'updatedAt', 'formTemplateId', - 'formDocLink', + 'templateBoxes', ], } as const; @@ -567,12 +562,15 @@ export const FormTemplateBaseEntitySchema = { required: ['id', 'name', 'formDocLink', 'createdAt', 'updatedAt'], } as const; -export const SignatureEntitySchema = { +export const AssignedGroupEntitySchema = { type: 'object', properties: { id: { type: 'string', }, + fieldGroupId: { + type: 'string', + }, order: { type: 'number', }, @@ -595,17 +593,43 @@ export const SignatureEntitySchema = { type: 'string', nullable: true, }, - signerPosition: { + signerDepartmentId: { + type: 'string', + nullable: true, + }, + signerEmployeeId: { + type: 'string', + nullable: true, + }, + signingEmployeeId: { + type: 'string', + nullable: true, + }, + signerType: { + type: 'string', + enum: ['POSITION', 'DEPARTMENT', 'USER', 'USER_LIST'], + }, + formInstanceId: { + type: 'string', + }, + fieldGroup: { + $ref: '#/components/schemas/FieldGroupBaseEntity', + }, + signingEmployee: { nullable: true, allOf: [ { - $ref: '#/components/schemas/PositionBaseEntity', + $ref: '#/components/schemas/EmployeeBaseEntity', }, ], }, - signerDepartmentId: { - type: 'string', + signerPosition: { nullable: true, + allOf: [ + { + $ref: '#/components/schemas/PositionBaseEntity', + }, + ], }, signerDepartment: { nullable: true, @@ -615,10 +639,6 @@ export const SignatureEntitySchema = { }, ], }, - signerEmployeeId: { - type: 'string', - nullable: true, - }, signerEmployee: { nullable: true, allOf: [ @@ -628,49 +648,23 @@ export const SignatureEntitySchema = { ], }, signerEmployeeList: { + nullable: true, type: 'array', items: { $ref: '#/components/schemas/EmployeeBaseEntity', }, }, - signingEmployeeId: { - type: 'string', - nullable: true, - }, - signingEmployee: { - nullable: true, - allOf: [ - { - $ref: '#/components/schemas/EmployeeBaseEntity', - }, - ], - }, - signerType: { - type: 'string', - enum: ['POSITION', 'DEPARTMENT', 'USER', 'USER_LIST'], - }, - formInstanceId: { - type: 'string', - }, }, required: [ 'id', + 'fieldGroupId', 'order', 'signed', - 'signedDocLink', 'createdAt', 'updatedAt', - 'signerPositionId', - 'signerPosition', - 'signerDepartmentId', - 'signerDepartment', - 'signerEmployeeId', - 'signerEmployee', - 'signerEmployeeList', - 'signingEmployeeId', - 'signingEmployee', 'signerType', 'formInstanceId', + 'fieldGroup', ], } as const; @@ -716,10 +710,10 @@ export const FormInstanceEntitySchema = { formTemplate: { $ref: '#/components/schemas/FormTemplateBaseEntity', }, - signatures: { + assignedGroups: { type: 'array', items: { - $ref: '#/components/schemas/SignatureEntity', + $ref: '#/components/schemas/AssignedGroupEntity', }, }, originatorId: { @@ -739,25 +733,57 @@ export const FormInstanceEntitySchema = { 'updatedAt', 'originator', 'formTemplate', - 'signatures', + 'assignedGroups', 'originatorId', 'formTemplateId', ], } as const; -export const UpdateFormInstanceDtoSchema = { +export const FormTemplateEntitySchema = { type: 'object', properties: { + id: { + type: 'string', + }, name: { type: 'string', }, formDocLink: { type: 'string', }, + fieldGroups: { + type: 'array', + items: { + $ref: '#/components/schemas/FieldGroupBaseEntity', + }, + }, + formInstances: { + type: 'array', + items: { + $ref: '#/components/schemas/FormInstanceEntity', + }, + }, + createdAt: { + format: 'date-time', + type: 'string', + }, + updatedAt: { + format: 'date-time', + type: 'string', + }, }, + required: [ + 'id', + 'name', + 'formDocLink', + 'fieldGroups', + 'formInstances', + 'createdAt', + 'updatedAt', + ], } as const; -export const CreateFormTemplateDtoSchema = { +export const UpdateFormTemplateDtoSchema = { type: 'object', properties: { name: { @@ -766,61 +792,92 @@ export const CreateFormTemplateDtoSchema = { formDocLink: { type: 'string', }, - signatureFields: { - type: 'array', - items: { - $ref: '#/components/schemas/CreateSignatureFieldDto', - }, - }, }, - required: ['name', 'formDocLink', 'signatureFields'], } as const; -export const FormTemplateEntitySchema = { +export const CreateDepartmentDtoSchema = { type: 'object', properties: { - id: { + name: { type: 'string', }, + }, + required: ['name'], +} as const; + +export const UpdateDepartmentDtoSchema = { + type: 'object', + properties: { name: { type: 'string', }, - formDocLink: { + }, +} as const; + +export const CreateAssignedGroupDtoSchema = { + type: 'object', + properties: { + order: { + type: 'number', + }, + fieldGroupId: { + type: 'string', + }, + signerType: { + type: 'string', + enum: ['POSITION', 'DEPARTMENT', 'USER', 'USER_LIST'], + }, + signerEmployeeId: { + type: 'string', + }, + signerPositionId: { type: 'string', }, - signatureFields: { + signerDepartmentId: { + type: 'string', + }, + signerEmployeeList: { type: 'array', items: { - $ref: '#/components/schemas/SignatureFieldEntity', + $ref: '#/components/schemas/ConnectEmployeeDto', }, }, - formInstances: { + }, + required: ['order', 'fieldGroupId', 'signerType', 'signerEmployeeList'], +} as const; + +export const CreateFormInstanceDtoSchema = { + type: 'object', + properties: { + name: { + type: 'string', + }, + assignedGroups: { type: 'array', items: { - $ref: '#/components/schemas/FormInstanceEntity', + $ref: '#/components/schemas/CreateAssignedGroupDto', }, }, - createdAt: { - format: 'date-time', + originatorId: { type: 'string', }, - updatedAt: { - format: 'date-time', + formTemplateId: { + type: 'string', + }, + formDocLink: { type: 'string', }, }, required: [ - 'id', 'name', + 'assignedGroups', + 'originatorId', + 'formTemplateId', 'formDocLink', - 'signatureFields', - 'formInstances', - 'createdAt', - 'updatedAt', ], } as const; -export const UpdateFormTemplateDtoSchema = { +export const UpdateFormInstanceDtoSchema = { type: 'object', properties: { name: { diff --git a/apps/web/src/client/sdk.gen.ts b/apps/web/src/client/sdk.gen.ts index 5c500e6b..6175f351 100644 --- a/apps/web/src/client/sdk.gen.ts +++ b/apps/web/src/client/sdk.gen.ts @@ -41,17 +41,17 @@ import type { PositionsControllerUpdateResponse, PositionsControllerFindOneByNameInDepartmentData, PositionsControllerFindOneByNameInDepartmentResponse, - SignatureFieldsControllerFindAllData, - SignatureFieldsControllerFindAllResponse, - SignatureFieldsControllerCreateData, - SignatureFieldsControllerCreateResponse, - SignatureFieldsControllerRemoveData, - SignatureFieldsControllerFindOneData, - SignatureFieldsControllerFindOneResponse, - SignatureFieldsControllerUpdateData, - SignatureFieldsControllerUpdateResponse, - SignaturesControllerUpdateSignatureSignerData, - SignaturesControllerUpdateSignatureSignerResponse, + AssignedGroupControllerUpdateAssignedGroupSignerData, + AssignedGroupControllerUpdateAssignedGroupSignerResponse, + FormTemplatesControllerFindAllData, + FormTemplatesControllerFindAllResponse, + FormTemplatesControllerCreateData, + FormTemplatesControllerCreateResponse, + FormTemplatesControllerRemoveData, + FormTemplatesControllerFindOneData, + FormTemplatesControllerFindOneResponse, + FormTemplatesControllerUpdateData, + FormTemplatesControllerUpdateResponse, DepartmentsControllerFindAllData, DepartmentsControllerFindAllResponse, DepartmentsControllerCreateData, @@ -80,15 +80,6 @@ import type { FormInstancesControllerSignFormInstanceResponse, FormInstancesControllerCompleteFormInstanceData, FormInstancesControllerCompleteFormInstanceResponse, - FormTemplatesControllerFindAllData, - FormTemplatesControllerFindAllResponse, - FormTemplatesControllerCreateData, - FormTemplatesControllerCreateResponse, - FormTemplatesControllerRemoveData, - FormTemplatesControllerFindOneData, - FormTemplatesControllerFindOneResponse, - FormTemplatesControllerUpdateData, - FormTemplatesControllerUpdateResponse, } from './types.gen'; import { client as _heyApiClient } from './client.gen'; @@ -411,100 +402,103 @@ export const positionsControllerFindOneByNameInDepartment = < }); }; -export const signatureFieldsControllerFindAll = < +export const assignedGroupControllerUpdateAssignedGroupSigner = < ThrowOnError extends boolean = false, >( - options: Options, + options: Options< + AssignedGroupControllerUpdateAssignedGroupSignerData, + ThrowOnError + >, ) => { - return (options.client ?? _heyApiClient).get< - SignatureFieldsControllerFindAllResponse, + return (options.client ?? _heyApiClient).patch< + AssignedGroupControllerUpdateAssignedGroupSignerResponse, unknown, ThrowOnError >({ - url: '/api/signature-fields', + url: '/api/signatures/{id}/signer', ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers, + }, }); }; -export const signatureFieldsControllerCreate = < +export const formTemplatesControllerFindAll = < ThrowOnError extends boolean = false, >( - options: Options, + options?: Options, ) => { - return (options.client ?? _heyApiClient).post< - SignatureFieldsControllerCreateResponse, + return (options?.client ?? _heyApiClient).get< + FormTemplatesControllerFindAllResponse, unknown, ThrowOnError >({ - url: '/api/signature-fields', + url: '/api/form-templates', ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers, - }, }); }; -export const signatureFieldsControllerRemove = < +export const formTemplatesControllerCreate = < ThrowOnError extends boolean = false, >( - options: Options, + options: Options, ) => { - return (options.client ?? _heyApiClient).delete< - unknown, + return (options.client ?? _heyApiClient).post< + FormTemplatesControllerCreateResponse, unknown, ThrowOnError >({ - url: '/api/signature-fields/{id}', + url: '/api/form-templates', ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers, + }, }); }; -export const signatureFieldsControllerFindOne = < +export const formTemplatesControllerRemove = < ThrowOnError extends boolean = false, >( - options: Options, + options: Options, ) => { - return (options.client ?? _heyApiClient).get< - SignatureFieldsControllerFindOneResponse, + return (options.client ?? _heyApiClient).delete< + unknown, unknown, ThrowOnError >({ - url: '/api/signature-fields/{id}', + url: '/api/form-templates/{id}', ...options, }); }; -export const signatureFieldsControllerUpdate = < +export const formTemplatesControllerFindOne = < ThrowOnError extends boolean = false, >( - options: Options, + options: Options, ) => { - return (options.client ?? _heyApiClient).patch< - SignatureFieldsControllerUpdateResponse, + return (options.client ?? _heyApiClient).get< + FormTemplatesControllerFindOneResponse, unknown, ThrowOnError >({ - url: '/api/signature-fields/{id}', + url: '/api/form-templates/{id}', ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers, - }, }); }; -export const signaturesControllerUpdateSignatureSigner = < +export const formTemplatesControllerUpdate = < ThrowOnError extends boolean = false, >( - options: Options, + options: Options, ) => { return (options.client ?? _heyApiClient).patch< - SignaturesControllerUpdateSignatureSignerResponse, + FormTemplatesControllerUpdateResponse, unknown, ThrowOnError >({ - url: '/api/signatures/{id}/signer', + url: '/api/form-templates/{id}', ...options, headers: { 'Content-Type': 'application/json', @@ -780,86 +774,3 @@ export const formInstancesControllerCompleteFormInstance = < ...options, }); }; - -export const formTemplatesControllerFindAll = < - ThrowOnError extends boolean = false, ->( - options?: Options, -) => { - return (options?.client ?? _heyApiClient).get< - FormTemplatesControllerFindAllResponse, - unknown, - ThrowOnError - >({ - url: '/api/form-templates', - ...options, - }); -}; - -export const formTemplatesControllerCreate = < - ThrowOnError extends boolean = false, ->( - options: Options, -) => { - return (options.client ?? _heyApiClient).post< - FormTemplatesControllerCreateResponse, - unknown, - ThrowOnError - >({ - url: '/api/form-templates', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers, - }, - }); -}; - -export const formTemplatesControllerRemove = < - ThrowOnError extends boolean = false, ->( - options: Options, -) => { - return (options.client ?? _heyApiClient).delete< - unknown, - unknown, - ThrowOnError - >({ - url: '/api/form-templates/{id}', - ...options, - }); -}; - -export const formTemplatesControllerFindOne = < - ThrowOnError extends boolean = false, ->( - options: Options, -) => { - return (options.client ?? _heyApiClient).get< - FormTemplatesControllerFindOneResponse, - unknown, - ThrowOnError - >({ - url: '/api/form-templates/{id}', - ...options, - }); -}; - -export const formTemplatesControllerUpdate = < - ThrowOnError extends boolean = false, ->( - options: Options, -) => { - return (options.client ?? _heyApiClient).patch< - FormTemplatesControllerUpdateResponse, - unknown, - ThrowOnError - >({ - url: '/api/form-templates/{id}', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers, - }, - }); -}; diff --git a/apps/web/src/client/types.gen.ts b/apps/web/src/client/types.gen.ts index a3f7fa7f..3bcc1c6f 100644 --- a/apps/web/src/client/types.gen.ts +++ b/apps/web/src/client/types.gen.ts @@ -94,10 +94,10 @@ export type PositionEntity = { name: string; single: boolean; department: DepartmentEntity; + employees: Array; departmentId: string; createdAt: string; updatedAt: string; - employees?: Array; }; export type UpdatePositionDto = { @@ -105,27 +105,6 @@ export type UpdatePositionDto = { departmentId?: string; }; -export type CreateSignatureFieldDto = { - name: string; - order: number; - formTemplateId?: string; -}; - -export type SignatureFieldEntity = { - id: string; - name: string; - order: number; - formTemplateId: string; - createdAt: string; - updatedAt: string; -}; - -export type UpdateSignatureFieldDto = { - name?: string; - order?: number; - formTemplateId?: string; -}; - export type ConnectEmployeeDto = { id: string; }; @@ -137,37 +116,58 @@ export enum SignerType { USER_LIST = 'USER_LIST', } -export type UpdateSignatureSignerDto = { - signerEmployeeId?: string | null; - signerPositionId?: string | null; - signerDepartmentId?: string | null; - signerEmployeeList?: Array; +export type UpdateAssignedGroupSignerDto = { + fieldGroupId?: string; signerType?: 'POSITION' | 'DEPARTMENT' | 'USER' | 'USER_LIST'; + signerEmployeeId?: string; + signerPositionId?: string; + signerDepartmentId?: string; + signerEmployeeList?: Array; }; -export type CreateDepartmentDto = { +export enum Type { + SIGNATURE = 'signature', + CHECKBOX = 'checkbox', +} + +export type CreateTemplateBoxDto = { name: string; + type: 'signature' | 'checkbox'; + x_coordinate: number; + y_coordinate: number; + fieldGroupId: string; }; -export type UpdateDepartmentDto = { - name?: string; +export type CreateFieldGroupDto = { + name: string; + order: number; + templateBoxes: Array; }; -export type CreateSignatureDto = { - order: number; - signerEmployeeId: string | null; - signerPositionId: string | null; - signerDepartmentId: string | null; - signerEmployeeList: Array; - signerType: 'POSITION' | 'DEPARTMENT' | 'USER' | 'USER_LIST'; +export type CreateFormTemplateDto = { + name: string; + formDocLink: string; + fieldGroups: Array; }; -export type CreateFormInstanceDto = { +export type TemplateBoxBaseEntity = { + id: string; + type: 'SIGNATURE' | 'CHECKBOX' | 'TEXT_FIELD'; + x_coordinate: number; + y_coordinate: number; + createdAt: string; + updatedAt: string; + fieldGroupId: string; +}; + +export type FieldGroupBaseEntity = { + id: string; name: string; - signatures: Array; - originatorId: string; + order: number; + createdAt: string; + updatedAt: string; formTemplateId: string; - formDocLink: string; + templateBoxes: Array; }; export type FormTemplateBaseEntity = { @@ -178,24 +178,26 @@ export type FormTemplateBaseEntity = { updatedAt: string; }; -export type SignatureEntity = { +export type AssignedGroupEntity = { id: string; + fieldGroupId: string; order: number; signed: boolean; - signedDocLink: string | null; + signedDocLink?: string | null; createdAt: string; updatedAt: string; - signerPositionId: string | null; - signerPosition: PositionBaseEntity | null; - signerDepartmentId: string | null; - signerDepartment: DepartmentEntity | null; - signerEmployeeId: string | null; - signerEmployee: EmployeeBaseEntity | null; - signerEmployeeList: Array; - signingEmployeeId: string | null; - signingEmployee: EmployeeBaseEntity | null; + signerPositionId?: string | null; + signerDepartmentId?: string | null; + signerEmployeeId?: string | null; + signingEmployeeId?: string | null; signerType: 'POSITION' | 'DEPARTMENT' | 'USER' | 'USER_LIST'; formInstanceId: string; + fieldGroup: FieldGroupBaseEntity; + signingEmployee?: EmployeeBaseEntity | null; + signerPosition?: PositionBaseEntity | null; + signerDepartment?: DepartmentEntity | null; + signerEmployee?: EmployeeBaseEntity | null; + signerEmployeeList?: Array | null; }; export type FormInstanceEntity = { @@ -210,33 +212,53 @@ export type FormInstanceEntity = { markedCompletedAt?: string | null; originator: EmployeeEntity; formTemplate: FormTemplateBaseEntity; - signatures: Array; + assignedGroups: Array; originatorId: string; formTemplateId: string; }; -export type UpdateFormInstanceDto = { +export type FormTemplateEntity = { + id: string; + name: string; + formDocLink: string; + fieldGroups: Array; + formInstances: Array; + createdAt: string; + updatedAt: string; +}; + +export type UpdateFormTemplateDto = { name?: string; formDocLink?: string; }; -export type CreateFormTemplateDto = { +export type CreateDepartmentDto = { name: string; - formDocLink: string; - signatureFields: Array; }; -export type FormTemplateEntity = { - id: string; +export type UpdateDepartmentDto = { + name?: string; +}; + +export type CreateAssignedGroupDto = { + order: number; + fieldGroupId: string; + signerType: 'POSITION' | 'DEPARTMENT' | 'USER' | 'USER_LIST'; + signerEmployeeId?: string; + signerPositionId?: string; + signerDepartmentId?: string; + signerEmployeeList: Array; +}; + +export type CreateFormInstanceDto = { name: string; + assignedGroups: Array; + originatorId: string; + formTemplateId: string; formDocLink: string; - signatureFields: Array; - formInstances: Array; - createdAt: string; - updatedAt: string; }; -export type UpdateFormTemplateDto = { +export type UpdateFormInstanceDto = { name?: string; formDocLink?: string; }; @@ -790,16 +812,37 @@ export type PositionsControllerFindOneByNameInDepartmentResponses = { export type PositionsControllerFindOneByNameInDepartmentResponse = PositionsControllerFindOneByNameInDepartmentResponses[keyof PositionsControllerFindOneByNameInDepartmentResponses]; -export type SignatureFieldsControllerFindAllData = { +export type AssignedGroupControllerUpdateAssignedGroupSignerData = { + body: UpdateAssignedGroupSignerDto; + path: { + id: string; + }; + query?: never; + url: '/api/signatures/{id}/signer'; +}; + +export type AssignedGroupControllerUpdateAssignedGroupSignerResponses = { + 200: { + [key: string]: unknown; + }; +}; + +export type AssignedGroupControllerUpdateAssignedGroupSignerResponse = + AssignedGroupControllerUpdateAssignedGroupSignerResponses[keyof AssignedGroupControllerUpdateAssignedGroupSignerResponses]; + +export type FormTemplatesControllerFindAllData = { body?: never; path?: never; - query: { - limit: number; + query?: { + /** + * Limit on number of form templates to return + */ + limit?: number; }; - url: '/api/signature-fields'; + url: '/api/form-templates'; }; -export type SignatureFieldsControllerFindAllErrors = { +export type FormTemplatesControllerFindAllErrors = { /** * Bad Request */ @@ -810,21 +853,21 @@ export type SignatureFieldsControllerFindAllErrors = { 403: unknown; }; -export type SignatureFieldsControllerFindAllResponses = { - 200: Array; +export type FormTemplatesControllerFindAllResponses = { + 200: Array; }; -export type SignatureFieldsControllerFindAllResponse = - SignatureFieldsControllerFindAllResponses[keyof SignatureFieldsControllerFindAllResponses]; +export type FormTemplatesControllerFindAllResponse = + FormTemplatesControllerFindAllResponses[keyof FormTemplatesControllerFindAllResponses]; -export type SignatureFieldsControllerCreateData = { - body: CreateSignatureFieldDto; +export type FormTemplatesControllerCreateData = { + body: CreateFormTemplateDto; path?: never; query?: never; - url: '/api/signature-fields'; + url: '/api/form-templates'; }; -export type SignatureFieldsControllerCreateErrors = { +export type FormTemplatesControllerCreateErrors = { /** * Unauthorized Request */ @@ -835,23 +878,23 @@ export type SignatureFieldsControllerCreateErrors = { 422: unknown; }; -export type SignatureFieldsControllerCreateResponses = { - 201: SignatureFieldEntity; +export type FormTemplatesControllerCreateResponses = { + 201: FormTemplateEntity; }; -export type SignatureFieldsControllerCreateResponse = - SignatureFieldsControllerCreateResponses[keyof SignatureFieldsControllerCreateResponses]; +export type FormTemplatesControllerCreateResponse = + FormTemplatesControllerCreateResponses[keyof FormTemplatesControllerCreateResponses]; -export type SignatureFieldsControllerRemoveData = { +export type FormTemplatesControllerRemoveData = { body?: never; path: { id: string; }; query?: never; - url: '/api/signature-fields/{id}'; + url: '/api/form-templates/{id}'; }; -export type SignatureFieldsControllerRemoveErrors = { +export type FormTemplatesControllerRemoveErrors = { /** * Bad Request */ @@ -866,20 +909,20 @@ export type SignatureFieldsControllerRemoveErrors = { 404: unknown; }; -export type SignatureFieldsControllerRemoveResponses = { +export type FormTemplatesControllerRemoveResponses = { 200: unknown; }; -export type SignatureFieldsControllerFindOneData = { +export type FormTemplatesControllerFindOneData = { body?: never; path: { id: string; }; query?: never; - url: '/api/signature-fields/{id}'; + url: '/api/form-templates/{id}'; }; -export type SignatureFieldsControllerFindOneErrors = { +export type FormTemplatesControllerFindOneErrors = { /** * Bad Request */ @@ -894,23 +937,23 @@ export type SignatureFieldsControllerFindOneErrors = { 404: unknown; }; -export type SignatureFieldsControllerFindOneResponses = { - 200: SignatureFieldEntity; +export type FormTemplatesControllerFindOneResponses = { + 200: FormTemplateEntity; }; -export type SignatureFieldsControllerFindOneResponse = - SignatureFieldsControllerFindOneResponses[keyof SignatureFieldsControllerFindOneResponses]; +export type FormTemplatesControllerFindOneResponse = + FormTemplatesControllerFindOneResponses[keyof FormTemplatesControllerFindOneResponses]; -export type SignatureFieldsControllerUpdateData = { - body: UpdateSignatureFieldDto; +export type FormTemplatesControllerUpdateData = { + body: UpdateFormTemplateDto; path: { id: string; }; query?: never; - url: '/api/signature-fields/{id}'; + url: '/api/form-templates/{id}'; }; -export type SignatureFieldsControllerUpdateErrors = { +export type FormTemplatesControllerUpdateErrors = { /** * Bad Request */ @@ -929,30 +972,12 @@ export type SignatureFieldsControllerUpdateErrors = { 422: unknown; }; -export type SignatureFieldsControllerUpdateResponses = { - 200: SignatureFieldEntity; -}; - -export type SignatureFieldsControllerUpdateResponse = - SignatureFieldsControllerUpdateResponses[keyof SignatureFieldsControllerUpdateResponses]; - -export type SignaturesControllerUpdateSignatureSignerData = { - body: UpdateSignatureSignerDto; - path: { - id: string; - }; - query?: never; - url: '/api/signatures/{id}/signer'; -}; - -export type SignaturesControllerUpdateSignatureSignerResponses = { - 200: { - [key: string]: unknown; - }; +export type FormTemplatesControllerUpdateResponses = { + 200: FormTemplateEntity; }; -export type SignaturesControllerUpdateSignatureSignerResponse = - SignaturesControllerUpdateSignatureSignerResponses[keyof SignaturesControllerUpdateSignatureSignerResponses]; +export type FormTemplatesControllerUpdateResponse = + FormTemplatesControllerUpdateResponses[keyof FormTemplatesControllerUpdateResponses]; export type DepartmentsControllerFindAllData = { body?: never; @@ -1338,7 +1363,7 @@ export type FormInstancesControllerSignFormInstanceData = { body?: never; path: { formInstanceId: string; - signatureId: string; + assignedGroupId: string; }; query?: never; url: '/api/form-instances/{formInstanceId}/sign/{signatureId}'; @@ -1397,155 +1422,6 @@ export type FormInstancesControllerCompleteFormInstanceResponses = { export type FormInstancesControllerCompleteFormInstanceResponse = FormInstancesControllerCompleteFormInstanceResponses[keyof FormInstancesControllerCompleteFormInstanceResponses]; -export type FormTemplatesControllerFindAllData = { - body?: never; - path?: never; - query?: { - /** - * Limit on number of form templates to return - */ - limit?: number; - }; - url: '/api/form-templates'; -}; - -export type FormTemplatesControllerFindAllErrors = { - /** - * Bad Request - */ - 400: unknown; - /** - * Unauthorized Request - */ - 403: unknown; -}; - -export type FormTemplatesControllerFindAllResponses = { - 200: Array; -}; - -export type FormTemplatesControllerFindAllResponse = - FormTemplatesControllerFindAllResponses[keyof FormTemplatesControllerFindAllResponses]; - -export type FormTemplatesControllerCreateData = { - body: CreateFormTemplateDto; - path?: never; - query?: never; - url: '/api/form-templates'; -}; - -export type FormTemplatesControllerCreateErrors = { - /** - * Unauthorized Request - */ - 403: unknown; - /** - * Bad Request - */ - 422: unknown; -}; - -export type FormTemplatesControllerCreateResponses = { - 201: FormTemplateEntity; -}; - -export type FormTemplatesControllerCreateResponse = - FormTemplatesControllerCreateResponses[keyof FormTemplatesControllerCreateResponses]; - -export type FormTemplatesControllerRemoveData = { - body?: never; - path: { - id: string; - }; - query?: never; - url: '/api/form-templates/{id}'; -}; - -export type FormTemplatesControllerRemoveErrors = { - /** - * Bad Request - */ - 400: unknown; - /** - * Unauthorized Request - */ - 403: unknown; - /** - * Resource not found - */ - 404: unknown; -}; - -export type FormTemplatesControllerRemoveResponses = { - 200: unknown; -}; - -export type FormTemplatesControllerFindOneData = { - body?: never; - path: { - id: string; - }; - query?: never; - url: '/api/form-templates/{id}'; -}; - -export type FormTemplatesControllerFindOneErrors = { - /** - * Bad Request - */ - 400: unknown; - /** - * Unauthorized Request - */ - 403: unknown; - /** - * Resource not found - */ - 404: unknown; -}; - -export type FormTemplatesControllerFindOneResponses = { - 200: FormTemplateEntity; -}; - -export type FormTemplatesControllerFindOneResponse = - FormTemplatesControllerFindOneResponses[keyof FormTemplatesControllerFindOneResponses]; - -export type FormTemplatesControllerUpdateData = { - body: UpdateFormTemplateDto; - path: { - id: string; - }; - query?: never; - url: '/api/form-templates/{id}'; -}; - -export type FormTemplatesControllerUpdateErrors = { - /** - * Bad Request - */ - 400: unknown; - /** - * Unauthorized Request - */ - 403: unknown; - /** - * Resource not found - */ - 404: unknown; - /** - * Bad Request - */ - 422: unknown; -}; - -export type FormTemplatesControllerUpdateResponses = { - 200: FormTemplateEntity; -}; - -export type FormTemplatesControllerUpdateResponse = - FormTemplatesControllerUpdateResponses[keyof FormTemplatesControllerUpdateResponses]; - export type ClientOptions = { baseUrl: string; }; diff --git a/apps/web/src/components/AvatarMap.tsx b/apps/web/src/components/AvatarMap.tsx index 70827468..3540c940 100644 --- a/apps/web/src/components/AvatarMap.tsx +++ b/apps/web/src/components/AvatarMap.tsx @@ -1,7 +1,6 @@ import { Avatar, Box, Flex, Text } from '@chakra-ui/react'; import { AwaitingIcon, CheckIcon } from 'apps/web/src/static/icons'; import { AvatarMapProps } from './types'; -import { SignatureEntitySchema } from '@web/client/schemas.gen'; import { SignerType } from '@web/client'; /** @@ -16,7 +15,6 @@ const AssigneeMap: React.FC = ({ assignees }) => { signerType: SignerType, isSigned: boolean, ) => { - SignatureEntitySchema; if (isSigned || signerType === SignerType.USER) { return title; } else if (signerType === SignerType.DEPARTMENT) { diff --git a/apps/web/src/components/FormCard.tsx b/apps/web/src/components/FormCard.tsx index 3f006e04..d02f9c1d 100644 --- a/apps/web/src/components/FormCard.tsx +++ b/apps/web/src/components/FormCard.tsx @@ -1,20 +1,20 @@ import { Box, Text, Avatar, AvatarGroup, Tooltip } from '@chakra-ui/react'; -import { SignatureEntity } from '@web/client/types.gen'; +import { AssignedGroupEntity } from '@web/client'; import { useRouter } from 'next/router'; /** * @param formName - the name of the form - * @param signatures - the signatures on the form + * @param assignedGroups - the assigned groups on the form * @param link - the link to the form * @returns a card for a form */ export const FormCard = ({ formName, - signatures, + assignedGroups, link, }: { formName: String; - signatures: SignatureEntity[]; + assignedGroups: AssignedGroupEntity[]; link: string; }) => { const router = useRouter(); @@ -46,9 +46,9 @@ export const FormCard = ({ {formName} - {signatures + {assignedGroups .sort((a, b) => a.order - b.order) - .map((signature: SignatureEntity, index: number) => { + .map((assignedGroup: AssignedGroupEntity, index: number) => { return ( - {signature.signerEmployee?.firstName + + {assignedGroup.signerEmployee?.firstName + ' ' + - signature.signerEmployee?.lastName} + assignedGroup.signerEmployee?.lastName} } @@ -67,13 +67,15 @@ export const FormCard = ({ > a.order - b.order) .find((v) => v.signed === false); - const _userCanSign = signerIsUser(_nextSignature, user); + const _userCanSign = signerIsUser(_nextAssignedGroup, user); /** - * Update the form instance with the next signature + * Update the form instance with the next assigned group */ const _handleFormSign = async () => { - if (_nextSignature == null || !_userCanSign) return; + if (_nextAssignedGroup == null || !_userCanSign) return; signFormInstanceMutation .mutateAsync({ path: { formInstanceId: formInstance.id, - signatureId: _nextSignature?.id!, + assignedGroupId: _nextAssignedGroup?.id!, }, }) .catch((e) => { @@ -274,11 +274,11 @@ const FormInstance = ({ ({ - signed: signature.signed, - title: getNameFromSignature(signature), - signerType: signature.signerType as any, - updatedAt: signature.updatedAt, + assignees={formInstance.assignedGroups.map((assignedGroup) => ({ + signed: assignedGroup.signed, + title: getNameFromAssignedGroup(assignedGroup), + signerType: assignedGroup.signerType as any, + updatedAt: assignedGroup.updatedAt, }))} /> diff --git a/apps/web/src/components/FormRow.tsx b/apps/web/src/components/FormRow.tsx index 69d4de5c..43ec81ee 100644 --- a/apps/web/src/components/FormRow.tsx +++ b/apps/web/src/components/FormRow.tsx @@ -7,8 +7,8 @@ import { Text, } from '@chakra-ui/react'; -import { FormInstanceEntity, SignatureEntity } from '@web/client/types.gen'; -import { getNameFromSignature } from '@web/utils/formInstanceUtils'; +import { AssignedGroupEntity, FormInstanceEntity } from '@web/client/types.gen'; +import { getNameFromAssignedGroup } from '@web/utils/formInstanceUtils'; import { useRouter } from 'next/router'; import { HoverableAvatar } from './HoverableAvatar'; @@ -90,14 +90,14 @@ export const FormRow = ({ - {formInstance.signatures + {formInstance.assignedGroups .sort((a, b) => a.order - b.order) - .map((signature: SignatureEntity, index: number) => { + .map((assignedGroup: AssignedGroupEntity, index: number) => { return ( <> @@ -106,10 +106,12 @@ export const FormRow = ({ {`${ - formInstance.signatures.filter((signature: SignatureEntity) => { - return signature.signed; - }).length - }/${formInstance.signatures.length}`}{' '} + formInstance.assignedGroups.filter( + (assignedGroup: AssignedGroupEntity) => { + return assignedGroup.signed; + }, + ).length + }/${formInstance.assignedGroups.length}`}{' '} signed diff --git a/apps/web/src/components/OverviewRow.tsx b/apps/web/src/components/OverviewRow.tsx index 0e514024..be898919 100644 --- a/apps/web/src/components/OverviewRow.tsx +++ b/apps/web/src/components/OverviewRow.tsx @@ -79,7 +79,7 @@ export const OverviewRow = ({ ); diff --git a/apps/web/src/components/createFormInstance/CreateFormInstanceModal.tsx b/apps/web/src/components/createFormInstance/CreateFormInstanceModal.tsx index b8654d36..fc614461 100644 --- a/apps/web/src/components/createFormInstance/CreateFormInstanceModal.tsx +++ b/apps/web/src/components/createFormInstance/CreateFormInstanceModal.tsx @@ -23,7 +23,7 @@ import { DropdownDownArrow, DropdownUpArrow } from '@web/static/icons'; import { chakraComponents, Select } from 'chakra-react-select'; import { useMutation, useQuery } from '@tanstack/react-query'; import { SignatureDropdown } from './SignatureDropdown'; -import { CreateFormInstanceModalProps, Option } from './types'; +import { AssignedGroupData, CreateFormInstanceModalProps } from './types'; import { useAuth } from '@web/hooks/useAuth'; import { queryClient } from '@web/pages/_app'; import { GrayPencilIcon } from '@web/static/icons'; @@ -48,10 +48,10 @@ const CreateFormInstanceModal: React.FC = ({ const [isFormTypeDropdownOpen, setIsFormTypeDropdownOpen] = useState(false); const [selectedFormTemplate, setSelectedFormTemplate] = useState(null); - const [formTypeSelected, setFormTypeSelected] = useState(false); - const [signaturePositions, setSignaturePositions] = useState< - (Option | null)[] + const [assignedGroupsData, setAssignedGroupsData] = useState< + AssignedGroupData[] >([]); + const [formTypeSelected, setFormTypeSelected] = useState(false); const [formName, setFormName] = useState('Create Form'); const createFormInstanceMutation = useMutation({ ...formInstancesControllerCreateMutation(), @@ -74,8 +74,15 @@ const CreateFormInstanceModal: React.FC = ({ * Reset signature positions when form template changes */ useEffect(() => { - setSignaturePositions( - new Array(selectedFormTemplate?.signatureFields.length).fill(null), + if (!selectedFormTemplate) return; + + setAssignedGroupsData( + selectedFormTemplate.fieldGroups.map((_, i) => { + return { + fieldGroupId: selectedFormTemplate?.fieldGroups[i].id!, + order: i, + }; + }), ); }, [selectedFormTemplate]); @@ -84,18 +91,21 @@ const CreateFormInstanceModal: React.FC = ({ */ const _submitFormInstance = async () => { if (!selectedFormTemplate) return; + if (!assignedGroupsData) return; await createFormInstanceMutation .mutateAsync({ body: { name: formName, - signatures: signaturePositions.map((pos, i) => { + assignedGroups: assignedGroupsData.map((pos, i) => { return { order: i, - signerEmployeeId: pos?.employeeValue!, - signerType: SignerType.USER, - signerDepartmentId: null, - signerPositionId: null, + fieldGroupId: pos?.fieldGroupId, + // signerEmployeeId: undefined, + signerType: SignerType.POSITION, + // signerDepartmentId: undefined, + // TODO: when we support multiple types, we should create this list outside of the mutation + signerPositionId: pos.positionId!, signerEmployeeList: [], }; }), @@ -130,7 +140,7 @@ const CreateFormInstanceModal: React.FC = ({ setFormName('Create Form'); setSelectedFormTemplate(null); setFormTypeSelected(false); - setSignaturePositions([]); + setAssignedGroupsData([]); }; function EditableControls() { @@ -267,14 +277,14 @@ const CreateFormInstanceModal: React.FC = ({ Assignees - {selectedFormTemplate?.signatureFields.map((field, i) => ( + {selectedFormTemplate?.fieldGroups.map((field, i) => ( ))} diff --git a/apps/web/src/components/createFormInstance/SignatureDropdown.tsx b/apps/web/src/components/createFormInstance/SignatureDropdown.tsx index 6ef1ec02..003684b3 100644 --- a/apps/web/src/components/createFormInstance/SignatureDropdown.tsx +++ b/apps/web/src/components/createFormInstance/SignatureDropdown.tsx @@ -2,9 +2,9 @@ import { Box, Heading } from '@chakra-ui/react'; import { DropdownDownArrow, DropdownUpArrow } from '@web/static/icons'; import { chakraComponents, Select } from 'chakra-react-select'; import { useState } from 'react'; -import { Option } from './types'; +import { AssignedGroupData, PositionOption } from './types'; import { SearchIcon } from '@web/static/icons'; -import { SignatureFieldEntity, PositionEntity } from '@web/client'; +import { FieldGroupBaseEntity, PositionEntity } from '@web/client'; const assigneePlaceholderWithIcon = (
@@ -25,16 +25,18 @@ export const SignatureDropdown = ({ field, index, positions, - signaturePositions, - setSignaturePositions, + assignedGroupData, + setAssignedGroupData, }: { - field: SignatureFieldEntity; + field: FieldGroupBaseEntity; index: number; positions?: PositionEntity[]; - signaturePositions: (Option | null)[]; - setSignaturePositions: (updatedSignaturePositions: (Option | null)[]) => void; + assignedGroupData: AssignedGroupData[]; + setAssignedGroupData: (updatedAssignedGroupData: AssignedGroupData[]) => void; }) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [selectedPosition, setSelectedPosition] = + useState(); /** * Get the employee name from the position @@ -50,7 +52,7 @@ export const SignatureDropdown = ({ /** * Format the option label */ - const _formatOptionLabel = ({ value }: Option) => { + const _formatOptionLabel = ({ value }: PositionOption) => { const positionEntity = positions?.find((position) => position.id === value); const employeeName = positionEntity?.name ?? ''; @@ -77,17 +79,22 @@ export const SignatureDropdown = ({ const fullLabel = fullName + ' ' + position.name; return { value: position.id, - employeeValue: employee?.id ?? '', // Ensure employeeValue is always a string label: fullLabel, }; })} placeholder={assigneePlaceholderWithIcon} - value={signaturePositions[index]} // Create a separate state for Department Head + value={selectedPosition} // Create a separate state for Department Head onChange={(selected) => { - // value is the selected option or null - let updatedSignaturePositions = signaturePositions.slice(0); - updatedSignaturePositions[index] = selected; - setSignaturePositions(updatedSignaturePositions); + setSelectedPosition(selected); + // TODO: probably should not be coercing this type + let selectedAssignedGroupData = assignedGroupData?.at(index)!; + selectedAssignedGroupData.positionId = selected?.value; + assignedGroupData[index] = { + ...selectedAssignedGroupData, + fieldGroupId: selectedAssignedGroupData?.fieldGroupId, + positionId: selected?.value, + }; + setAssignedGroupData(assignedGroupData); }} className="custom-dropdown" components={{ diff --git a/apps/web/src/components/createFormInstance/types.ts b/apps/web/src/components/createFormInstance/types.ts index 1a2555ff..8dd5aa8c 100644 --- a/apps/web/src/components/createFormInstance/types.ts +++ b/apps/web/src/components/createFormInstance/types.ts @@ -1,10 +1,18 @@ +import { PositionEntity } from '@web/client'; + export interface CreateFormInstanceModalProps { isOpen: boolean; onClose: () => void; } -export interface Option { +// value represents the position id +export interface PositionOption { value: string; - employeeValue: string; label: string; } + +export interface AssignedGroupData { + fieldGroupId: string; + order: number; + positionId?: string; +} diff --git a/apps/web/src/components/createFormTemplate/CreateFormTemplateModal.tsx b/apps/web/src/components/createFormTemplate/CreateFormTemplateModal.tsx index 15d2dd8e..a555b76c 100644 --- a/apps/web/src/components/createFormTemplate/CreateFormTemplateModal.tsx +++ b/apps/web/src/components/createFormTemplate/CreateFormTemplateModal.tsx @@ -20,8 +20,8 @@ import { AddIcon, UploadForm } from '@web/static/icons'; import { Reorder } from 'framer-motion'; import { useState } from 'react'; import { useMutation } from '@tanstack/react-query'; -import { SignatureField } from './SignatureField'; -import { TempSignatureField } from './types'; +import { FieldGroup } from './FieldGroup'; +import { TempFieldGroup } from './types'; import { v4 as uuidv4 } from 'uuid'; import { queryClient } from '@web/pages/_app'; import { useBlob } from '@web/hooks/useBlob'; @@ -57,9 +57,8 @@ export const CreateFormTemplateModal = ({ }) => { const [formTemplateName, setFormTemplateName] = useState('New Form Template'); - const [signatureFields, setSignatureFields] = useState( - [], - ); + const [fieldGroups, setFieldGroups] = useState([]); + let isFormTemplateNameInvalid = formTemplateName === ''; const { inputFileRef, @@ -82,24 +81,23 @@ export const CreateFormTemplateModal = ({ }); /** - * Delete a signature field given an id + * Delete a field group given an id */ - const _deleteSignatureField = (id: string) => { - let newSignatureFields = signatureFields.filter((item) => { + const _deleteFieldGroup = (id: string) => { + let newFieldGroups = fieldGroups.filter((item) => { return item.id !== id; }); - setSignatureFields(newSignatureFields); + setFieldGroups(newFieldGroups); }; /** - * Add a signature field + * Add a field group */ - const _handleChange = (newSignatureField: TempSignatureField) => { - let tempSignatureFields = signatureFields.slice(0); - tempSignatureFields.filter( - (value) => value.id === newSignatureField.id, - )[0].value = newSignatureField.value; - setSignatureFields(tempSignatureFields); + const _handleChange = (newFieldGroup: TempFieldGroup) => { + let tempFieldGroups = fieldGroups.slice(0); + tempFieldGroups.filter((value) => value.id === newFieldGroup.id)[0].value = + newFieldGroup.value; + setFieldGroups(tempFieldGroups); }; /** @@ -116,10 +114,13 @@ export const CreateFormTemplateModal = ({ body: { name: formTemplateName, formDocLink: blob.url, - signatureFields: signatureFields.map((signatureField, i) => { + fieldGroups: fieldGroups.map((fieldGroup, i) => { return { - name: signatureField.value, + name: fieldGroup.value, order: i, + // TODO: THESE SHOULD BE SPECIFIED IN THE FORM TEMPLATE CREATION + templateBoxes: [], + formTemplateId: '', }; }), }, @@ -138,7 +139,7 @@ export const CreateFormTemplateModal = ({ */ const _handleModalClose = () => { setFormTemplateName('New Form Template'); - setSignatureFields([]); + setFieldGroups([]); onCloseCreateFormTemplate(); clearLocalBlob(); }; @@ -236,14 +237,14 @@ export const CreateFormTemplateModal = ({ as={Reorder.Group} spacing={2} axis="y" - values={signatureFields} - onReorder={setSignatureFields} + values={fieldGroups} + onReorder={setFieldGroups} mt="20px" > - {signatureFields.map((signatureField) => ( + {fieldGroups.map((fieldGroup) => ( - ))} @@ -271,17 +272,17 @@ export const CreateFormTemplateModal = ({ _groupHover={{ fill: 'var(--chakra-colors-gray-500)' }} /> } - mt={signatureFields.length > 0 ? '14px' : '0px'} + mt={fieldGroups.length > 0 ? '14px' : '0px'} padding="0px" data-group _hover={{ bg: 'transparent' }} onClick={() => { - let currentSignatureFields = signatureFields.slice(0); - currentSignatureFields.push({ + let currentFieldGroups = fieldGroups.slice(0); + currentFieldGroups.push({ id: uuidv4(), value: '', }); - setSignatureFields(currentSignatureFields); + setFieldGroups(currentFieldGroups); }} > field.value === '') + fieldGroups.length == 0 || + fieldGroups.some((field) => field.value === '') } onClick={(_) => { toast.promise(_submitFormTemplate(), { diff --git a/apps/web/src/components/createFormTemplate/SignatureField.tsx b/apps/web/src/components/createFormTemplate/FieldGroup.tsx similarity index 68% rename from apps/web/src/components/createFormTemplate/SignatureField.tsx rename to apps/web/src/components/createFormTemplate/FieldGroup.tsx index c6201b63..a282fda6 100644 --- a/apps/web/src/components/createFormTemplate/SignatureField.tsx +++ b/apps/web/src/components/createFormTemplate/FieldGroup.tsx @@ -17,24 +17,24 @@ import { DeleteIcon, DraggerIcon, } from '@web/static/icons'; -import { TempSignatureField } from './types'; +import { TempFieldGroup } from './types'; /** - * @param field - the signature field - * @param handleChange - function to handle changes to the signature field - * @param handleDelete - function to handle deletion of the signature field - * @returns a signature field + * @param fieldGroup - the field group + * @param handleChange - function to handle changes to the field group + * @param handleDelete - function to handle deletion of the field group + * @returns a field group */ -export const SignatureField = ({ - field, +export const FieldGroup = ({ + fieldGroup, handleChange, handleDelete, }: { - field: TempSignatureField; - handleChange: (newSignatureField: TempSignatureField) => void; + fieldGroup: TempFieldGroup; + handleChange: (newFieldGroup: TempFieldGroup) => void; handleDelete: (id: string) => void; }) => { - const SignatureFieldEditableControls = () => { + const FieldGroupEditableControls = () => { const { isEditing, getEditButtonProps } = useEditableControls(); return isEditing ? ( @@ -43,8 +43,8 @@ export const SignatureField = ({ } - isDisabled={field.value === ''} - onSubmit={() => handleChange(field)} + isDisabled={fieldGroup.value === ''} + onSubmit={() => handleChange(fieldGroup)} > Submit @@ -54,18 +54,18 @@ export const SignatureField = ({ } {...getEditButtonProps()} /> } - onClick={() => handleDelete(field.id)} + onClick={() => handleDelete(fieldGroup.id)} /> @@ -74,7 +74,7 @@ export const SignatureField = ({ return ( <> - {field.value === '' ? ( + {fieldGroup.value === '' ? ( - handleChange({ id: field.id, value: e.target.value }) + handleChange({ id: fieldGroup.id, value: e.target.value }) } w="100%" /> - + diff --git a/apps/web/src/components/createFormTemplate/FormTemplateButtons.tsx b/apps/web/src/components/createFormTemplate/FormTemplateButtons.tsx index 38c0decb..500e527f 100644 --- a/apps/web/src/components/createFormTemplate/FormTemplateButtons.tsx +++ b/apps/web/src/components/createFormTemplate/FormTemplateButtons.tsx @@ -1,10 +1,6 @@ import { Button, Flex, Text } from '@chakra-ui/react'; import { useMutation } from '@tanstack/react-query'; -import { - CreateSignatureFieldDto, - CreateFormTemplateDto, - formTemplatesControllerCreate, -} from '@web/client'; +import { CreateFieldGroupDto } from '@web/client'; import { formTemplatesControllerCreateMutation, formTemplatesControllerFindAllQueryKey, @@ -55,12 +51,13 @@ export const FormTemplateButtons = ({ throw new Error('No PDF file uploaded'); } - const signatures: CreateSignatureFieldDto[] = []; + const fieldGroups: CreateFieldGroupDto[] = []; - Array.from(fieldGroups).forEach(([key, value], index) => { - signatures.push({ - name: `Group ${index + 1}`, + Array.from(fieldGroups).forEach((fieldGroup, index) => { + fieldGroups.push({ + name: fieldGroup.name, order: index, + templateBoxes: fieldGroup.templateBoxes, }); }); @@ -71,7 +68,7 @@ export const FormTemplateButtons = ({ body: { name: formTemplateName ? formTemplateName : '', formDocLink: blob.url, - signatureFields: signatures, + fieldGroups: fieldGroups, }, }) .then((response) => { diff --git a/apps/web/src/components/createFormTemplate/types.ts b/apps/web/src/components/createFormTemplate/types.ts index 78b3a3ac..b1c2eb44 100644 --- a/apps/web/src/components/createFormTemplate/types.ts +++ b/apps/web/src/components/createFormTemplate/types.ts @@ -1,4 +1,4 @@ -export type TempSignatureField = { +export type TempFieldGroup = { id: string; value: string; }; diff --git a/apps/web/src/hooks/useForm.ts b/apps/web/src/hooks/useForm.ts index 422d6c18..1f5fcb2a 100644 --- a/apps/web/src/hooks/useForm.ts +++ b/apps/web/src/hooks/useForm.ts @@ -6,7 +6,7 @@ import { nextSigner, signerIsUser, } from '@web/utils/formInstanceUtils'; -import { FormInstanceEntity, SignatureEntity } from '@web/client'; +import { AssignedGroupEntity, FormInstanceEntity } from '@web/client'; import { formInstancesControllerFindAllAssignedToCurrentEmployeeOptions, formInstancesControllerFindAllCreatedByCurrentEmployeeOptions, @@ -57,10 +57,12 @@ export const useForm = () => { * @returns true if the form instance is signed by the current user, false otherwise */ const isSignedByUser = (formInstance: FormInstanceEntity) => { - const signatures: SignatureEntity[] = formInstance.signatures; + const assignedGroups: AssignedGroupEntity[] = formInstance.assignedGroups; - return signatures.some((signature: SignatureEntity) => { - return signature.signerEmployeeId === user?.id && signature.signed; + return assignedGroups.some((assignedGroup: AssignedGroupEntity) => { + return ( + assignedGroup.signerEmployeeId === user?.id && assignedGroup.signed + ); }); }; diff --git a/apps/web/src/utils/formInstanceUtils.ts b/apps/web/src/utils/formInstanceUtils.ts index 6feff6d9..a59a5afe 100644 --- a/apps/web/src/utils/formInstanceUtils.ts +++ b/apps/web/src/utils/formInstanceUtils.ts @@ -1,4 +1,8 @@ -import { FormInstanceEntity, SignatureEntity, SignerType } from '@web/client'; +import { + AssignedGroupEntity, + FormInstanceEntity, + SignerType, +} from '@web/client'; import { User } from '@web/context/types'; /** @@ -8,45 +12,47 @@ import { User } from '@web/context/types'; * @returns true if the form instance is fully signed, false otherwise */ export const isFullySigned = (formInstance: FormInstanceEntity) => { - const signatures: SignatureEntity[] = formInstance.signatures; + const assignedGroups: AssignedGroupEntity[] = formInstance.assignedGroups; - const unsignedSignatures: SignatureEntity[] = signatures.filter( - (signature: SignatureEntity) => { - return !signature.signed; + const unsignedAssignedGroups: AssignedGroupEntity[] = assignedGroups.filter( + (assignedGroup: AssignedGroupEntity) => { + return !assignedGroup.signed; }, ); - return unsignedSignatures.length === 0; + return unsignedAssignedGroups.length === 0; }; /** - * Finds the name of the signer from a signature - * @param signature the signature to check + * Finds the name of the signer from a assigned group + * @param assignedGroup the assigned group to check * @returns the name of the signer */ -export const getNameFromSignature = (signature: SignatureEntity) => { - const signerType = signature.signerType as any; +export const getNameFromAssignedGroup = ( + assignedGroup: AssignedGroupEntity, +) => { + const signerType = assignedGroup.signerType as any; - if (signature.signed || signerType === SignerType.USER) { + if (assignedGroup.signed || signerType === SignerType.USER) { return ( - signature.signerEmployee?.firstName! + + assignedGroup.signerEmployee?.firstName! + ' ' + - signature.signerEmployee?.lastName! + assignedGroup.signerEmployee?.lastName! ); } else if (signerType === SignerType.DEPARTMENT) { - return signature.signerDepartment?.name!; + return assignedGroup.signerDepartment?.name!; } else if (signerType === SignerType.POSITION) { - return signature.signerPosition?.name!; + return assignedGroup.signerPosition?.name!; } else if (signerType === SignerType.USER_LIST) { - if (signature.signed) { + if (assignedGroup.signed) { return ( - signature.signerEmployee?.firstName! + + assignedGroup.signerEmployee?.firstName! + ' ' + - signature.signerEmployee?.lastName! + assignedGroup.signerEmployee?.lastName! ); } return ( - signature.signerEmployeeList + assignedGroup.signerEmployeeList ?.map((employee) => { return employee.firstName + ' ' + employee.lastName; }) @@ -58,18 +64,20 @@ export const getNameFromSignature = (signature: SignatureEntity) => { }; /** - * Finds the initials of the signer from a signature - * @param signature the signature to check + * Finds the initials of the signer from a assigned group + * @param assignedGroup the assigned group to check * @returns the initials of the signer */ -export const getInitialsFromSignature = (signature: SignatureEntity) => { - const signerType = signature.signerType; +export const getInitialsFromAssignedGroup = ( + assignedGroup: AssignedGroupEntity, +) => { + const signerType = assignedGroup.signerType; - if (signature.signed || signerType === SignerType.USER) { + if (assignedGroup.signed || signerType === SignerType.USER) { return ( - signature.signerEmployee?.firstName! + + assignedGroup.signerEmployee?.firstName! + ' ' + - signature.signerEmployee?.lastName! + assignedGroup.signerEmployee?.lastName! ); } else if (signerType === SignerType.DEPARTMENT) { return 'D'; @@ -82,49 +90,51 @@ export const getInitialsFromSignature = (signature: SignatureEntity) => { }; /** - * Finds the next signer in the signature chain of a form instance + * Finds the next signer in the assigned group chain of a form instance * * @param formInstance the form instance to check - * @returns the next signer in the signature chain + * @returns the next signer in the assignedGroup chain */ export const nextSigner = (formInstance: FormInstanceEntity) => { - const signatures: SignatureEntity[] = formInstance.signatures; + const assignedGroups: AssignedGroupEntity[] = formInstance.assignedGroups; // Sort signatures by order - signatures.sort((a: SignatureEntity, b: SignatureEntity) => { + assignedGroups.sort((a: AssignedGroupEntity, b: AssignedGroupEntity) => { return a.order - b.order; }); - // Find the first signature that doesn't have a signature - const firstUnsignedSignature: SignatureEntity | undefined = signatures.find( - (signature: SignatureEntity) => { - return signature.signed === false; - }, - ); + // Find the first assigned group that is not signed + const firstUnsignedAssignedGroup: AssignedGroupEntity | undefined = + assignedGroups.find((assignedGroup: AssignedGroupEntity) => { + return assignedGroup.signed === false; + }); - return firstUnsignedSignature; + return firstUnsignedAssignedGroup; }; /** * Determines if a signature's next signer is the current user * - * @param signature the signature to check + * @param assignedGroup the assignedGroup to check * @param user the current user * @returns true if the next signer is the current user, false otherwise */ -export const signerIsUser = (signature?: SignatureEntity, user?: User) => { - if (!signature || !user) return false; +export const signerIsUser = ( + assignedGroup?: AssignedGroupEntity, + user?: User, +) => { + if (!assignedGroup || !user) return false; - const signerType = signature.signerType; + const signerType = assignedGroup.signerType; return ( (signerType === SignerType.USER && - signature.signerEmployeeId === user?.id) || + assignedGroup.signerEmployeeId === user?.id) || (signerType === SignerType.POSITION && - signature.signerPositionId === user?.positionId) || + assignedGroup.signerPositionId === user?.positionId) || (signerType === SignerType.DEPARTMENT && - user?.departmentId === signature.signerDepartmentId) || + user?.departmentId === assignedGroup.signerDepartmentId) || (signerType === SignerType.USER_LIST && - signature.signerEmployeeList?.some( + assignedGroup.signerEmployeeList?.some( (employee) => employee.id === user?.id, )) ); diff --git a/tsconfig.json b/tsconfig.json index bd102a52..63ec268b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,6 @@ "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "paths": { - "@server/*": ["./apps/server/src/*"], "@web/*": ["./apps/web/src/*"] } }