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:
bosiraphael
2023-11-17 19:12:22 +01:00
committed by GitHub
parent f62108d539
commit d481da183f
43 changed files with 454 additions and 864 deletions

View File

@ -10,6 +10,7 @@ import {
IconCheckbox,
IconSearch,
IconSettings,
IconTargetArrow,
} from '@/ui/display/icon/index';
import { useIsSubMenuNavbarDisplayed } from '@/ui/layout/hooks/useIsSubMenuNavbarDisplayed';
import MainNavbar from '@/ui/navigation/navbar/components/MainNavbar';
@ -56,6 +57,12 @@ export const AppNavbar = () => {
<Favorites />
<NavTitle label="Workspace" />
<ObjectMetadataNavItems />
<NavItem
label="Opportunities"
to="/opportunities"
active={currentPath === '/opportunities'}
Icon={IconTargetArrow}
/>
</MainNavbar>
) : (
<SettingsNavbar />

View File

@ -3534,76 +3534,6 @@ export type GetPersonPhoneByIdQueryVariables = Exact<{
export type GetPersonPhoneByIdQuery = { __typename?: 'Query', person: { __typename?: 'Person', id: string, phone?: string | null } };
export type CreateOneCompanyPipelineProgressMutationVariables = Exact<{
uuid: Scalars['String'];
companyId: Scalars['String'];
pipelineId: Scalars['String'];
pipelineStageId: Scalars['String'];
}>;
export type CreateOneCompanyPipelineProgressMutation = { __typename?: 'Mutation', createOnePipelineProgress: { __typename?: 'PipelineProgress', id: string } };
export type CreatePipelineStageMutationVariables = Exact<{
data: PipelineStageCreateInput;
}>;
export type CreatePipelineStageMutation = { __typename?: 'Mutation', pipelineStage: { __typename?: 'PipelineStage', id: string, name: string, color: string } };
export type DeleteManyPipelineProgressMutationVariables = Exact<{
ids?: InputMaybe<Array<Scalars['String']> | Scalars['String']>;
}>;
export type DeleteManyPipelineProgressMutation = { __typename?: 'Mutation', deleteManyPipelineProgress: { __typename?: 'AffectedRows', count: number } };
export type DeletePipelineStageMutationVariables = Exact<{
where: PipelineStageWhereUniqueInput;
}>;
export type DeletePipelineStageMutation = { __typename?: 'Mutation', pipelineStage: { __typename?: 'PipelineStage', id: string, name: string, color: string } };
export type UpdateOnePipelineProgressMutationVariables = Exact<{
data: PipelineProgressUpdateInput;
where: PipelineProgressWhereUniqueInput;
}>;
export type UpdateOnePipelineProgressMutation = { __typename?: 'Mutation', updateOnePipelineProgress?: { __typename?: 'PipelineProgress', id: string, amount?: number | null, closeDate?: string | null, probability?: number | null, pointOfContact?: { __typename?: 'Person', id: string } | null } | null };
export type UpdateOnePipelineProgressStageMutationVariables = Exact<{
id?: InputMaybe<Scalars['String']>;
pipelineStageId?: InputMaybe<Scalars['String']>;
}>;
export type UpdateOnePipelineProgressStageMutation = { __typename?: 'Mutation', updateOnePipelineProgress?: { __typename?: 'PipelineProgress', id: string } | null };
export type UpdatePipelineStageMutationVariables = Exact<{
id?: InputMaybe<Scalars['String']>;
data: PipelineStageUpdateInput;
}>;
export type UpdatePipelineStageMutation = { __typename?: 'Mutation', updateOnePipelineStage?: { __typename?: 'PipelineStage', id: string, name: string, color: string } | null };
export type GetPipelineProgressQueryVariables = Exact<{
where?: InputMaybe<PipelineProgressWhereInput>;
orderBy?: InputMaybe<Array<PipelineProgressOrderByWithRelationInput> | PipelineProgressOrderByWithRelationInput>;
}>;
export type GetPipelineProgressQuery = { __typename?: 'Query', findManyPipelineProgress: Array<{ __typename?: 'PipelineProgress', id: string, pipelineStageId: string, companyId?: string | null, personId?: string | null, amount?: number | null, closeDate?: string | null, pointOfContactId?: string | null, probability?: number | null, pointOfContact?: { __typename?: 'Person', id: string, firstName?: string | null, lastName?: string | null, displayName: string, avatarUrl?: string | null } | null }> };
export type GetPipelinesQueryVariables = Exact<{
where?: InputMaybe<PipelineWhereInput>;
}>;
export type GetPipelinesQuery = { __typename?: 'Query', findManyPipeline: Array<{ __typename?: 'Pipeline', id: string, name: string, pipelineProgressableType: PipelineProgressableType, pipelineStages?: Array<{ __typename?: 'PipelineStage', id: string, name: string, color: string, position?: number | null }> | null }> };
export type SearchActivityQueryVariables = Exact<{
where?: InputMaybe<ActivityWhereInput>;
limit?: InputMaybe<Scalars['Int']>;
@ -5128,353 +5058,6 @@ export function useGetPersonPhoneByIdLazyQuery(baseOptions?: Apollo.LazyQueryHoo
export type GetPersonPhoneByIdQueryHookResult = ReturnType<typeof useGetPersonPhoneByIdQuery>;
export type GetPersonPhoneByIdLazyQueryHookResult = ReturnType<typeof useGetPersonPhoneByIdLazyQuery>;
export type GetPersonPhoneByIdQueryResult = Apollo.QueryResult<GetPersonPhoneByIdQuery, GetPersonPhoneByIdQueryVariables>;
export const CreateOneCompanyPipelineProgressDocument = gql`
mutation CreateOneCompanyPipelineProgress($uuid: String!, $companyId: String!, $pipelineId: String!, $pipelineStageId: String!) {
createOnePipelineProgress(
data: {id: $uuid, company: {connect: {id: $companyId}}, pipeline: {connect: {id: $pipelineId}}, pipelineStage: {connect: {id: $pipelineStageId}}}
) {
id
}
}
`;
export type CreateOneCompanyPipelineProgressMutationFn = Apollo.MutationFunction<CreateOneCompanyPipelineProgressMutation, CreateOneCompanyPipelineProgressMutationVariables>;
/**
* __useCreateOneCompanyPipelineProgressMutation__
*
* To run a mutation, you first call `useCreateOneCompanyPipelineProgressMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useCreateOneCompanyPipelineProgressMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [createOneCompanyPipelineProgressMutation, { data, loading, error }] = useCreateOneCompanyPipelineProgressMutation({
* variables: {
* uuid: // value for 'uuid'
* companyId: // value for 'companyId'
* pipelineId: // value for 'pipelineId'
* pipelineStageId: // value for 'pipelineStageId'
* },
* });
*/
export function useCreateOneCompanyPipelineProgressMutation(baseOptions?: Apollo.MutationHookOptions<CreateOneCompanyPipelineProgressMutation, CreateOneCompanyPipelineProgressMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<CreateOneCompanyPipelineProgressMutation, CreateOneCompanyPipelineProgressMutationVariables>(CreateOneCompanyPipelineProgressDocument, options);
}
export type CreateOneCompanyPipelineProgressMutationHookResult = ReturnType<typeof useCreateOneCompanyPipelineProgressMutation>;
export type CreateOneCompanyPipelineProgressMutationResult = Apollo.MutationResult<CreateOneCompanyPipelineProgressMutation>;
export type CreateOneCompanyPipelineProgressMutationOptions = Apollo.BaseMutationOptions<CreateOneCompanyPipelineProgressMutation, CreateOneCompanyPipelineProgressMutationVariables>;
export const CreatePipelineStageDocument = gql`
mutation CreatePipelineStage($data: PipelineStageCreateInput!) {
pipelineStage: createOnePipelineStage(data: $data) {
id
name
color
}
}
`;
export type CreatePipelineStageMutationFn = Apollo.MutationFunction<CreatePipelineStageMutation, CreatePipelineStageMutationVariables>;
/**
* __useCreatePipelineStageMutation__
*
* To run a mutation, you first call `useCreatePipelineStageMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useCreatePipelineStageMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [createPipelineStageMutation, { data, loading, error }] = useCreatePipelineStageMutation({
* variables: {
* data: // value for 'data'
* },
* });
*/
export function useCreatePipelineStageMutation(baseOptions?: Apollo.MutationHookOptions<CreatePipelineStageMutation, CreatePipelineStageMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<CreatePipelineStageMutation, CreatePipelineStageMutationVariables>(CreatePipelineStageDocument, options);
}
export type CreatePipelineStageMutationHookResult = ReturnType<typeof useCreatePipelineStageMutation>;
export type CreatePipelineStageMutationResult = Apollo.MutationResult<CreatePipelineStageMutation>;
export type CreatePipelineStageMutationOptions = Apollo.BaseMutationOptions<CreatePipelineStageMutation, CreatePipelineStageMutationVariables>;
export const DeleteManyPipelineProgressDocument = gql`
mutation DeleteManyPipelineProgress($ids: [String!]) {
deleteManyPipelineProgress(where: {id: {in: $ids}}) {
count
}
}
`;
export type DeleteManyPipelineProgressMutationFn = Apollo.MutationFunction<DeleteManyPipelineProgressMutation, DeleteManyPipelineProgressMutationVariables>;
/**
* __useDeleteManyPipelineProgressMutation__
*
* To run a mutation, you first call `useDeleteManyPipelineProgressMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useDeleteManyPipelineProgressMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [deleteManyPipelineProgressMutation, { data, loading, error }] = useDeleteManyPipelineProgressMutation({
* variables: {
* ids: // value for 'ids'
* },
* });
*/
export function useDeleteManyPipelineProgressMutation(baseOptions?: Apollo.MutationHookOptions<DeleteManyPipelineProgressMutation, DeleteManyPipelineProgressMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<DeleteManyPipelineProgressMutation, DeleteManyPipelineProgressMutationVariables>(DeleteManyPipelineProgressDocument, options);
}
export type DeleteManyPipelineProgressMutationHookResult = ReturnType<typeof useDeleteManyPipelineProgressMutation>;
export type DeleteManyPipelineProgressMutationResult = Apollo.MutationResult<DeleteManyPipelineProgressMutation>;
export type DeleteManyPipelineProgressMutationOptions = Apollo.BaseMutationOptions<DeleteManyPipelineProgressMutation, DeleteManyPipelineProgressMutationVariables>;
export const DeletePipelineStageDocument = gql`
mutation DeletePipelineStage($where: PipelineStageWhereUniqueInput!) {
pipelineStage: deleteOnePipelineStage(where: $where) {
id
name
color
}
}
`;
export type DeletePipelineStageMutationFn = Apollo.MutationFunction<DeletePipelineStageMutation, DeletePipelineStageMutationVariables>;
/**
* __useDeletePipelineStageMutation__
*
* To run a mutation, you first call `useDeletePipelineStageMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useDeletePipelineStageMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [deletePipelineStageMutation, { data, loading, error }] = useDeletePipelineStageMutation({
* variables: {
* where: // value for 'where'
* },
* });
*/
export function useDeletePipelineStageMutation(baseOptions?: Apollo.MutationHookOptions<DeletePipelineStageMutation, DeletePipelineStageMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<DeletePipelineStageMutation, DeletePipelineStageMutationVariables>(DeletePipelineStageDocument, options);
}
export type DeletePipelineStageMutationHookResult = ReturnType<typeof useDeletePipelineStageMutation>;
export type DeletePipelineStageMutationResult = Apollo.MutationResult<DeletePipelineStageMutation>;
export type DeletePipelineStageMutationOptions = Apollo.BaseMutationOptions<DeletePipelineStageMutation, DeletePipelineStageMutationVariables>;
export const UpdateOnePipelineProgressDocument = gql`
mutation UpdateOnePipelineProgress($data: PipelineProgressUpdateInput!, $where: PipelineProgressWhereUniqueInput!) {
updateOnePipelineProgress(where: $where, data: $data) {
id
amount
closeDate
probability
pointOfContact {
id
}
}
}
`;
export type UpdateOnePipelineProgressMutationFn = Apollo.MutationFunction<UpdateOnePipelineProgressMutation, UpdateOnePipelineProgressMutationVariables>;
/**
* __useUpdateOnePipelineProgressMutation__
*
* To run a mutation, you first call `useUpdateOnePipelineProgressMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useUpdateOnePipelineProgressMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [updateOnePipelineProgressMutation, { data, loading, error }] = useUpdateOnePipelineProgressMutation({
* variables: {
* data: // value for 'data'
* where: // value for 'where'
* },
* });
*/
export function useUpdateOnePipelineProgressMutation(baseOptions?: Apollo.MutationHookOptions<UpdateOnePipelineProgressMutation, UpdateOnePipelineProgressMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<UpdateOnePipelineProgressMutation, UpdateOnePipelineProgressMutationVariables>(UpdateOnePipelineProgressDocument, options);
}
export type UpdateOnePipelineProgressMutationHookResult = ReturnType<typeof useUpdateOnePipelineProgressMutation>;
export type UpdateOnePipelineProgressMutationResult = Apollo.MutationResult<UpdateOnePipelineProgressMutation>;
export type UpdateOnePipelineProgressMutationOptions = Apollo.BaseMutationOptions<UpdateOnePipelineProgressMutation, UpdateOnePipelineProgressMutationVariables>;
export const UpdateOnePipelineProgressStageDocument = gql`
mutation UpdateOnePipelineProgressStage($id: String, $pipelineStageId: String) {
updateOnePipelineProgress(
where: {id: $id}
data: {pipelineStage: {connect: {id: $pipelineStageId}}}
) {
id
}
}
`;
export type UpdateOnePipelineProgressStageMutationFn = Apollo.MutationFunction<UpdateOnePipelineProgressStageMutation, UpdateOnePipelineProgressStageMutationVariables>;
/**
* __useUpdateOnePipelineProgressStageMutation__
*
* To run a mutation, you first call `useUpdateOnePipelineProgressStageMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useUpdateOnePipelineProgressStageMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [updateOnePipelineProgressStageMutation, { data, loading, error }] = useUpdateOnePipelineProgressStageMutation({
* variables: {
* id: // value for 'id'
* pipelineStageId: // value for 'pipelineStageId'
* },
* });
*/
export function useUpdateOnePipelineProgressStageMutation(baseOptions?: Apollo.MutationHookOptions<UpdateOnePipelineProgressStageMutation, UpdateOnePipelineProgressStageMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<UpdateOnePipelineProgressStageMutation, UpdateOnePipelineProgressStageMutationVariables>(UpdateOnePipelineProgressStageDocument, options);
}
export type UpdateOnePipelineProgressStageMutationHookResult = ReturnType<typeof useUpdateOnePipelineProgressStageMutation>;
export type UpdateOnePipelineProgressStageMutationResult = Apollo.MutationResult<UpdateOnePipelineProgressStageMutation>;
export type UpdateOnePipelineProgressStageMutationOptions = Apollo.BaseMutationOptions<UpdateOnePipelineProgressStageMutation, UpdateOnePipelineProgressStageMutationVariables>;
export const UpdatePipelineStageDocument = gql`
mutation UpdatePipelineStage($id: String, $data: PipelineStageUpdateInput!) {
updateOnePipelineStage(where: {id: $id}, data: $data) {
id
name
color
}
}
`;
export type UpdatePipelineStageMutationFn = Apollo.MutationFunction<UpdatePipelineStageMutation, UpdatePipelineStageMutationVariables>;
/**
* __useUpdatePipelineStageMutation__
*
* To run a mutation, you first call `useUpdatePipelineStageMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useUpdatePipelineStageMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [updatePipelineStageMutation, { data, loading, error }] = useUpdatePipelineStageMutation({
* variables: {
* id: // value for 'id'
* data: // value for 'data'
* },
* });
*/
export function useUpdatePipelineStageMutation(baseOptions?: Apollo.MutationHookOptions<UpdatePipelineStageMutation, UpdatePipelineStageMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<UpdatePipelineStageMutation, UpdatePipelineStageMutationVariables>(UpdatePipelineStageDocument, options);
}
export type UpdatePipelineStageMutationHookResult = ReturnType<typeof useUpdatePipelineStageMutation>;
export type UpdatePipelineStageMutationResult = Apollo.MutationResult<UpdatePipelineStageMutation>;
export type UpdatePipelineStageMutationOptions = Apollo.BaseMutationOptions<UpdatePipelineStageMutation, UpdatePipelineStageMutationVariables>;
export const GetPipelineProgressDocument = gql`
query GetPipelineProgress($where: PipelineProgressWhereInput, $orderBy: [PipelineProgressOrderByWithRelationInput!]) {
findManyPipelineProgress(where: $where, orderBy: $orderBy) {
id
pipelineStageId
companyId
personId
amount
closeDate
pointOfContactId
pointOfContact {
id
firstName
lastName
displayName
avatarUrl
}
probability
}
}
`;
/**
* __useGetPipelineProgressQuery__
*
* To run a query within a React component, call `useGetPipelineProgressQuery` and pass it any options that fit your needs.
* When your component renders, `useGetPipelineProgressQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetPipelineProgressQuery({
* variables: {
* where: // value for 'where'
* orderBy: // value for 'orderBy'
* },
* });
*/
export function useGetPipelineProgressQuery(baseOptions?: Apollo.QueryHookOptions<GetPipelineProgressQuery, GetPipelineProgressQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<GetPipelineProgressQuery, GetPipelineProgressQueryVariables>(GetPipelineProgressDocument, options);
}
export function useGetPipelineProgressLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetPipelineProgressQuery, GetPipelineProgressQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<GetPipelineProgressQuery, GetPipelineProgressQueryVariables>(GetPipelineProgressDocument, options);
}
export type GetPipelineProgressQueryHookResult = ReturnType<typeof useGetPipelineProgressQuery>;
export type GetPipelineProgressLazyQueryHookResult = ReturnType<typeof useGetPipelineProgressLazyQuery>;
export type GetPipelineProgressQueryResult = Apollo.QueryResult<GetPipelineProgressQuery, GetPipelineProgressQueryVariables>;
export const GetPipelinesDocument = gql`
query GetPipelines($where: PipelineWhereInput) {
findManyPipeline(where: $where) {
id
name
pipelineProgressableType
pipelineStages {
id
name
color
position
}
}
}
`;
/**
* __useGetPipelinesQuery__
*
* To run a query within a React component, call `useGetPipelinesQuery` and pass it any options that fit your needs.
* When your component renders, `useGetPipelinesQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useGetPipelinesQuery({
* variables: {
* where: // value for 'where'
* },
* });
*/
export function useGetPipelinesQuery(baseOptions?: Apollo.QueryHookOptions<GetPipelinesQuery, GetPipelinesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<GetPipelinesQuery, GetPipelinesQueryVariables>(GetPipelinesDocument, options);
}
export function useGetPipelinesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetPipelinesQuery, GetPipelinesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<GetPipelinesQuery, GetPipelinesQueryVariables>(GetPipelinesDocument, options);
}
export type GetPipelinesQueryHookResult = ReturnType<typeof useGetPipelinesQuery>;
export type GetPipelinesLazyQueryHookResult = ReturnType<typeof useGetPipelinesLazyQuery>;
export type GetPipelinesQueryResult = Apollo.QueryResult<GetPipelinesQuery, GetPipelinesQueryVariables>;
export const SearchActivityDocument = gql`
query SearchActivity($where: ActivityWhereInput, $limit: Int, $orderBy: [ActivityOrderByWithRelationInput!]) {
searchResults: findManyActivities(

View File

@ -20,7 +20,7 @@ export const useHandleCheckableActivityTargetChange = ({
objectNameSingular: 'activityTargetV2',
});
const { deleteOneObject } = useDeleteOneObjectRecord({
objectNamePlural: 'activityTargetV2',
objectNameSingular: 'activityTargetV2',
});
return async (

View File

@ -12,7 +12,7 @@ type ActivityActionBarProps = {
export const ActivityActionBar = ({ activityId }: ActivityActionBarProps) => {
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
const { deleteOneObject } = useDeleteOneObjectRecord({
objectNamePlural: 'activitiesV2',
objectNameSingular: 'activityV2',
});
const deleteActivity = () => {

View File

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

View File

@ -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(() => {

View File

@ -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],
);
};

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@ export const ObjectMetadataNavItems = () => {
return (
<>
{objectMetadataItems.map((objectMetadataItem) => {
if (objectMetadataItem.nameSingular === 'opportunityV2') return null;
return (
<NavItem
key={objectMetadataItem.id}

View File

@ -3,32 +3,37 @@ import { getOperationName } from '@apollo/client/utilities';
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
import { capitalize } from '~/utils/string/capitalize';
export const useDeleteOneObjectRecord = ({
objectNamePlural,
}: Pick<ObjectMetadataItemIdentifier, 'objectNamePlural'>) => {
export const useDeleteOneObjectRecord = <T>({
objectNameSingular,
}: Pick<ObjectMetadataItemIdentifier, 'objectNameSingular'>) => {
const {
foundObjectMetadataItem,
objectNotFoundInMetadata,
findManyQuery,
deleteOneMutation,
} = useFindOneObjectMetadataItem({
objectNamePlural,
objectNameSingular,
});
// TODO: type this with a minimal type at least with Record<string, any>
const [mutate] = useMutation(deleteOneMutation);
const deleteOneObject = foundObjectMetadataItem
? (idToDelete: string) => {
return mutate({
variables: {
idToDelete,
},
refetchQueries: [getOperationName(findManyQuery) ?? ''],
});
}
: undefined;
const deleteOneObject =
objectNameSingular && foundObjectMetadataItem
? async (idToDelete: string) => {
const deletedObject = await mutate({
variables: {
idToDelete,
},
refetchQueries: [getOperationName(findManyQuery) ?? ''],
});
return deletedObject.data[
`create${capitalize(objectNameSingular)}`
] as T;
}
: undefined;
return {
deleteOneObject,

View File

@ -3,6 +3,7 @@ import { isNonEmptyString } from '@sniptt/guards';
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
import { useFavorites } from '@/favorites/hooks/useFavorites';
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { useDeleteOneObjectRecord } from '@/object-record/hooks/useDeleteOneObjectRecord';
import {
IconCheckbox,
@ -28,6 +29,11 @@ export const useRecordTableContextMenuEntries = () => {
const { scopeId: objectNamePlural, resetTableRowSelection } =
useRecordTable();
const { data } = useGetFavoritesQuery();
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
objectNamePlural,
});
const { createFavorite, deleteFavorite, favorites } = useFavorites({
objectNamePlural,
});
@ -53,7 +59,7 @@ export const useRecordTableContextMenuEntries = () => {
});
const { deleteOneObject } = useDeleteOneObjectRecord({
objectNamePlural,
objectNameSingular: foundObjectMetadataItem?.nameSingular,
});
const handleDeleteClick = useRecoilCallback(({ snapshot }) => async () => {

View File

@ -1,21 +0,0 @@
import { gql } from '@apollo/client';
export const CREATE_COMPANY_PIPELINE_PROGRESS = gql`
mutation CreateOneCompanyPipelineProgress(
$uuid: String!
$companyId: String!
$pipelineId: String!
$pipelineStageId: String!
) {
createOnePipelineProgress(
data: {
id: $uuid
company: { connect: { id: $companyId } }
pipeline: { connect: { id: $pipelineId } }
pipelineStage: { connect: { id: $pipelineStageId } }
}
) {
id
}
}
`;

View File

@ -1,11 +0,0 @@
import { gql } from '@apollo/client';
export const CREATE_PIPELINE_STAGE = gql`
mutation CreatePipelineStage($data: PipelineStageCreateInput!) {
pipelineStage: createOnePipelineStage(data: $data) {
id
name
color
}
}
`;

View File

@ -1,9 +0,0 @@
import { gql } from '@apollo/client';
export const DELETE_PIPELINE_PROGRESS = gql`
mutation DeleteManyPipelineProgress($ids: [String!]) {
deleteManyPipelineProgress(where: { id: { in: $ids } }) {
count
}
}
`;

View File

@ -1,11 +0,0 @@
import { gql } from '@apollo/client';
export const DELETE_PIPELINE_STAGE = gql`
mutation DeletePipelineStage($where: PipelineStageWhereUniqueInput!) {
pipelineStage: deleteOnePipelineStage(where: $where) {
id
name
color
}
}
`;

View File

@ -1,18 +0,0 @@
import { gql } from '@apollo/client';
export const UPDATE_PIPELINE_PROGRESS = gql`
mutation UpdateOnePipelineProgress(
$data: PipelineProgressUpdateInput!
$where: PipelineProgressWhereUniqueInput!
) {
updateOnePipelineProgress(where: $where, data: $data) {
id
amount
closeDate
probability
pointOfContact {
id
}
}
}
`;

View File

@ -1,15 +0,0 @@
import { gql } from '@apollo/client';
export const UPDATE_PIPELINE_PROGRESS_STAGE = gql`
mutation UpdateOnePipelineProgressStage(
$id: String
$pipelineStageId: String
) {
updateOnePipelineProgress(
where: { id: $id }
data: { pipelineStage: { connect: { id: $pipelineStageId } } }
) {
id
}
}
`;

View File

@ -1,11 +0,0 @@
import { gql } from '@apollo/client';
export const UPDATE_PIPELINE_STAGE = gql`
mutation UpdatePipelineStage($id: String, $data: PipelineStageUpdateInput!) {
updateOnePipelineStage(where: { id: $id }, data: $data) {
id
name
color
}
}
`;

View File

@ -1,26 +0,0 @@
import { gql } from '@apollo/client';
export const GET_PIPELINE_PROGRESS = gql`
query GetPipelineProgress(
$where: PipelineProgressWhereInput
$orderBy: [PipelineProgressOrderByWithRelationInput!]
) {
findManyPipelineProgress(where: $where, orderBy: $orderBy) {
id
pipelineStageId
companyId
personId
amount
closeDate
pointOfContactId
pointOfContact {
id
firstName
lastName
displayName
avatarUrl
}
probability
}
}
`;

View File

@ -1,17 +0,0 @@
import { gql } from '@apollo/client';
export const GET_PIPELINES = gql`
query GetPipelines($where: PipelineWhereInput) {
findManyPipeline(where: $where) {
id
name
pipelineProgressableType
pipelineStages {
id
name
color
position
}
}
}
`;

View File

@ -1,25 +1,29 @@
import { getOperationName } from '@apollo/client/utilities';
import { useRecoilValue } from 'recoil';
import { useCreateOneObjectRecord } from '@/object-record/hooks/useCreateOneObjectRecord';
import { useDeleteOneObjectRecord } from '@/object-record/hooks/useDeleteOneObjectRecord';
import { PipelineStep } from '@/pipeline/types/PipelineStep';
import { BoardColumnDefinition } from '@/ui/layout/board/types/BoardColumnDefinition';
import {
useCreatePipelineStageMutation,
useDeletePipelineStageMutation,
} from '~/generated/graphql';
import { GET_PIPELINES } from '../graphql/queries/getPipelines';
import { currentPipelineState } from '../states/currentPipelineState';
export const usePipelineStages = () => {
const currentPipeline = useRecoilValue(currentPipelineState);
const [createPipelineStageMutation] = useCreatePipelineStageMutation();
const [deletePipelineStageMutation] = useDeletePipelineStageMutation();
const { createOneObject: createOnePipelineStep } =
useCreateOneObjectRecord<PipelineStep>({
objectNameSingular: 'pipelineStepV2',
});
const { deleteOneObject: deleteOnePipelineStep } =
useDeleteOneObjectRecord<PipelineStep>({
objectNameSingular: 'pipelineStepV2',
});
const handlePipelineStageAdd = async (boardColumn: BoardColumnDefinition) => {
if (!currentPipeline?.id) return;
return createPipelineStageMutation({
return createOnePipelineStep?.({
variables: {
data: {
color: boardColumn.colorCode ?? 'gray',
@ -30,17 +34,13 @@ export const usePipelineStages = () => {
type: 'ongoing',
},
},
refetchQueries: [getOperationName(GET_PIPELINES) ?? ''],
});
};
const handlePipelineStageDelete = async (boardColumnId: string) => {
if (!currentPipeline?.id) return;
return deletePipelineStageMutation({
variables: { where: { id: boardColumnId } },
refetchQueries: [getOperationName(GET_PIPELINES) ?? ''],
});
return deleteOnePipelineStep?.(boardColumnId);
};
return { handlePipelineStageAdd, handlePipelineStageDelete };

View File

@ -0,0 +1,8 @@
import { atom } from 'recoil';
import { PipelineStep } from '@/pipeline/types/PipelineStep';
export const currentPipelineStepsState = atom<PipelineStep[]>({
key: 'currentPipelineStepsState',
default: [],
});

View File

@ -0,0 +1,17 @@
import { PipelineStep } from '@/pipeline/types/PipelineStep';
import { Person } from '~/generated-metadata/graphql';
export type Opportunity = {
id: string;
amount: {
amountMicros: number;
currencyCode: string;
};
closeDate: Date;
probability: number;
pipelineStepId: string;
pipelineStep: PipelineStep;
pointOfContactId: string;
pointOfContact: Pick<Person, 'id' | 'firstName' | 'lastName' | 'avatarUrl'>;
[key: string]: any;
};

View File

@ -0,0 +1,6 @@
export type PipelineStep = {
id: string;
name: string;
color: string;
position: number;
};

View File

@ -1,10 +1,11 @@
import { useCallback, useRef } from 'react';
import { getOperationName } from '@apollo/client/utilities';
import { useApolloClient } from '@apollo/client';
import styled from '@emotion/styled';
import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
import { useRecoilValue } from 'recoil';
import { GET_PIPELINE_PROGRESS } from '@/pipeline/graphql/queries/getPipelineProgress';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { Opportunity } from '@/pipeline/types/Opportunity';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { StyledBoard } from '@/ui/layout/board/components/StyledBoard';
import { BoardColumnContext } from '@/ui/layout/board/contexts/BoardColumnContext';
@ -13,11 +14,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutsideByClassName } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import {
PipelineProgress,
PipelineStage,
useUpdateOnePipelineProgressStageMutation,
} from '~/generated/graphql';
import { PipelineProgress, PipelineStage } from '~/generated/graphql';
import { logError } from '~/utils/logError';
import { useCurrentCardSelected } from '../hooks/useCurrentCardSelected';
@ -59,8 +56,12 @@ export const EntityBoard = ({
const boardColumns = useRecoilValue(boardColumnsState);
const setCardSelected = useSetCardSelected();
const [updatePipelineProgressStage] =
useUpdateOnePipelineProgressStageMutation();
const { updateOneObject: updateOneOpportunity } =
useUpdateOneObjectRecord<Opportunity>({
objectNameSingular: 'opportunityV2',
});
const apolloClient = useApolloClient();
const { unselectAllActiveCards } = useCurrentCardSelected();
@ -69,33 +70,25 @@ export const EntityBoard = ({
pipelineProgressId: NonNullable<PipelineProgress['id']>,
pipelineStageId: NonNullable<PipelineStage['id']>,
) => {
updatePipelineProgressStage({
variables: {
await updateOneOpportunity?.({
idToUpdate: pipelineProgressId,
input: {
pipelineStepId: pipelineStageId,
},
});
const cache = apolloClient.cache;
cache.modify({
id: cache.identify({
id: pipelineProgressId,
pipelineStageId,
__typename: 'PipelineProgress',
}),
fields: {
pipelineStageId: () => pipelineStageId,
},
optimisticResponse: {
__typename: 'Mutation',
updateOnePipelineProgress: {
__typename: 'PipelineProgress',
id: pipelineProgressId,
},
},
update: (cache) => {
cache.modify({
id: cache.identify({
id: pipelineProgressId,
__typename: 'PipelineProgress',
}),
fields: {
pipelineStageId: () => pipelineStageId,
},
});
},
refetchQueries: [getOperationName(GET_PIPELINE_PROGRESS) ?? ''],
});
},
[updatePipelineProgressStage],
[apolloClient.cache, updateOneOpportunity],
);
useListenClickOutsideByClassName({

View File

@ -1,10 +1,9 @@
import { useSetRecoilState } from 'recoil';
import { IconTrash } from '@/ui/display/icon';
import { useDeleteSelectedBoardCards } from '@/ui/layout/board/hooks/useDeleteSelectedBoardCards';
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
import { useDeleteSelectedBoardCards } from './useDeleteSelectedBoardCards';
export const useBoardActionBarEntries = () => {
const setActionBarEntries = useSetRecoilState(actionBarEntriesState);

View File

@ -1,7 +1,8 @@
import { useRecoilState } from 'recoil';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { PipelineStep } from '@/pipeline/types/PipelineStep';
import { useMoveViewColumns } from '@/views/hooks/useMoveViewColumns';
import { useUpdatePipelineStageMutation } from '~/generated/graphql';
import { boardColumnsState } from '../states/boardColumnsState';
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
@ -11,19 +12,20 @@ export const useBoardColumns = () => {
const { handleColumnMove } = useMoveViewColumns();
const [updatePipelineStageMutation] = useUpdatePipelineStageMutation();
const { updateOneObject: updateOnePipelineStep } =
useUpdateOneObjectRecord<PipelineStep>({
objectNameSingular: 'pipelineStepV2',
});
const updatedPipelineStages = (stages: BoardColumnDefinition[]) => {
if (!stages.length) return;
return Promise.all(
stages.map((stage) =>
updatePipelineStageMutation({
variables: {
data: {
position: stage.position,
},
id: stage.id,
updateOnePipelineStep?.({
idToUpdate: stage.id,
input: {
position: stage.position,
},
}),
),

View File

@ -1,10 +1,9 @@
import { useSetRecoilState } from 'recoil';
import { IconTrash } from '@/ui/display/icon';
import { useDeleteSelectedBoardCards } from '@/ui/layout/board/hooks/useDeleteSelectedBoardCards';
import { contextMenuEntriesState } from '@/ui/navigation/context-menu/states/contextMenuEntriesState';
import { useDeleteSelectedBoardCards } from './useDeleteSelectedBoardCards';
export const useBoardContextMenuEntries = () => {
const setContextMenuEntries = useSetRecoilState(contextMenuEntriesState);

View File

@ -1,40 +1,41 @@
import { getOperationName } from '@apollo/client/utilities';
import { useRecoilValue } from 'recoil';
import { useApolloClient } from '@apollo/client';
import { useRecoilCallback } from 'recoil';
import { GET_PIPELINES } from '@/pipeline/graphql/queries/getPipelines';
import { useDeleteManyPipelineProgressMutation } from '~/generated/graphql';
import { useDeleteOneObjectRecord } from '@/object-record/hooks/useDeleteOneObjectRecord';
import { Opportunity } from '@/pipeline/types/Opportunity';
import { selectedCardIdsSelector } from '../states/selectors/selectedCardIdsSelector';
import { useRemoveCardIds } from './useRemoveCardIds';
export const useDeleteSelectedBoardCards = () => {
const selectedCardIds = useRecoilValue(selectedCardIdsSelector);
const removeCardIds = useRemoveCardIds();
const apolloClient = useApolloClient();
const [deletePipelineProgress] = useDeleteManyPipelineProgressMutation({
refetchQueries: [getOperationName(GET_PIPELINES) ?? ''],
});
const { deleteOneObject: deleteOneOpportunity } =
useDeleteOneObjectRecord<Opportunity>({
objectNameSingular: 'opportunityV2',
});
const deleteSelectedBoardCards = async () => {
await deletePipelineProgress({
variables: {
ids: selectedCardIds,
},
optimisticResponse: {
__typename: 'Mutation',
deleteManyPipelineProgress: {
count: selectedCardIds.length,
},
},
update: (cache) => {
const deleteSelectedBoardCards = useRecoilCallback(
({ snapshot }) =>
async () => {
const selectedCardIds = snapshot
.getLoadable(selectedCardIdsSelector)
.getValue();
await Promise.all(
selectedCardIds.map(async (id) => {
await deleteOneOpportunity?.(id);
}),
);
removeCardIds(selectedCardIds);
selectedCardIds.forEach((id) => {
cache.evict({ id: `PipelineProgress:${id}` });
apolloClient.cache.evict({ id: `Opportunity:${id}` });
});
},
});
};
[apolloClient.cache, deleteOneOpportunity, removeCardIds],
);
return deleteSelectedBoardCards;
};

View File

@ -1,16 +1,20 @@
import { useEffect } from 'react';
import styled from '@emotion/styled';
import { CompanyBoard } from '@/companies/board/components/CompanyBoard';
import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { PipelineAddButton } from '@/pipeline/components/PipelineAddButton';
import { usePipelineStages } from '@/pipeline/hooks/usePipelineStages';
import { PipelineStep } from '@/pipeline/types/PipelineStep';
import { IconTargetArrow } from '@/ui/display/icon';
import { BoardOptionsContext } from '@/ui/layout/board/contexts/BoardOptionsContext';
import { PageBody } from '@/ui/layout/page/PageBody';
import { PageContainer } from '@/ui/layout/page/PageContainer';
import { PageHeader } from '@/ui/layout/page/PageHeader';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useUpdatePipelineStageMutation } from '~/generated/graphql';
import { useView } from '@/views/hooks/useView';
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
const StyledBoardContainer = styled.div`
@ -22,30 +26,37 @@ export const Opportunities = () => {
const { handlePipelineStageAdd, handlePipelineStageDelete } =
usePipelineStages();
const [updatePipelineStage] = useUpdatePipelineStageMutation();
const { updateOneObject: updateOnePipelineStep } =
useUpdateOneObjectRecord<PipelineStep>({
objectNameSingular: 'pipelineStepV2',
});
const handleEditColumnTitle = (
boardColumnId: string,
newTitle: string,
newColor: string,
) => {
updatePipelineStage({
variables: {
id: boardColumnId,
data: { name: newTitle, color: newColor },
},
optimisticResponse: {
__typename: 'Mutation',
updateOnePipelineStage: {
__typename: 'PipelineStage',
id: boardColumnId,
name: newTitle,
color: newColor,
},
updateOnePipelineStep?.({
idToUpdate: boardColumnId,
input: {
name: newTitle,
color: newColor,
},
});
};
const opportunitiesV2MetadataId = useFindOneObjectMetadataItem({
objectNameSingular: 'opportunityV2',
}).foundObjectMetadataItem?.id;
const { setViewObjectMetadataId } = useView({
viewScopeId: 'company-board-view',
});
useEffect(() => {
setViewObjectMetadataId?.(opportunitiesV2MetadataId);
}, [opportunitiesV2MetadataId, setViewObjectMetadataId]);
return (
<PageContainer>
<RecoilScope>

View File

@ -39,8 +39,8 @@ export const SettingsWorkspaceMembers = () => {
objectNamePlural: 'workspaceMembersV2',
});
const { deleteOneObject: deleteOneWorkspaceMember } =
useDeleteOneObjectRecord({
objectNamePlural: 'workspaceMembersV2',
useDeleteOneObjectRecord<WorkspaceMember>({
objectNameSingular: 'workspaceMemberV2',
});
const currentWorkspace = useRecoilValue(currentWorkspaceState);

View File

@ -10,8 +10,6 @@ import { INSERT_ONE_PERSON } from '@/people/graphql/mutations/insertOnePerson';
import { UPDATE_ONE_PERSON } from '@/people/graphql/mutations/updateOnePerson';
import { GET_PEOPLE } from '@/people/graphql/queries/getPeople';
import { GET_PERSON } from '@/people/graphql/queries/getPerson';
import { GET_PIPELINE_PROGRESS } from '@/pipeline/graphql/queries/getPipelineProgress';
import { GET_PIPELINES } from '@/pipeline/graphql/queries/getPipelines';
import { SEARCH_ACTIVITY_QUERY } from '@/search/graphql/queries/searchActivityQuery';
import { SEARCH_COMPANY_QUERY } from '@/search/graphql/queries/searchCompanyQuery';
import { SEARCH_PEOPLE_QUERY } from '@/search/graphql/queries/searchPeopleQuery';
@ -37,8 +35,6 @@ import {
} from './mock-data/companies';
import { mockedObjectMetadataItems } from './mock-data/metadata';
import { mockedEmptyPersonData, mockedPeopleData } from './mock-data/people';
import { mockedPipelineProgressData } from './mock-data/pipeline-progress';
import { mockedPipelinesData } from './mock-data/pipelines';
import { mockedUsersData } from './mock-data/users';
import { mockedViewFieldsData } from './mock-data/view-fields';
import { mockedViewsData } from './mock-data/views';
@ -47,6 +43,7 @@ import {
filterAndSortData,
updateOneFromData,
} from './mock-data';
import { createEvent } from '@storybook/testing-library';
const metadataGraphql = graphql.link(
`${process.env.REACT_APP_SERVER_BASE_URL}/metadata`,
@ -205,23 +202,7 @@ export const graphqlMocks = [
);
},
),
graphql.query(getOperationName(GET_PIPELINES) ?? '', (req, res, ctx) => {
return res(
ctx.data({
findManyPipeline: mockedPipelinesData,
}),
);
}),
graphql.query(
getOperationName(GET_PIPELINE_PROGRESS) ?? '',
(req, res, ctx) => {
return res(
ctx.data({
findManyPipelineProgress: mockedPipelineProgressData,
}),
);
},
),
graphql.mutation(getOperationName(CREATE_EVENT) ?? '', (req, res, ctx) => {
return res(
ctx.data({

View File

@ -8,6 +8,8 @@ import { seedViewFields } from 'src/database/typeorm-seeds/workspace/view-fields
import { seedViews } from 'src/database/typeorm-seeds/workspace/views';
import { TypeORMService } from 'src/database/typeorm/typeorm.service';
import { seedMetadataSchema } from 'src/database/typeorm-seeds/metadata';
import { seedOpportunity } from 'src/database/typeorm-seeds/workspace/opportunity';
import { seedPipelineStep } from 'src/database/typeorm-seeds/workspace/pipeline-step';
import { seedWorkspaceMember } from 'src/database/typeorm-seeds/workspace/workspaceMember';
import { seedPeople } from 'src/database/typeorm-seeds/workspace/people';
@ -55,6 +57,8 @@ export class DataSeedWorkspaceCommand extends CommandRunner {
await seedCompanies(workspaceDataSource, dataSourceMetadata.schema);
await seedPeople(workspaceDataSource, dataSourceMetadata.schema);
await seedPipelineStep(workspaceDataSource, dataSourceMetadata.schema);
await seedOpportunity(workspaceDataSource, dataSourceMetadata.schema);
await seedViews(workspaceDataSource, dataSourceMetadata.schema);
await seedViewFields(workspaceDataSource, dataSourceMetadata.schema);

View File

@ -86,7 +86,7 @@ export const seedObjectMetadata = async (
dataSourceId: SeedDataSourceId,
workspaceId: SeedWorkspaceId,
isActive: true,
isSystem: true,
isSystem: false,
},
{
id: SeedObjectMetadataIds.PipelineStep,

View File

@ -0,0 +1,38 @@
import { DataSource } from 'typeorm';
const tableName = 'opportunity';
export const seedOpportunity = async (
workspaceDataSource: DataSource,
schemaName: string,
) => {
await workspaceDataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, [
'id',
'amountAmountMicros',
'amountCurrencyCode',
'closeDate',
'probability',
'pipelineStepId',
'pointOfContactId',
'personId',
'companyId',
])
.orIgnore()
.values([
{
id: '7c887ee3-be10-412b-a663-16bd3c2228e1',
amountAmountMicros: 100000,
amountCurrencyCode: 'USD',
closeDate: new Date(),
probability: 0.5,
pipelineStepId: '6edf4ead-006a-46e1-9c6d-228f1d0143c9',
pointOfContactId: '86083141-1c0e-494c-a1b6-85b1c6fefaa5',
personId: '86083141-1c0e-494c-a1b6-85b1c6fefaa5',
companyId: 'fe256b39-3ec3-4fe3-8997-b76aa0bfa408',
},
])
.execute();
};

View File

@ -0,0 +1,47 @@
import { DataSource } from 'typeorm';
const tableName = 'pipelineStep';
export const seedPipelineStep = async (
workspaceDataSource: DataSource,
schemaName: string,
) => {
await workspaceDataSource
.createQueryBuilder()
.insert()
.into(`${schemaName}.${tableName}`, ['id', 'name', 'color', 'position'])
.orIgnore()
.values([
{
id: '6edf4ead-006a-46e1-9c6d-228f1d0143c9',
name: 'New',
color: 'red',
position: 0,
},
{
id: 'd8361722-03fb-4e65-bd4f-ec9e52e5ec0a',
name: 'Screening',
color: 'purple',
position: 1,
},
{
id: '30b14887-d592-427d-bd97-6e670158db02',
name: 'Meeting',
color: 'sky',
position: 2,
},
{
id: 'db5a6648-d80d-4020-af64-4817ab4a12e8',
name: 'Proposal',
color: 'turquoise',
position: 3,
},
{
id: 'bea8bb7b-5467-48a6-9a8a-a8fa500123fe',
name: 'Customer',
color: 'yellow',
position: 4,
},
])
.execute();
};

View File

@ -1,7 +1,6 @@
import { DataSource } from 'typeorm';
import { SeedViewIds } from 'src/database/typeorm-seeds/workspace/views';
import { SeedCompanyFieldMetadataIds } from 'src/database/typeorm-seeds/metadata/field-metadata/company';
const tableName = 'viewField';
@ -23,130 +22,144 @@ export const seedViewFields = async (
.values([
{
fieldMetadataId: 'name',
viewId: SeedViewIds.PrismaCompany,
viewId: SeedViewIds.Company,
position: 0,
isVisible: true,
size: 180,
},
{
fieldMetadataId: 'domainName',
viewId: SeedViewIds.PrismaCompany,
viewId: SeedViewIds.Company,
position: 1,
isVisible: true,
size: 100,
},
{
fieldMetadataId: 'accountOwner',
viewId: SeedViewIds.PrismaCompany,
viewId: SeedViewIds.Company,
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId: 'createdAt',
viewId: SeedViewIds.PrismaCompany,
viewId: SeedViewIds.Company,
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId: 'employees',
viewId: SeedViewIds.PrismaCompany,
viewId: SeedViewIds.Company,
position: 4,
isVisible: true,
size: 150,
},
{
fieldMetadataId: 'linkedin',
viewId: SeedViewIds.PrismaCompany,
viewId: SeedViewIds.Company,
position: 5,
isVisible: true,
size: 170,
},
{
fieldMetadataId: 'address',
viewId: SeedViewIds.PrismaCompany,
viewId: SeedViewIds.Company,
position: 6,
isVisible: true,
size: 170,
},
{
fieldMetadataId: 'displayName',
viewId: SeedViewIds.PrismaPerson,
viewId: SeedViewIds.Person,
position: 0,
isVisible: true,
size: 210,
},
{
fieldMetadataId: 'email',
viewId: SeedViewIds.PrismaPerson,
viewId: SeedViewIds.Person,
position: 1,
isVisible: true,
size: 150,
},
{
fieldMetadataId: 'company',
viewId: SeedViewIds.PrismaPerson,
viewId: SeedViewIds.Person,
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId: 'phone',
viewId: SeedViewIds.PrismaPerson,
viewId: SeedViewIds.Person,
position: 3,
isVisible: true,
size: 150,
},
{
fieldMetadataId: 'createdAt',
viewId: SeedViewIds.PrismaPerson,
viewId: SeedViewIds.Person,
position: 4,
isVisible: true,
size: 150,
},
{
fieldMetadataId: 'city',
viewId: SeedViewIds.PrismaPerson,
viewId: SeedViewIds.Person,
position: 5,
isVisible: true,
size: 150,
},
{
fieldMetadataId: 'jobTitle',
viewId: SeedViewIds.PrismaPerson,
viewId: SeedViewIds.Person,
position: 6,
isVisible: true,
size: 150,
},
{
fieldMetadataId: 'linkedin',
viewId: SeedViewIds.PrismaPerson,
viewId: SeedViewIds.Person,
position: 7,
isVisible: true,
size: 150,
},
{
fieldMetadataId: 'x',
viewId: SeedViewIds.PrismaPerson,
viewId: SeedViewIds.Person,
position: 8,
isVisible: true,
size: 150,
},
{
fieldMetadataId: SeedCompanyFieldMetadataIds.Name,
viewId: SeedViewIds.Company,
fieldMetadataId: 'amount',
viewId: SeedViewIds.Opportunity,
position: 0,
isVisible: true,
size: 180,
size: 150,
},
{
fieldMetadataId: SeedCompanyFieldMetadataIds.DomainName,
viewId: SeedViewIds.Company,
fieldMetadataId: 'closeDate',
viewId: SeedViewIds.Opportunity,
position: 1,
isVisible: true,
size: 100,
size: 150,
},
{
fieldMetadataId: 'probability',
viewId: SeedViewIds.Opportunity,
position: 2,
isVisible: true,
size: 150,
},
{
fieldMetadataId: 'pointOfContact',
viewId: SeedViewIds.Opportunity,
position: 3,
isVisible: true,
size: 150,
},
])
.execute();

View File

@ -5,9 +5,6 @@ import { SeedObjectMetadataIds } from 'src/database/typeorm-seeds/metadata/objec
const tableName = 'view';
export const enum SeedViewIds {
PrismaCompany = '20202020-1ad3-4d1b-81f3-d7ecba29f8d3',
PrismaPerson = '20202020-9adf-4768-bd9c-27a82fa141f1',
PrismaOpportunity = '20202020-ffc7-4c81-bb41-d4baed4ed685',
Company = '20202020-2441-4424-8163-4002c523d415',
Person = '20202020-1979-447d-8115-593744eb4ead',
Opportunity = '20202020-b2b3-48a5-96ce-0936d6af21f7',
@ -28,24 +25,6 @@ export const seedViews = async (
])
.orIgnore()
.values([
{
id: SeedViewIds.PrismaCompany,
name: 'All companies',
objectMetadataId: 'company',
type: 'table',
},
{
id: SeedViewIds.PrismaPerson,
name: 'All people',
objectMetadataId: 'person',
type: 'table',
},
{
id: SeedViewIds.PrismaOpportunity,
name: 'All opportunities',
objectMetadataId: 'company',
type: 'kanban',
},
{
id: SeedViewIds.Company,
name: 'All Companies',

View File

@ -0,0 +1,33 @@
import { EntityManager } from 'typeorm';
export const opportunityPrefillData = async (
entityManager: EntityManager,
schemaName: string,
) => {
await entityManager
.createQueryBuilder()
.insert()
.into(`${schemaName}.opportunity`, [
'amount',
'closeDate',
'probability',
'pipelineStepId',
'pointOfContactId',
'personId',
'companyId',
])
.orIgnore()
.values([
{
amount: 100000,
closeDate: new Date(),
probability: 0.5,
pipelineStepId: '73ac09c6-2b90-4874-9e5d-553ea76912ee',
pointOfContactId: 'bb757987-ae38-4d16-96ec-b25b595e7bd8',
personId: 'a4a2b8e9-7a2b-4b6a-8c8b-7e9a0a0a0a0a',
companyId: 'fe256b39-3ec3-4fe3-8997-b76aa0bfa408',
},
])
.returning('*')
.execute();
};

View File

@ -5,6 +5,7 @@ import { viewPrefillData } from 'src/workspace/workspace-manager/standard-object
import { companyPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/company';
import { personPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/person';
import { pipelineStepPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/pipeline-step';
import { opportunityPrefillData } from 'src/workspace/workspace-manager/standard-objects-prefill-data/opportunity';
export const standardObjectsPrefillData = async (
workspaceDataSource: DataSource,
@ -27,5 +28,6 @@ export const standardObjectsPrefillData = async (
await personPrefillData(entityManager, schemaName);
await viewPrefillData(entityManager, schemaName, objectMetadataMap);
await pipelineStepPrefillData(entityManager, schemaName);
await opportunityPrefillData(entityManager, schemaName);
});
};

View File

@ -9,7 +9,7 @@ const opportunityMetadata = {
description: 'An opportunity',
icon: 'IconTargetArrow',
isActive: true,
isSystem: true,
isSystem: false,
fields: [
{
isCustom: false,