From f98e49c26e387e5696f47aa073817b8a009a0b0e Mon Sep 17 00:00:00 2001 From: Emilien Chauvet Date: Tue, 18 Jul 2023 17:32:15 -0700 Subject: [PATCH] Opportunity fields (#744) * Add opportunity probability and point of contact * Have requests sent properly * Add probaility field --- front/src/generated/graphql.tsx | 61 ++++++++++++++++++- .../companies/components/CompanyBoardCard.tsx | 27 +++++--- .../companies/types/CompanyProgress.ts | 13 +++- front/src/modules/pipeline/queries/select.ts | 16 ++++- .../resolvers/pipeline-progress.resolver.ts | 14 +++++ .../migration.sql | 15 +++++ server/src/database/schema.prisma | 14 +++-- 7 files changed, 139 insertions(+), 21 deletions(-) create mode 100644 server/src/database/migrations/20230718224818_add_opportunity_fields/migration.sql diff --git a/front/src/generated/graphql.tsx b/front/src/generated/graphql.tsx index 942892e6a..094a56095 100644 --- a/front/src/generated/graphql.tsx +++ b/front/src/generated/graphql.tsx @@ -992,6 +992,7 @@ export type NullableStringFieldUpdateOperationsInput = { export type Person = { __typename?: 'Person'; + PipelineProgress?: Maybe>; _commentThreadCount: Scalars['Int']; city: Scalars['String']; commentThreads: Array; @@ -1009,6 +1010,7 @@ export type Person = { }; export type PersonCreateInput = { + PipelineProgress?: InputMaybe; city: Scalars['String']; company?: InputMaybe; createdAt?: InputMaybe; @@ -1024,6 +1026,10 @@ export type PersonCreateNestedManyWithoutCompanyInput = { connect?: InputMaybe>; }; +export type PersonCreateNestedOneWithoutPipelineProgressInput = { + connect?: InputMaybe; +}; + export type PersonListRelationFilter = { every?: InputMaybe; none?: InputMaybe; @@ -1035,6 +1041,7 @@ export type PersonOrderByRelationAggregateInput = { }; export type PersonOrderByWithRelationInput = { + PipelineProgress?: InputMaybe; city?: InputMaybe; company?: InputMaybe; companyId?: InputMaybe; @@ -1047,6 +1054,11 @@ export type PersonOrderByWithRelationInput = { updatedAt?: InputMaybe; }; +export type PersonRelationFilter = { + is?: InputMaybe; + isNot?: InputMaybe; +}; + export enum PersonScalarFieldEnum { City = 'city', CompanyId = 'companyId', @@ -1062,6 +1074,7 @@ export enum PersonScalarFieldEnum { } export type PersonUpdateInput = { + PipelineProgress?: InputMaybe; city?: InputMaybe; company?: InputMaybe; createdAt?: InputMaybe; @@ -1085,10 +1098,16 @@ export type PersonUpdateManyWithoutWorkspaceNestedInput = { set?: InputMaybe>; }; +export type PersonUpdateOneWithoutPipelineProgressNestedInput = { + connect?: InputMaybe; + disconnect?: InputMaybe; +}; + export type PersonWhereInput = { AND?: InputMaybe>; NOT?: InputMaybe>; OR?: InputMaybe>; + PipelineProgress?: InputMaybe; city?: InputMaybe; company?: InputMaybe; companyId?: InputMaybe; @@ -1142,6 +1161,9 @@ export type PipelineProgress = { pipelineId: Scalars['String']; pipelineStage: PipelineStage; pipelineStageId: Scalars['String']; + pointOfContact?: Maybe; + pointOfContactId?: Maybe; + probability?: Maybe; progressableId: Scalars['String']; progressableType: PipelineProgressableType; updatedAt: Scalars['DateTime']; @@ -1154,11 +1176,17 @@ export type PipelineProgressCreateInput = { id?: InputMaybe; pipeline: PipelineCreateNestedOneWithoutPipelineProgressesInput; pipelineStage: PipelineStageCreateNestedOneWithoutPipelineProgressesInput; + pointOfContact?: InputMaybe; + probability?: InputMaybe; progressableId: Scalars['String']; progressableType: PipelineProgressableType; updatedAt?: InputMaybe; }; +export type PipelineProgressCreateNestedManyWithoutPointOfContactInput = { + connect?: InputMaybe>; +}; + export type PipelineProgressListRelationFilter = { every?: InputMaybe; none?: InputMaybe; @@ -1178,6 +1206,9 @@ export type PipelineProgressOrderByWithRelationInput = { pipelineId?: InputMaybe; pipelineStage?: InputMaybe; pipelineStageId?: InputMaybe; + pointOfContact?: InputMaybe; + pointOfContactId?: InputMaybe; + probability?: InputMaybe; progressableId?: InputMaybe; progressableType?: InputMaybe; updatedAt?: InputMaybe; @@ -1191,6 +1222,8 @@ export enum PipelineProgressScalarFieldEnum { Id = 'id', PipelineId = 'pipelineId', PipelineStageId = 'pipelineStageId', + PointOfContactId = 'pointOfContactId', + Probability = 'probability', ProgressableId = 'progressableId', ProgressableType = 'progressableType', UpdatedAt = 'updatedAt', @@ -1204,6 +1237,8 @@ export type PipelineProgressUpdateInput = { id?: InputMaybe; pipeline?: InputMaybe; pipelineStage?: InputMaybe; + pointOfContact?: InputMaybe; + probability?: InputMaybe; progressableId?: InputMaybe; progressableType?: InputMaybe; updatedAt?: InputMaybe; @@ -1215,6 +1250,12 @@ export type PipelineProgressUpdateManyWithoutPipelineStageNestedInput = { set?: InputMaybe>; }; +export type PipelineProgressUpdateManyWithoutPointOfContactNestedInput = { + connect?: InputMaybe>; + disconnect?: InputMaybe>; + set?: InputMaybe>; +}; + export type PipelineProgressUpdateManyWithoutWorkspaceNestedInput = { connect?: InputMaybe>; disconnect?: InputMaybe>; @@ -1233,6 +1274,9 @@ export type PipelineProgressWhereInput = { pipelineId?: InputMaybe; pipelineStage?: InputMaybe; pipelineStageId?: InputMaybe; + pointOfContact?: InputMaybe; + pointOfContactId?: InputMaybe; + probability?: InputMaybe; progressableId?: InputMaybe; progressableType?: InputMaybe; updatedAt?: InputMaybe; @@ -2165,12 +2209,14 @@ export type GetPipelineProgressQueryVariables = Exact<{ }>; -export type GetPipelineProgressQuery = { __typename?: 'Query', findManyPipelineProgress: Array<{ __typename?: 'PipelineProgress', id: string, pipelineStageId: string, progressableType: PipelineProgressableType, progressableId: string, amount?: number | null, closeDate?: string | null }> }; +export type GetPipelineProgressQuery = { __typename?: 'Query', findManyPipelineProgress: Array<{ __typename?: 'PipelineProgress', id: string, pipelineStageId: string, progressableType: PipelineProgressableType, progressableId: string, amount?: number | null, closeDate?: string | null, pointOfContactId?: string | null, probability?: number | null, pointOfContact?: { __typename?: 'Person', id: string, firstName: string, lastName: string } | null }> }; export type UpdateOnePipelineProgressMutationVariables = Exact<{ id?: InputMaybe; amount?: InputMaybe; closeDate?: InputMaybe; + probability?: InputMaybe; + pointOfContactId?: InputMaybe; }>; @@ -3809,6 +3855,13 @@ export const GetPipelineProgressDocument = gql` progressableId amount closeDate + pointOfContactId + pointOfContact { + id + firstName + lastName + } + probability } } `; @@ -3842,10 +3895,10 @@ export type GetPipelineProgressQueryHookResult = ReturnType; export type GetPipelineProgressQueryResult = Apollo.QueryResult; export const UpdateOnePipelineProgressDocument = gql` - mutation UpdateOnePipelineProgress($id: String, $amount: Int, $closeDate: DateTime) { + mutation UpdateOnePipelineProgress($id: String, $amount: Int, $closeDate: DateTime, $probability: Int, $pointOfContactId: String) { updateOnePipelineProgress( where: {id: $id} - data: {amount: {set: $amount}, closeDate: {set: $closeDate}} + data: {amount: {set: $amount}, closeDate: {set: $closeDate}, probability: {set: $probability}, pointOfContact: {connect: {id: $pointOfContactId}}} ) { id amount @@ -3871,6 +3924,8 @@ export type UpdateOnePipelineProgressMutationFn = Apollo.MutationFunction, - ) => { + async (pipelineProgress: PipelineProgressForBoard) => { await updatePipelineProgress({ variables: { id: pipelineProgress.id, amount: pipelineProgress.amount, - closeDate: pipelineProgress.closeDate || null, + closeDate: pipelineProgress.closeDate, + probability: pipelineProgress.probability, + pointOfContactId: pipelineProgress.pointOfContactId || undefined, }, refetchQueries: [ getOperationName(GET_PIPELINE_PROGRESS) ?? '', @@ -169,6 +167,17 @@ export function CompanyBoardCard() { }} /> + } + placeholder="Opportunity probability for closing" + value={pipelineProgress.probability} + onSubmit={(value) => + handleCardUpdate({ + ...pipelineProgress, + probability: value, + }) + } + /> diff --git a/front/src/modules/companies/types/CompanyProgress.ts b/front/src/modules/companies/types/CompanyProgress.ts index 42b113ace..e6c9e0cfe 100644 --- a/front/src/modules/companies/types/CompanyProgress.ts +++ b/front/src/modules/companies/types/CompanyProgress.ts @@ -1,10 +1,17 @@ -import { Company, PipelineProgress } from '~/generated/graphql'; +import { Company, Person, PipelineProgress } from '~/generated/graphql'; export type CompanyForBoard = Pick; export type PipelineProgressForBoard = Pick< PipelineProgress, - 'id' | 'amount' | 'closeDate' | 'progressableId' ->; + | 'id' + | 'amount' + | 'closeDate' + | 'progressableId' + | 'probability' + | 'pointOfContactId' +> & { + pointOfContact?: Pick | null; +}; export type CompanyProgress = { company: CompanyForBoard; diff --git a/front/src/modules/pipeline/queries/select.ts b/front/src/modules/pipeline/queries/select.ts index 5d64c1a38..3d7fc8744 100644 --- a/front/src/modules/pipeline/queries/select.ts +++ b/front/src/modules/pipeline/queries/select.ts @@ -37,6 +37,13 @@ export const GET_PIPELINE_PROGRESS = gql` progressableId amount closeDate + pointOfContactId + pointOfContact { + id + firstName + lastName + } + probability } } `; @@ -46,10 +53,17 @@ export const UPDATE_PIPELINE_PROGRESS = gql` $id: String $amount: Int $closeDate: DateTime + $probability: Int + $pointOfContactId: String ) { updateOnePipelineProgress( where: { id: $id } - data: { amount: { set: $amount }, closeDate: { set: $closeDate } } + data: { + amount: { set: $amount } + closeDate: { set: $closeDate } + probability: { set: $probability } + pointOfContact: { connect: { id: $pointOfContactId } } + } ) { id amount diff --git a/server/src/core/pipeline/resolvers/pipeline-progress.resolver.ts b/server/src/core/pipeline/resolvers/pipeline-progress.resolver.ts index 8e6ddf19c..59d6ebaca 100644 --- a/server/src/core/pipeline/resolvers/pipeline-progress.resolver.ts +++ b/server/src/core/pipeline/resolvers/pipeline-progress.resolver.ts @@ -68,6 +68,20 @@ export class PipelineProgressResolver { @PrismaSelector({ modelName: 'PipelineProgress' }) prismaSelect: PrismaSelect<'PipelineProgress'>, ): Promise | null> { + // TODO: Do a proper check with recursion testing on args in a more generic place + for (const key in args.data) { + if (args.data[key]) { + for (const subKey in args.data[key]) { + if (JSON.stringify(args.data[key][subKey]) === '{}') { + delete args.data[key][subKey]; + } + } + } + + if (JSON.stringify(args.data[key]) === '{}') { + delete args.data[key]; + } + } return this.pipelineProgressService.update({ where: args.where, data: args.data, diff --git a/server/src/database/migrations/20230718224818_add_opportunity_fields/migration.sql b/server/src/database/migrations/20230718224818_add_opportunity_fields/migration.sql new file mode 100644 index 000000000..d86a7d006 --- /dev/null +++ b/server/src/database/migrations/20230718224818_add_opportunity_fields/migration.sql @@ -0,0 +1,15 @@ +/* + Warnings: + + - Made the column `settingsId` on table `users` required. This step will fail if there are existing NULL values in that column. + +*/ +-- AlterTable +ALTER TABLE "pipeline_progresses" ADD COLUMN "pointOfContactId" TEXT, +ADD COLUMN "probability" INTEGER; + +-- AlterTable +ALTER TABLE "users" ALTER COLUMN "settingsId" SET NOT NULL; + +-- AddForeignKey +ALTER TABLE "pipeline_progresses" ADD CONSTRAINT "pipeline_progresses_pointOfContactId_fkey" FOREIGN KEY ("pointOfContactId") REFERENCES "people"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/server/src/database/schema.prisma b/server/src/database/schema.prisma index 9349764e1..34bc0c970 100644 --- a/server/src/database/schema.prisma +++ b/server/src/database/schema.prisma @@ -251,8 +251,9 @@ model Person { /// @TypeGraphQL.omit(input: true, output: true) deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + PipelineProgress PipelineProgress[] @@map("people") } @@ -436,14 +437,17 @@ enum PipelineProgressableType { model PipelineProgress { /// @Validator.IsString() /// @Validator.IsOptional() - id String @id @default(uuid()) - amount Int? - closeDate DateTime? + id String @id @default(uuid()) + amount Int? + closeDate DateTime? + probability Int? pipeline Pipeline @relation(fields: [pipelineId], references: [id]) pipelineId String pipelineStage PipelineStage @relation(fields: [pipelineStageId], references: [id]) pipelineStageId String + pointOfContact Person? @relation(fields: [pointOfContactId], references: [id]) + pointOfContactId String? progressableType PipelineProgressableType progressableId String /// @TypeGraphQL.omit(input: true, output: true)