V2 opportunities (#2565)
* changed isSystem to false * wip * wip * wip * add amount viewfield seed * seed other viewFields * upate tenant seeds * Remove calls to old pipelines --------- Co-authored-by: Charles Bochet <charles@twenty.com> Co-authored-by: Charles Bochet <charlesBochet@users.noreply.github.com>
This commit is contained in:
@ -2,6 +2,7 @@ import { ReactNode, useContext } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
|
||||
import { EntityChipVariant } from '@/ui/display/chip/components/EntityChip';
|
||||
import { IconEye } from '@/ui/display/icon/index';
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
@ -17,7 +18,6 @@ import { RecordInlineCell } from '@/ui/object/record-inline-cell/components/Reco
|
||||
import { InlineCellHotkeyScope } from '@/ui/object/record-inline-cell/types/InlineCellHotkeyScope';
|
||||
import { AnimatedEaseInOut } from '@/ui/utilities/animation/components/AnimatedEaseInOut';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
|
||||
import { companyProgressesFamilyState } from '../states/companyProgressesFamilyState';
|
||||
@ -150,6 +150,30 @@ export const CompanyBoardCard = () => {
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
|
||||
const useUpdateOneObjectMutation: () => [(params: any) => any, any] = () => {
|
||||
const { updateOneObject } = useUpdateOneObjectRecord({
|
||||
objectNameSingular: 'opportunityV2',
|
||||
});
|
||||
|
||||
const updateEntity = ({
|
||||
variables,
|
||||
}: {
|
||||
variables: {
|
||||
where: { id: string };
|
||||
data: {
|
||||
[fieldName: string]: any;
|
||||
};
|
||||
};
|
||||
}) => {
|
||||
updateOneObject?.({
|
||||
idToUpdate: variables.where.id,
|
||||
input: variables.data,
|
||||
});
|
||||
};
|
||||
|
||||
return [updateEntity, { loading: false }];
|
||||
};
|
||||
|
||||
// boardCardId check can be moved to a wrapper to avoid unnecessary logic above
|
||||
if (!company || !pipelineProgress || !boardCardId) {
|
||||
return null;
|
||||
@ -224,8 +248,7 @@ export const CompanyBoardCard = () => {
|
||||
entityChipDisplayMapper:
|
||||
viewField.entityChipDisplayMapper,
|
||||
},
|
||||
useUpdateEntityMutation:
|
||||
useUpdateOnePipelineProgressMutation,
|
||||
useUpdateEntityMutation: useUpdateOneObjectMutation,
|
||||
hotkeyScope: InlineCellHotkeyScope.InlineCell,
|
||||
}}
|
||||
>
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
|
||||
import { PaginatedObjectTypeResults } from '@/object-record/types/PaginatedObjectTypeResults';
|
||||
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
import { useBoardActionBarEntries } from '@/ui/layout/board/hooks/useBoardActionBarEntries';
|
||||
import { useBoardContext } from '@/ui/layout/board/hooks/useBoardContext';
|
||||
import { useBoardContextMenuEntries } from '@/ui/layout/board/hooks/useBoardContextMenuEntries';
|
||||
@ -15,13 +19,7 @@ import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates'
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { mapViewFieldsToBoardFieldDefinitions } from '@/views/utils/mapViewFieldsToBoardFieldDefinitions';
|
||||
import {
|
||||
Pipeline,
|
||||
PipelineProgressableType,
|
||||
useGetCompaniesQuery,
|
||||
useGetPipelineProgressQuery,
|
||||
useGetPipelinesQuery,
|
||||
} from '~/generated/graphql';
|
||||
import { Company } from '~/generated-metadata/graphql';
|
||||
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
||||
|
||||
import { useUpdateCompanyBoardCardIds } from '../hooks/useUpdateBoardCardIds';
|
||||
@ -40,6 +38,10 @@ export const HooksCompanyBoardEffect = () => {
|
||||
const { currentViewFiltersState, currentViewFieldsState } =
|
||||
useViewScopedStates();
|
||||
|
||||
const [pipelineSteps, setPipelineSteps] = useState<PipelineStep[]>([]);
|
||||
const [opportunities, setOpportunities] = useState<Opportunity[]>([]);
|
||||
const [companies, setCompanies] = useState<Company[]>([]);
|
||||
|
||||
const currentViewFields = useRecoilValue(currentViewFieldsState);
|
||||
const currentViewFilters = useRecoilValue(currentViewFiltersState);
|
||||
|
||||
@ -56,21 +58,62 @@ export const HooksCompanyBoardEffect = () => {
|
||||
availableBoardCardFieldsScopedState,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
|
||||
const updateCompanyBoardCardIds = useUpdateCompanyBoardCardIds();
|
||||
const updateCompanyBoard = useUpdateCompanyBoard();
|
||||
|
||||
const { data: pipelineData, loading: loadingGetPipelines } =
|
||||
useGetPipelinesQuery({
|
||||
variables: {
|
||||
where: {
|
||||
pipelineProgressableType: {
|
||||
equals: PipelineProgressableType.Company,
|
||||
useFindManyObjectRecords({
|
||||
objectNamePlural: 'pipelineStepsV2',
|
||||
filter: {},
|
||||
onCompleted: useCallback(
|
||||
(data: PaginatedObjectTypeResults<PipelineStep>) => {
|
||||
setPipelineSteps(data.edges.map((edge) => edge.node));
|
||||
},
|
||||
[],
|
||||
),
|
||||
});
|
||||
|
||||
const whereFilters = useMemo(() => {
|
||||
return {
|
||||
AND: [
|
||||
{
|
||||
pipelineStageId: {
|
||||
in: pipelineSteps.map((pipelineStep) => pipelineStep.id),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
...(currentViewFilters?.map(turnFilterIntoWhereClause) || []),
|
||||
],
|
||||
};
|
||||
}, [currentViewFilters, pipelineSteps]) as any;
|
||||
|
||||
const pipeline = pipelineData?.findManyPipeline[0] as Pipeline | undefined;
|
||||
useFindManyObjectRecords({
|
||||
skip: !pipelineSteps.length,
|
||||
objectNamePlural: 'opportunitiesV2',
|
||||
filter: whereFilters,
|
||||
onCompleted: useCallback(
|
||||
(_data: PaginatedObjectTypeResults<Opportunity>) => {
|
||||
const pipelineProgresses: Array<Opportunity> = [];
|
||||
|
||||
updateCompanyBoardCardIds(pipelineProgresses);
|
||||
|
||||
setOpportunities(pipelineProgresses);
|
||||
setIsBoardLoaded(true);
|
||||
},
|
||||
[setIsBoardLoaded, updateCompanyBoardCardIds],
|
||||
),
|
||||
});
|
||||
|
||||
useFindManyObjectRecords({
|
||||
skip: !opportunities.length,
|
||||
objectNamePlural: 'companiesV2',
|
||||
filter: {
|
||||
id: {
|
||||
in: opportunities.map((opportuntiy) => opportuntiy.companyId || ''),
|
||||
},
|
||||
},
|
||||
onCompleted: useCallback((data: PaginatedObjectTypeResults<Company>) => {
|
||||
setCompanies(data.edges.map((edge) => edge.node));
|
||||
}, []),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setAvailableFilterDefinitions(opportunitiesBoardOptions.filterDefinitions);
|
||||
@ -87,77 +130,32 @@ export const HooksCompanyBoardEffect = () => {
|
||||
setViewType?.(ViewType.Kanban);
|
||||
}, [setViewObjectMetadataId, setViewType]);
|
||||
|
||||
const pipelineStageIds = pipeline?.pipelineStages
|
||||
?.map((pipelineStage) => pipelineStage.id)
|
||||
.flat();
|
||||
|
||||
const whereFilters = useMemo(() => {
|
||||
return {
|
||||
AND: [
|
||||
{ pipelineStageId: { in: pipelineStageIds } },
|
||||
...(currentViewFilters?.map(turnFilterIntoWhereClause) || []),
|
||||
],
|
||||
};
|
||||
}, [currentViewFilters, pipelineStageIds]) as any;
|
||||
|
||||
const updateCompanyBoardCardIds = useUpdateCompanyBoardCardIds();
|
||||
|
||||
const { data: pipelineProgressData, loading: loadingGetPipelineProgress } =
|
||||
useGetPipelineProgressQuery({
|
||||
variables: {
|
||||
where: whereFilters,
|
||||
},
|
||||
onCompleted: (data) => {
|
||||
const pipelineProgresses = data?.findManyPipelineProgress || [];
|
||||
|
||||
updateCompanyBoardCardIds(pipelineProgresses);
|
||||
|
||||
setIsBoardLoaded(true);
|
||||
},
|
||||
});
|
||||
|
||||
const pipelineProgresses = useMemo(() => {
|
||||
return pipelineProgressData?.findManyPipelineProgress || [];
|
||||
}, [pipelineProgressData]);
|
||||
|
||||
const { data: companiesData, loading: loadingGetCompanies } =
|
||||
useGetCompaniesQuery({
|
||||
variables: {
|
||||
where: {
|
||||
id: {
|
||||
in: pipelineProgresses.map((item) => item.companyId || ''),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const loading =
|
||||
loadingGetPipelines || loadingGetPipelineProgress || loadingGetCompanies;
|
||||
const loading = !companies;
|
||||
|
||||
const { setActionBarEntries } = useBoardActionBarEntries();
|
||||
const { setContextMenuEntries } = useBoardContextMenuEntries();
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && pipeline && pipelineProgresses && companiesData) {
|
||||
if (!loading && opportunities && companies) {
|
||||
setActionBarEntries();
|
||||
setContextMenuEntries();
|
||||
setAvailableBoardCardFields(pipelineAvailableFieldDefinitions);
|
||||
updateCompanyBoard(pipeline, pipelineProgresses, companiesData.companies);
|
||||
setEntityCountInCurrentView(companiesData.companies.length);
|
||||
updateCompanyBoard(pipelineSteps, opportunities, companies);
|
||||
setEntityCountInCurrentView(companies.length);
|
||||
}
|
||||
}, [
|
||||
loading,
|
||||
pipeline,
|
||||
pipelineProgresses,
|
||||
companiesData,
|
||||
updateCompanyBoard,
|
||||
setActionBarEntries,
|
||||
setContextMenuEntries,
|
||||
searchParams,
|
||||
setEntityCountInCurrentView,
|
||||
setAvailableBoardCardFields,
|
||||
opportunities,
|
||||
companies,
|
||||
pipelineSteps,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -1,20 +1,15 @@
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { GET_PIPELINE_PROGRESS } from '@/pipeline/graphql/queries/getPipelineProgress';
|
||||
import { GET_PIPELINES } from '@/pipeline/graphql/queries/getPipelines';
|
||||
import { useCreateOneObjectRecord } from '@/object-record/hooks/useCreateOneObjectRecord';
|
||||
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { boardCardIdsByColumnIdFamilyState } from '@/ui/layout/board/states/boardCardIdsByColumnIdFamilyState';
|
||||
import { useCreateOneCompanyPipelineProgressMutation } from '~/generated/graphql';
|
||||
|
||||
export const useCreateCompanyProgress = () => {
|
||||
const [createOneCompanyPipelineProgress] =
|
||||
useCreateOneCompanyPipelineProgressMutation({
|
||||
refetchQueries: [
|
||||
getOperationName(GET_PIPELINE_PROGRESS) ?? '',
|
||||
getOperationName(GET_PIPELINES) ?? '',
|
||||
],
|
||||
const { createOneObject: createOneOpportunity } =
|
||||
useCreateOneObjectRecord<Opportunity>({
|
||||
objectNameSingular: 'opportunityV2',
|
||||
});
|
||||
|
||||
const [currentPipeline] = useRecoilState(currentPipelineState);
|
||||
@ -33,15 +28,11 @@ export const useCreateCompanyProgress = () => {
|
||||
newUuid,
|
||||
]);
|
||||
|
||||
await createOneCompanyPipelineProgress({
|
||||
variables: {
|
||||
uuid: newUuid,
|
||||
pipelineStageId: pipelineStageId,
|
||||
pipelineId: currentPipeline?.id ?? '',
|
||||
companyId: companyId,
|
||||
},
|
||||
await createOneOpportunity?.({
|
||||
pipelineStepId: pipelineStageId,
|
||||
companyId: companyId,
|
||||
});
|
||||
},
|
||||
[createOneCompanyPipelineProgress, currentPipeline],
|
||||
[createOneOpportunity, currentPipeline?.id],
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,15 +1,13 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { boardCardIdsByColumnIdFamilyState } from '@/ui/layout/board/states/boardCardIdsByColumnIdFamilyState';
|
||||
import { boardColumnsState } from '@/ui/layout/board/states/boardColumnsState';
|
||||
import { GetPipelineProgressQuery } from '~/generated/graphql';
|
||||
|
||||
export const useUpdateCompanyBoardCardIds = () =>
|
||||
useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(
|
||||
pipelineProgresses: GetPipelineProgressQuery['findManyPipelineProgress'],
|
||||
) => {
|
||||
(pipelineProgresses: Opportunity[]) => {
|
||||
const boardColumns = snapshot
|
||||
.getLoadable(boardColumnsState)
|
||||
.valueOrThrow();
|
||||
|
||||
@ -1,31 +1,26 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
|
||||
import { currentPipelineStepsState } from '@/pipeline/states/currentPipelineStepsState';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
import { boardCardIdsByColumnIdFamilyState } from '@/ui/layout/board/states/boardCardIdsByColumnIdFamilyState';
|
||||
import { boardColumnsState } from '@/ui/layout/board/states/boardColumnsState';
|
||||
import { savedBoardColumnsState } from '@/ui/layout/board/states/savedBoardColumnsState';
|
||||
import { BoardColumnDefinition } from '@/ui/layout/board/types/BoardColumnDefinition';
|
||||
import { entityFieldsFamilyState } from '@/ui/object/field/states/entityFieldsFamilyState';
|
||||
import { isThemeColor } from '@/ui/theme/utils/castStringAsThemeColor';
|
||||
import { Pipeline } from '~/generated/graphql';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { logError } from '~/utils/logError';
|
||||
|
||||
import { companyProgressesFamilyState } from '../states/companyProgressesFamilyState';
|
||||
import {
|
||||
CompanyForBoard,
|
||||
CompanyProgressDict,
|
||||
PipelineProgressForBoard,
|
||||
} from '../types/CompanyProgress';
|
||||
import { CompanyForBoard, CompanyProgressDict } from '../types/CompanyProgress';
|
||||
|
||||
export const useUpdateCompanyBoard = () =>
|
||||
useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(
|
||||
pipeline: Pipeline,
|
||||
pipelineProgresses: (PipelineProgressForBoard & {
|
||||
pipelineStageId: string;
|
||||
})[],
|
||||
pipelineSteps: PipelineStep[],
|
||||
pipelineProgresses: Opportunity[],
|
||||
companies: CompanyForBoard[],
|
||||
) => {
|
||||
const indexCompanyByIdReducer = (
|
||||
@ -44,7 +39,7 @@ export const useUpdateCompanyBoard = () =>
|
||||
|
||||
const indexPipelineProgressByIdReducer = (
|
||||
acc: CompanyProgressDict,
|
||||
pipelineProgress: PipelineProgressForBoard,
|
||||
pipelineProgress: Opportunity,
|
||||
) => {
|
||||
const company =
|
||||
pipelineProgress.companyId &&
|
||||
@ -77,21 +72,19 @@ export const useUpdateCompanyBoard = () =>
|
||||
}
|
||||
}
|
||||
|
||||
const currentPipeline = snapshot
|
||||
.getLoadable(currentPipelineState)
|
||||
const currentPipelineSteps = snapshot
|
||||
.getLoadable(currentPipelineStepsState)
|
||||
.valueOrThrow();
|
||||
|
||||
const currentBoardColumns = snapshot
|
||||
.getLoadable(boardColumnsState)
|
||||
.valueOrThrow();
|
||||
|
||||
if (!isDeeplyEqual(pipeline, currentPipeline)) {
|
||||
set(currentPipelineState, pipeline);
|
||||
if (!isDeeplyEqual(pipelineSteps, currentPipelineSteps)) {
|
||||
set(currentPipelineStepsState, currentPipelineSteps);
|
||||
}
|
||||
|
||||
const pipelineStages = pipeline?.pipelineStages ?? [];
|
||||
|
||||
const orderedPipelineStages = [...pipelineStages].sort((a, b) => {
|
||||
const orderedPipelineStages = [...pipelineSteps].sort((a, b) => {
|
||||
if (!a.position || !b.position) return 0;
|
||||
return a.position - b.position;
|
||||
});
|
||||
|
||||
@ -1,17 +1,8 @@
|
||||
import { Company, Person, PipelineProgress } from '~/generated/graphql';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { Company } from '~/generated/graphql';
|
||||
|
||||
export type CompanyForBoard = Pick<Company, 'id' | 'name' | 'domainName'>;
|
||||
export type PipelineProgressForBoard = Pick<
|
||||
PipelineProgress,
|
||||
| 'id'
|
||||
| 'amount'
|
||||
| 'closeDate'
|
||||
| 'companyId'
|
||||
| 'probability'
|
||||
| 'pointOfContactId'
|
||||
> & {
|
||||
pointOfContact?: Pick<Person, 'id' | 'displayName'> | null;
|
||||
};
|
||||
export type PipelineProgressForBoard = Opportunity;
|
||||
|
||||
export type CompanyProgress = {
|
||||
company: CompanyForBoard;
|
||||
|
||||
Reference in New Issue
Block a user