Opportunity fields (#744)

* Add opportunity probability and point of contact

* Have requests sent properly

* Add probaility field
This commit is contained in:
Emilien Chauvet
2023-07-18 17:32:15 -07:00
committed by GitHub
parent 87a116d369
commit f98e49c26e
7 changed files with 139 additions and 21 deletions

View File

@ -992,6 +992,7 @@ export type NullableStringFieldUpdateOperationsInput = {
export type Person = {
__typename?: 'Person';
PipelineProgress?: Maybe<Array<PipelineProgress>>;
_commentThreadCount: Scalars['Int'];
city: Scalars['String'];
commentThreads: Array<CommentThread>;
@ -1009,6 +1010,7 @@ export type Person = {
};
export type PersonCreateInput = {
PipelineProgress?: InputMaybe<PipelineProgressCreateNestedManyWithoutPointOfContactInput>;
city: Scalars['String'];
company?: InputMaybe<CompanyCreateNestedOneWithoutPeopleInput>;
createdAt?: InputMaybe<Scalars['DateTime']>;
@ -1024,6 +1026,10 @@ export type PersonCreateNestedManyWithoutCompanyInput = {
connect?: InputMaybe<Array<PersonWhereUniqueInput>>;
};
export type PersonCreateNestedOneWithoutPipelineProgressInput = {
connect?: InputMaybe<PersonWhereUniqueInput>;
};
export type PersonListRelationFilter = {
every?: InputMaybe<PersonWhereInput>;
none?: InputMaybe<PersonWhereInput>;
@ -1035,6 +1041,7 @@ export type PersonOrderByRelationAggregateInput = {
};
export type PersonOrderByWithRelationInput = {
PipelineProgress?: InputMaybe<PipelineProgressOrderByRelationAggregateInput>;
city?: InputMaybe<SortOrder>;
company?: InputMaybe<CompanyOrderByWithRelationInput>;
companyId?: InputMaybe<SortOrder>;
@ -1047,6 +1054,11 @@ export type PersonOrderByWithRelationInput = {
updatedAt?: InputMaybe<SortOrder>;
};
export type PersonRelationFilter = {
is?: InputMaybe<PersonWhereInput>;
isNot?: InputMaybe<PersonWhereInput>;
};
export enum PersonScalarFieldEnum {
City = 'city',
CompanyId = 'companyId',
@ -1062,6 +1074,7 @@ export enum PersonScalarFieldEnum {
}
export type PersonUpdateInput = {
PipelineProgress?: InputMaybe<PipelineProgressUpdateManyWithoutPointOfContactNestedInput>;
city?: InputMaybe<StringFieldUpdateOperationsInput>;
company?: InputMaybe<CompanyUpdateOneWithoutPeopleNestedInput>;
createdAt?: InputMaybe<DateTimeFieldUpdateOperationsInput>;
@ -1085,10 +1098,16 @@ export type PersonUpdateManyWithoutWorkspaceNestedInput = {
set?: InputMaybe<Array<PersonWhereUniqueInput>>;
};
export type PersonUpdateOneWithoutPipelineProgressNestedInput = {
connect?: InputMaybe<PersonWhereUniqueInput>;
disconnect?: InputMaybe<Scalars['Boolean']>;
};
export type PersonWhereInput = {
AND?: InputMaybe<Array<PersonWhereInput>>;
NOT?: InputMaybe<Array<PersonWhereInput>>;
OR?: InputMaybe<Array<PersonWhereInput>>;
PipelineProgress?: InputMaybe<PipelineProgressListRelationFilter>;
city?: InputMaybe<StringFilter>;
company?: InputMaybe<CompanyRelationFilter>;
companyId?: InputMaybe<StringNullableFilter>;
@ -1142,6 +1161,9 @@ export type PipelineProgress = {
pipelineId: Scalars['String'];
pipelineStage: PipelineStage;
pipelineStageId: Scalars['String'];
pointOfContact?: Maybe<Person>;
pointOfContactId?: Maybe<Scalars['String']>;
probability?: Maybe<Scalars['Int']>;
progressableId: Scalars['String'];
progressableType: PipelineProgressableType;
updatedAt: Scalars['DateTime'];
@ -1154,11 +1176,17 @@ export type PipelineProgressCreateInput = {
id?: InputMaybe<Scalars['String']>;
pipeline: PipelineCreateNestedOneWithoutPipelineProgressesInput;
pipelineStage: PipelineStageCreateNestedOneWithoutPipelineProgressesInput;
pointOfContact?: InputMaybe<PersonCreateNestedOneWithoutPipelineProgressInput>;
probability?: InputMaybe<Scalars['Int']>;
progressableId: Scalars['String'];
progressableType: PipelineProgressableType;
updatedAt?: InputMaybe<Scalars['DateTime']>;
};
export type PipelineProgressCreateNestedManyWithoutPointOfContactInput = {
connect?: InputMaybe<Array<PipelineProgressWhereUniqueInput>>;
};
export type PipelineProgressListRelationFilter = {
every?: InputMaybe<PipelineProgressWhereInput>;
none?: InputMaybe<PipelineProgressWhereInput>;
@ -1178,6 +1206,9 @@ export type PipelineProgressOrderByWithRelationInput = {
pipelineId?: InputMaybe<SortOrder>;
pipelineStage?: InputMaybe<PipelineStageOrderByWithRelationInput>;
pipelineStageId?: InputMaybe<SortOrder>;
pointOfContact?: InputMaybe<PersonOrderByWithRelationInput>;
pointOfContactId?: InputMaybe<SortOrder>;
probability?: InputMaybe<SortOrder>;
progressableId?: InputMaybe<SortOrder>;
progressableType?: InputMaybe<SortOrder>;
updatedAt?: InputMaybe<SortOrder>;
@ -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<StringFieldUpdateOperationsInput>;
pipeline?: InputMaybe<PipelineUpdateOneRequiredWithoutPipelineProgressesNestedInput>;
pipelineStage?: InputMaybe<PipelineStageUpdateOneRequiredWithoutPipelineProgressesNestedInput>;
pointOfContact?: InputMaybe<PersonUpdateOneWithoutPipelineProgressNestedInput>;
probability?: InputMaybe<NullableIntFieldUpdateOperationsInput>;
progressableId?: InputMaybe<StringFieldUpdateOperationsInput>;
progressableType?: InputMaybe<EnumPipelineProgressableTypeFieldUpdateOperationsInput>;
updatedAt?: InputMaybe<DateTimeFieldUpdateOperationsInput>;
@ -1215,6 +1250,12 @@ export type PipelineProgressUpdateManyWithoutPipelineStageNestedInput = {
set?: InputMaybe<Array<PipelineProgressWhereUniqueInput>>;
};
export type PipelineProgressUpdateManyWithoutPointOfContactNestedInput = {
connect?: InputMaybe<Array<PipelineProgressWhereUniqueInput>>;
disconnect?: InputMaybe<Array<PipelineProgressWhereUniqueInput>>;
set?: InputMaybe<Array<PipelineProgressWhereUniqueInput>>;
};
export type PipelineProgressUpdateManyWithoutWorkspaceNestedInput = {
connect?: InputMaybe<Array<PipelineProgressWhereUniqueInput>>;
disconnect?: InputMaybe<Array<PipelineProgressWhereUniqueInput>>;
@ -1233,6 +1274,9 @@ export type PipelineProgressWhereInput = {
pipelineId?: InputMaybe<StringFilter>;
pipelineStage?: InputMaybe<PipelineStageRelationFilter>;
pipelineStageId?: InputMaybe<StringFilter>;
pointOfContact?: InputMaybe<PersonRelationFilter>;
pointOfContactId?: InputMaybe<StringNullableFilter>;
probability?: InputMaybe<IntNullableFilter>;
progressableId?: InputMaybe<StringFilter>;
progressableType?: InputMaybe<EnumPipelineProgressableTypeFilter>;
updatedAt?: InputMaybe<DateTimeFilter>;
@ -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<Scalars['String']>;
amount?: InputMaybe<Scalars['Int']>;
closeDate?: InputMaybe<Scalars['DateTime']>;
probability?: InputMaybe<Scalars['Int']>;
pointOfContactId?: InputMaybe<Scalars['String']>;
}>;
@ -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<typeof useGetPipelin
export type GetPipelineProgressLazyQueryHookResult = ReturnType<typeof useGetPipelineProgressLazyQuery>;
export type GetPipelineProgressQueryResult = Apollo.QueryResult<GetPipelineProgressQuery, GetPipelineProgressQueryVariables>;
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<Update
* id: // value for 'id'
* amount: // value for 'amount'
* closeDate: // value for 'closeDate'
* probability: // value for 'probability'
* pointOfContactId: // value for 'pointOfContactId'
* },
* });
*/

View File

@ -12,17 +12,15 @@ import { selectedBoardCardsState } from '@/pipeline/states/selectedBoardCardsSta
import { BoardCardEditableFieldDate } from '@/ui/board/card-field/components/BoardCardEditableFieldDate';
import { ChipVariant } from '@/ui/chip/components/EntityChip';
import { NumberEditableField } from '@/ui/editable-field/variants/components/NumberEditableField';
import { IconCurrencyDollar } from '@/ui/icon';
import { IconCheck, IconCurrencyDollar } from '@/ui/icon';
import { IconCalendarEvent } from '@/ui/icon';
import { Checkbox } from '@/ui/input/components/Checkbox';
import { useRecoilScopedState } from '@/ui/recoil-scope/hooks/useRecoilScopedState';
import {
PipelineProgress,
useUpdateOnePipelineProgressMutation,
} from '~/generated/graphql';
import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql';
import { getLogoUrlFromDomainName } from '~/utils';
import { CompanyAccountOwnerEditableField } from '../editable-field/components/CompanyAccountOwnerEditableField';
import { PipelineProgressForBoard } from '../types/CompanyProgress';
import { CompanyChip } from './CompanyChip';
@ -104,14 +102,14 @@ export function CompanyBoardCard() {
}
const handleCardUpdate = useCallback(
async (
pipelineProgress: Pick<PipelineProgress, 'id' | 'amount' | 'closeDate'>,
) => {
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() {
}}
/>
</span>
<NumberEditableField
icon={<IconCheck />}
placeholder="Opportunity probability for closing"
value={pipelineProgress.probability}
onSubmit={(value) =>
handleCardUpdate({
...pipelineProgress,
probability: value,
})
}
/>
</StyledBoardCardBody>
</StyledBoardCard>
</StyledBoardCardWrapper>

View File

@ -1,10 +1,17 @@
import { Company, PipelineProgress } from '~/generated/graphql';
import { Company, Person, PipelineProgress } from '~/generated/graphql';
export type CompanyForBoard = Pick<Company, 'id' | 'name' | 'domainName'>;
export type PipelineProgressForBoard = Pick<
PipelineProgress,
'id' | 'amount' | 'closeDate' | 'progressableId'
>;
| 'id'
| 'amount'
| 'closeDate'
| 'progressableId'
| 'probability'
| 'pointOfContactId'
> & {
pointOfContact?: Pick<Person, 'id' | 'firstName' | 'lastName'> | null;
};
export type CompanyProgress = {
company: CompanyForBoard;

View File

@ -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

View File

@ -68,6 +68,20 @@ export class PipelineProgressResolver {
@PrismaSelector({ modelName: 'PipelineProgress' })
prismaSelect: PrismaSelect<'PipelineProgress'>,
): Promise<Partial<PipelineProgress> | 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,

View File

@ -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;

View File

@ -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)