Fixed bug for refectch activities and create activity on the currently filtered user. (#1493)

* Fixed bug for refectch activities and create activity on the currently filtered user.

* Refactor optimistif effect

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2023-09-11 08:02:51 +02:00
committed by GitHub
parent 08727aafe5
commit 9be069bedc
37 changed files with 363 additions and 243 deletions

View File

@ -3196,7 +3196,7 @@ export type GetFavoritesQueryVariables = Exact<{ [key: string]: never; }>;
export type GetFavoritesQuery = { __typename?: 'Query', findFavorites: Array<{ __typename?: 'Favorite', id: string, person?: { __typename?: 'Person', id: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null } | null, company?: { __typename?: 'Company', id: string, name: string, domainName: string, accountOwner?: { __typename?: 'User', id: string, displayName: string, avatarUrl?: string | null } | null } | null }> }; export type GetFavoritesQuery = { __typename?: 'Query', findFavorites: Array<{ __typename?: 'Favorite', id: string, person?: { __typename?: 'Person', id: string, firstName?: string | null, lastName?: string | null, avatarUrl?: string | null } | null, company?: { __typename?: 'Company', id: string, name: string, domainName: string, accountOwner?: { __typename?: 'User', id: string, displayName: string, avatarUrl?: string | null } | null } | null }> };
export type InsertPersonFragmentFragment = { __typename?: 'Person', id: string, firstName?: string | null, lastName?: string | null, displayName: string, createdAt: string }; export type PersonFieldsFragmentFragment = { __typename?: 'Person', id: string, phone?: string | null, email?: string | null, city?: string | null, firstName?: string | null, lastName?: string | null, displayName: string, jobTitle?: string | null, linkedinUrl?: string | null, xUrl?: string | null, avatarUrl?: string | null, createdAt: string, _activityCount: number, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null };
export type DeleteManyPersonMutationVariables = Exact<{ export type DeleteManyPersonMutationVariables = Exact<{
ids?: InputMaybe<Array<Scalars['String']> | Scalars['String']>; ids?: InputMaybe<Array<Scalars['String']> | Scalars['String']>;
@ -3217,7 +3217,7 @@ export type InsertOnePersonMutationVariables = Exact<{
}>; }>;
export type InsertOnePersonMutation = { __typename?: 'Mutation', createOnePerson: { __typename?: 'Person', id: string, firstName?: string | null, lastName?: string | null, displayName: string, createdAt: string } }; export type InsertOnePersonMutation = { __typename?: 'Mutation', createOnePerson: { __typename?: 'Person', id: string, phone?: string | null, email?: string | null, city?: string | null, firstName?: string | null, lastName?: string | null, displayName: string, jobTitle?: string | null, linkedinUrl?: string | null, xUrl?: string | null, avatarUrl?: string | null, createdAt: string, _activityCount: number, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null } };
export type RemovePersonPictureMutationVariables = Exact<{ export type RemovePersonPictureMutationVariables = Exact<{
where: PersonWhereUniqueInput; where: PersonWhereUniqueInput;
@ -3707,7 +3707,7 @@ export const UserQueryFragmentFragmentDoc = gql`
} }
`; `;
export const CompanyFieldsFragmentFragmentDoc = gql` export const CompanyFieldsFragmentFragmentDoc = gql`
fragment CompanyFieldsFragment on Company { fragment companyFieldsFragment on Company {
accountOwner { accountOwner {
id id
email email
@ -3726,13 +3726,26 @@ export const CompanyFieldsFragmentFragmentDoc = gql`
name name
} }
`; `;
export const InsertPersonFragmentFragmentDoc = gql` export const PersonFieldsFragmentFragmentDoc = gql`
fragment InsertPersonFragment on Person { fragment personFieldsFragment on Person {
id id
phone
email
city
firstName firstName
lastName lastName
displayName displayName
jobTitle
linkedinUrl
xUrl
avatarUrl
createdAt createdAt
_activityCount
company {
id
name
domainName
}
} }
`; `;
export const AddActivityTargetsOnActivityDocument = gql` export const AddActivityTargetsOnActivityDocument = gql`
@ -4535,7 +4548,7 @@ export type InsertManyCompanyMutationOptions = Apollo.BaseMutationOptions<Insert
export const InsertOneCompanyDocument = gql` export const InsertOneCompanyDocument = gql`
mutation InsertOneCompany($data: CompanyCreateInput!) { mutation InsertOneCompany($data: CompanyCreateInput!) {
createOneCompany(data: $data) { createOneCompany(data: $data) {
...CompanyFieldsFragment ...companyFieldsFragment
} }
} }
${CompanyFieldsFragmentFragmentDoc}`; ${CompanyFieldsFragmentFragmentDoc}`;
@ -4568,7 +4581,7 @@ export type InsertOneCompanyMutationOptions = Apollo.BaseMutationOptions<InsertO
export const UpdateOneCompanyDocument = gql` export const UpdateOneCompanyDocument = gql`
mutation UpdateOneCompany($where: CompanyWhereUniqueInput!, $data: CompanyUpdateInput!) { mutation UpdateOneCompany($where: CompanyWhereUniqueInput!, $data: CompanyUpdateInput!) {
updateOneCompany(data: $data, where: $where) { updateOneCompany(data: $data, where: $where) {
...CompanyFieldsFragment ...companyFieldsFragment
} }
} }
${CompanyFieldsFragmentFragmentDoc}`; ${CompanyFieldsFragmentFragmentDoc}`;
@ -4942,10 +4955,10 @@ export type InsertManyPersonMutationOptions = Apollo.BaseMutationOptions<InsertM
export const InsertOnePersonDocument = gql` export const InsertOnePersonDocument = gql`
mutation InsertOnePerson($data: PersonCreateInput!) { mutation InsertOnePerson($data: PersonCreateInput!) {
createOnePerson(data: $data) { createOnePerson(data: $data) {
...InsertPersonFragment ...personFieldsFragment
} }
} }
${InsertPersonFragmentFragmentDoc}`; ${PersonFieldsFragmentFragmentDoc}`;
export type InsertOnePersonMutationFn = Apollo.MutationFunction<InsertOnePersonMutation, InsertOnePersonMutationVariables>; export type InsertOnePersonMutationFn = Apollo.MutationFunction<InsertOnePersonMutation, InsertOnePersonMutationVariables>;
/** /**
@ -5855,7 +5868,7 @@ export type SearchActivityQueryResult = Apollo.QueryResult<SearchActivityQuery,
export const SearchCompanyDocument = gql` export const SearchCompanyDocument = gql`
query SearchCompany($where: CompanyWhereInput, $limit: Int, $orderBy: [CompanyOrderByWithRelationInput!]) { query SearchCompany($where: CompanyWhereInput, $limit: Int, $orderBy: [CompanyOrderByWithRelationInput!]) {
searchResults: findManyCompany(where: $where, take: $limit, orderBy: $orderBy) { searchResults: findManyCompany(where: $where, take: $limit, orderBy: $orderBy) {
...CompanyFieldsFragment ...companyFieldsFragment
} }
} }
${CompanyFieldsFragmentFragmentDoc}`; ${CompanyFieldsFragmentFragmentDoc}`;

View File

@ -1,4 +1,5 @@
import { useApolloClient } from '@apollo/client'; import { useApolloClient } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery'; import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect'; import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
@ -14,6 +15,7 @@ import {
} from '~/generated/graphql'; } from '~/generated/graphql';
import { ACTIVITY_UPDATE_FRAGMENT } from '../graphql/fragments/activityUpdateFragment'; import { ACTIVITY_UPDATE_FRAGMENT } from '../graphql/fragments/activityUpdateFragment';
import { GET_ACTIVITIES } from '../graphql/queries/getActivities';
export type OwnProps = { export type OwnProps = {
activity: Pick<Activity, 'id'> & { activity: Pick<Activity, 'id'> & {
@ -86,6 +88,7 @@ export function ActivityAssigneePicker({
}, },
}, },
}, },
refetchQueries: [getOperationName(GET_ACTIVITIES) ?? ''],
}); });
} }

View File

@ -119,6 +119,7 @@ export function ActivityEditor({
title: newTitle, title: newTitle,
}, },
}, },
refetchQueries: [getOperationName(GET_ACTIVITIES) ?? ''],
}); });
}, },
[activity.id, cachedActivity, updateActivityMutation], [activity.id, cachedActivity, updateActivityMutation],

View File

@ -30,10 +30,15 @@ export function useOpenCreateActivityDrawer() {
); );
const [, setViewableActivityId] = useRecoilState(viewableActivityIdState); const [, setViewableActivityId] = useRecoilState(viewableActivityIdState);
return function openCreateActivityDrawer( return function openCreateActivityDrawer({
type: ActivityType, type,
entities?: ActivityTargetableEntity[], targetableEntities,
) { assigneeId,
}: {
type: ActivityType;
targetableEntities?: ActivityTargetableEntity[];
assigneeId?: string;
}) {
const now = new Date().toISOString(); const now = new Date().toISOString();
return createActivityMutation({ return createActivityMutation({
@ -43,11 +48,13 @@ export function useOpenCreateActivityDrawer() {
createdAt: now, createdAt: now,
updatedAt: now, updatedAt: now,
author: { connect: { id: currentUser?.id ?? '' } }, author: { connect: { id: currentUser?.id ?? '' } },
assignee: { connect: { id: currentUser?.id ?? '' } }, assignee: { connect: { id: assigneeId ?? currentUser?.id ?? '' } },
type: type, type: type,
activityTargets: { activityTargets: {
createMany: { createMany: {
data: entities ? getRelationData(entities) : [], data: targetableEntities
? getRelationData(targetableEntities)
: [],
skipDuplicates: true, skipDuplicates: true,
}, },
}, },
@ -63,7 +70,7 @@ export function useOpenCreateActivityDrawer() {
onCompleted(data) { onCompleted(data) {
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false }); setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
setViewableActivityId(data.createOneActivity.id); setViewableActivityId(data.createOneActivity.id);
setActivityTargetableEntityArray(entities ?? []); setActivityTargetableEntityArray(targetableEntities ?? []);
openRightDrawer(RightDrawerPages.CreateActivity); openRightDrawer(RightDrawerPages.CreateActivity);
}, },
}); });

View File

@ -24,6 +24,9 @@ export function useOpenCreateActivityDrawerForSelectedRowIds() {
type: entityType, type: entityType,
id, id,
})); }));
openCreateActivityDrawer(type, activityTargetableEntityArray); openCreateActivityDrawer({
type,
targetableEntities: activityTargetableEntityArray,
});
}; };
} }

View File

@ -59,7 +59,12 @@ export function Notes({ entity }: { entity: ActivityTargetableEntity }) {
Icon={IconNotes} Icon={IconNotes}
title="New note" title="New note"
variant="secondary" variant="secondary"
onClick={() => openCreateActivity(ActivityType.Note, [entity])} onClick={() =>
openCreateActivity({
type: ActivityType.Note,
targetableEntities: [entity],
})
}
/> />
</StyledTaskGroupEmptyContainer> </StyledTaskGroupEmptyContainer>
); );
@ -76,7 +81,12 @@ export function Notes({ entity }: { entity: ActivityTargetableEntity }) {
size="small" size="small"
variant="secondary" variant="secondary"
title="Add note" title="Add note"
onClick={() => openCreateActivity(ActivityType.Note, [entity])} onClick={() =>
openCreateActivity({
type: ActivityType.Note,
targetableEntities: [entity],
})
}
></Button> ></Button>
} }
/> />

View File

@ -5,13 +5,13 @@ import { IconPlus } from '@/ui/icon';
import { ActivityType } from '~/generated/graphql'; import { ActivityType } from '~/generated/graphql';
export function AddTaskButton({ export function AddTaskButton({
entity, activityTargetEntity,
}: { }: {
entity?: ActivityTargetableEntity; activityTargetEntity?: ActivityTargetableEntity;
}) { }) {
const openCreateActivity = useOpenCreateActivityDrawer(); const openCreateActivity = useOpenCreateActivityDrawer();
if (!entity) { if (!activityTargetEntity) {
return <></>; return <></>;
} }
@ -21,7 +21,12 @@ export function AddTaskButton({
size="small" size="small"
variant="secondary" variant="secondary"
title="Add task" title="Add task"
onClick={() => openCreateActivity(ActivityType.Task, [entity])} onClick={() =>
openCreateActivity({
type: ActivityType.Task,
targetableEntities: [activityTargetEntity],
})
}
></Button> ></Button>
); );
} }

View File

@ -0,0 +1,34 @@
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { PageAddButton } from '@/ui/layout/components/PageAddButton';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
import { ActivityType } from '~/generated/graphql';
export function PageAddTaskButton() {
const openCreateActivity = useOpenCreateActivityDrawer();
const filters = useRecoilScopedValue(
filtersScopedState,
TasksRecoilScopeContext,
);
const assigneeIdFilter = filters.find(
(filter) => filter.key === 'assigneeId',
);
function handleClick() {
openCreateActivity({
type: ActivityType.Task,
assigneeId: assigneeIdFilter?.value,
});
}
return (
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
<PageAddButton onClick={handleClick} />
</RecoilScope>
);
}

View File

@ -83,7 +83,10 @@ export function TaskGroups({ entity, showAddButton }: OwnProps) {
title="New task" title="New task"
variant={'secondary'} variant={'secondary'}
onClick={() => onClick={() =>
openCreateActivity(ActivityType.Task, entity ? [entity] : undefined) openCreateActivity({
type: ActivityType.Task,
targetableEntities: entity ? [entity] : undefined,
})
} }
/> />
</StyledTaskGroupEmptyContainer> </StyledTaskGroupEmptyContainer>
@ -95,21 +98,27 @@ export function TaskGroups({ entity, showAddButton }: OwnProps) {
{activeTabId === 'done' ? ( {activeTabId === 'done' ? (
<TaskList <TaskList
tasks={completedTasks ?? []} tasks={completedTasks ?? []}
button={showAddButton && <AddTaskButton entity={entity} />} button={
showAddButton && <AddTaskButton activityTargetEntity={entity} />
}
/> />
) : ( ) : (
<> <>
<TaskList <TaskList
title="Today" title="Today"
tasks={todayOrPreviousTasks ?? []} tasks={todayOrPreviousTasks ?? []}
button={showAddButton && <AddTaskButton entity={entity} />} button={
showAddButton && <AddTaskButton activityTargetEntity={entity} />
}
/> />
<TaskList <TaskList
title="Upcoming" title="Upcoming"
tasks={upcomingTasks ?? []} tasks={upcomingTasks ?? []}
button={ button={
showAddButton && showAddButton &&
!todayOrPreviousTasks?.length && <AddTaskButton entity={entity} /> !todayOrPreviousTasks?.length && (
<AddTaskButton activityTargetEntity={entity} />
)
} }
/> />
<TaskList <TaskList
@ -118,7 +127,9 @@ export function TaskGroups({ entity, showAddButton }: OwnProps) {
button={ button={
showAddButton && showAddButton &&
!todayOrPreviousTasks?.length && !todayOrPreviousTasks?.length &&
!upcomingTasks?.length && <AddTaskButton entity={entity} /> !upcomingTasks?.length && (
<AddTaskButton activityTargetEntity={entity} />
)
} }
/> />
</> </>

View File

@ -77,8 +77,18 @@ export function Timeline({ entity }: { entity: ActivityTargetableEntity }) {
<StyledEmptyTimelineTitle>No activity yet</StyledEmptyTimelineTitle> <StyledEmptyTimelineTitle>No activity yet</StyledEmptyTimelineTitle>
<StyledEmptyTimelineSubTitle>Create one:</StyledEmptyTimelineSubTitle> <StyledEmptyTimelineSubTitle>Create one:</StyledEmptyTimelineSubTitle>
<ActivityCreateButton <ActivityCreateButton
onNoteClick={() => openCreateActivity(ActivityType.Note, [entity])} onNoteClick={() =>
onTaskClick={() => openCreateActivity(ActivityType.Task, [entity])} openCreateActivity({
type: ActivityType.Note,
targetableEntities: [entity],
})
}
onTaskClick={() =>
openCreateActivity({
type: ActivityType.Task,
targetableEntities: [entity],
})
}
/> />
</StyledTimelineEmptyContainer> </StyledTimelineEmptyContainer>
); );

View File

@ -1,39 +1,111 @@
import { useApolloClient } from '@apollo/client'; import {
ApolloCache,
DocumentNode,
OperationVariables,
useApolloClient,
} from '@apollo/client';
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { GET_COMPANIES } from '@/companies/graphql/queries/getCompanies';
import { GET_PEOPLE } from '@/people/graphql/queries/getPeople';
import { GetCompaniesQuery, GetPeopleQuery } from '~/generated/graphql';
import { optimisticEffectState } from '../states/optimisticEffectState'; import { optimisticEffectState } from '../states/optimisticEffectState';
import { OptimisticEffect } from '../types/OptimisticEffect'; import { OptimisticEffectDefinition } from '../types/OptimisticEffectDefinition';
export function useOptimisticEffect() { export function useOptimisticEffect() {
const apolloClient = useApolloClient(); const apolloClient = useApolloClient();
const registerOptimisticEffect = useRecoilCallback( const registerOptimisticEffect = useRecoilCallback(
({ snapshot, set }) => ({ snapshot, set }) =>
(optimisticEffect: OptimisticEffect<unknown, unknown>) => { ({
const { key } = optimisticEffect; variables,
definition,
}: {
variables: OperationVariables;
definition: OptimisticEffectDefinition<unknown>;
}) => {
const optimisticEffects = snapshot const optimisticEffects = snapshot
.getLoadable(optimisticEffectState) .getLoadable(optimisticEffectState)
.getValue(); .getValue();
function optimisticEffectWriter({
cache,
newData,
query,
variables,
}: {
cache: ApolloCache<unknown>;
newData: unknown[];
variables: OperationVariables;
query: DocumentNode;
}) {
const existingData = cache.readQuery({
query,
variables,
});
if (!existingData) {
return;
}
if (query === GET_PEOPLE) {
cache.writeQuery({
query,
variables,
data: {
people: definition.resolver({
currentData: (existingData as GetPeopleQuery).people,
newData,
variables,
}),
},
});
}
if (query === GET_COMPANIES) {
cache.writeQuery({
query,
variables,
data: {
companies: definition.resolver({
currentData: (existingData as GetCompaniesQuery).companies,
newData,
variables,
}),
},
});
}
}
const optimisticEffect = {
key: definition.key,
variables,
typename: definition.typename,
query: definition.query,
writer: optimisticEffectWriter,
};
set(optimisticEffectState, { set(optimisticEffectState, {
...optimisticEffects, ...optimisticEffects,
[key]: optimisticEffect, [definition.key]: optimisticEffect,
}); });
}, },
); );
const triggerOptimisticEffects = useRecoilCallback( const triggerOptimisticEffects = useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>
(typename: string, entities: any[]) => { (typename: string, newData: any[]) => {
const optimisticEffects = snapshot const optimisticEffects = snapshot
.getLoadable(optimisticEffectState) .getLoadable(optimisticEffectState)
.getValue(); .getValue();
Object.values(optimisticEffects).forEach((optimisticEffect) => { Object.values(optimisticEffects).forEach((optimisticEffect) => {
if (optimisticEffect.typename === typename) { if (optimisticEffect.typename === typename) {
optimisticEffect.resolver({ optimisticEffect.writer({
cache: apolloClient.cache, cache: apolloClient.cache,
entities, query: optimisticEffect.query,
newData,
variables: optimisticEffect.variables, variables: optimisticEffect.variables,
}); });
} }

View File

@ -1,9 +1,9 @@
import { atom } from 'recoil'; import { atom } from 'recoil';
import { OptimisticEffect } from '../types/OptimisticEffect'; import { OptimisticEffect } from '../types/internal/OptimisticEffect';
export const optimisticEffectState = atom< export const optimisticEffectState = atom<
Record<string, OptimisticEffect<unknown, unknown>> Record<string, OptimisticEffect<unknown>>
>({ >({
key: 'optimisticEffectState', key: 'optimisticEffectState',
default: {}, default: {},

View File

@ -1,18 +0,0 @@
import { ApolloCache } from '@apollo/client';
type OptimisticEffectResolver<T, QueryVariables> = ({
cache,
entities,
variables,
}: {
cache: ApolloCache<T>;
entities: T[];
variables: QueryVariables;
}) => void;
export type OptimisticEffect<T, QueryVariables> = {
key: string;
typename: string;
variables: QueryVariables;
resolver: OptimisticEffectResolver<T, QueryVariables>;
};

View File

@ -0,0 +1,10 @@
import { DocumentNode } from 'graphql';
import { OptimisticEffectResolver } from './OptimisticEffectResolver';
export type OptimisticEffectDefinition<T> = {
key: string;
query: DocumentNode;
typename: string;
resolver: OptimisticEffectResolver<T>;
};

View File

@ -0,0 +1,11 @@
import { OperationVariables } from '@apollo/client';
export type OptimisticEffectResolver<T> = ({
currentData,
newData,
variables,
}: {
currentData: T[];
newData: T[];
variables: OperationVariables;
}) => void;

View File

@ -0,0 +1,21 @@
import { ApolloCache, DocumentNode, OperationVariables } from '@apollo/client';
type OptimisticEffectWriter<T> = ({
cache,
newData,
variables,
query,
}: {
cache: ApolloCache<T>;
query: DocumentNode;
newData: T[];
variables: OperationVariables;
}) => void;
export type OptimisticEffect<T> = {
key: string;
query: DocumentNode;
typename: string;
variables: OperationVariables;
writer: OptimisticEffectWriter<T>;
};

View File

@ -1,7 +1,7 @@
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
export const COMPANY_FIELDS_FRAGMENT = gql` export const COMPANY_FIELDS_FRAGMENT = gql`
fragment CompanyFieldsFragment on Company { fragment companyFieldsFragment on Company {
accountOwner { accountOwner {
id id
email email
@ -18,5 +18,6 @@ export const COMPANY_FIELDS_FRAGMENT = gql`
idealCustomerProfile idealCustomerProfile
id id
name name
_activityCount
} }
`; `;

View File

@ -3,7 +3,7 @@ import { gql } from '@apollo/client';
export const INSERT_ONE_COMPANY = gql` export const INSERT_ONE_COMPANY = gql`
mutation InsertOneCompany($data: CompanyCreateInput!) { mutation InsertOneCompany($data: CompanyCreateInput!) {
createOneCompany(data: $data) { createOneCompany(data: $data) {
...CompanyFieldsFragment ...companyFieldsFragment
} }
} }
`; `;

View File

@ -6,7 +6,7 @@ export const UPDATE_ONE_COMPANY = gql`
$data: CompanyUpdateInput! $data: CompanyUpdateInput!
) { ) {
updateOneCompany(data: $data, where: $where) { updateOneCompany(data: $data, where: $where) {
...CompanyFieldsFragment ...companyFieldsFragment
} }
} }
`; `;

View File

@ -0,0 +1,18 @@
import { Company } from '~/generated/graphql';
import { GET_COMPANIES } from '../queries/getCompanies';
export const getCompaniesOptimisticEffectDefinition = {
key: 'generic-entity-table-data-companies',
typename: 'Company',
query: GET_COMPANIES,
resolver: ({
currentData,
newData,
}: {
currentData: Company[];
newData: Company[];
}) => {
return [...newData, ...currentData];
},
};

View File

@ -1,47 +0,0 @@
import { ApolloCache } from '@apollo/client';
import {
Company,
GetCompaniesQuery,
GetCompaniesQueryVariables,
} from '~/generated/graphql';
import { GET_COMPANIES } from '../queries/getCompanies';
function optimisticEffectResolver({
cache,
entities,
variables,
}: {
cache: ApolloCache<Company>;
entities: Company[];
variables: GetCompaniesQueryVariables;
}) {
const existingData = cache.readQuery<GetCompaniesQuery>({
query: GET_COMPANIES,
variables: { orderBy: variables.orderBy, where: variables.where },
});
if (!existingData) {
return;
}
cache.writeQuery({
query: GET_COMPANIES,
variables: { orderBy: variables.orderBy, where: variables.where },
data: {
companies: [...entities, ...existingData.companies],
},
});
}
export function getCompaniesOptimisticEffect(
variables: GetCompaniesQueryVariables,
) {
return {
key: 'generic-entity-table-data-companies',
variables: variables,
typename: 'Company',
resolver: optimisticEffectResolver,
};
}

View File

@ -1,5 +1,5 @@
import { companiesAvailableColumnDefinitions } from '@/companies/constants/companiesAvailableColumnDefinitions'; import { companiesAvailableColumnDefinitions } from '@/companies/constants/companiesAvailableColumnDefinitions';
import { getCompaniesOptimisticEffect } from '@/companies/graphql/optimistic-effects/getCompaniesOptimisticEffect'; import { getCompaniesOptimisticEffectDefinition } from '@/companies/graphql/optimistic-effect-definitions/getCompaniesOptimisticEffectDefinition';
import { useCompanyTableActionBarEntries } from '@/companies/hooks/useCompanyTableActionBarEntries'; import { useCompanyTableActionBarEntries } from '@/companies/hooks/useCompanyTableActionBarEntries';
import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyTableContextMenuEntries'; import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyTableContextMenuEntries';
import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport'; import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport';
@ -53,7 +53,9 @@ export function CompanyTable() {
<GenericEntityTableData <GenericEntityTableData
getRequestResultKey="companies" getRequestResultKey="companies"
useGetRequest={useGetCompaniesQuery} useGetRequest={useGetCompaniesQuery}
getRequestOptimisticEffect={getCompaniesOptimisticEffect} getRequestOptimisticEffectDefinition={
getCompaniesOptimisticEffectDefinition
}
orderBy={sortsOrderBy} orderBy={sortsOrderBy}
whereFilters={filtersWhere} whereFilters={filtersWhere}
filterDefinitionArray={companiesFilters} filterDefinitionArray={companiesFilters}

View File

@ -1,11 +0,0 @@
import { gql } from '@apollo/client';
export const INSERT_PERSON_FRAGMENT = gql`
fragment InsertPersonFragment on Person {
id
firstName
lastName
displayName
createdAt
}
`;

View File

@ -0,0 +1,24 @@
import { gql } from '@apollo/client';
export const PERSON_FIELDS_FRAGMENT = gql`
fragment personFieldsFragment on Person {
id
phone
email
city
firstName
lastName
displayName
jobTitle
linkedinUrl
xUrl
avatarUrl
createdAt
_activityCount
company {
id
name
domainName
}
}
`;

View File

@ -3,7 +3,7 @@ import { gql } from '@apollo/client';
export const INSERT_ONE_PERSON = gql` export const INSERT_ONE_PERSON = gql`
mutation InsertOnePerson($data: PersonCreateInput!) { mutation InsertOnePerson($data: PersonCreateInput!) {
createOnePerson(data: $data) { createOnePerson(data: $data) {
...InsertPersonFragment ...personFieldsFragment
} }
} }
`; `;

View File

@ -1,45 +0,0 @@
import { ApolloCache } from '@apollo/client';
import {
GetPeopleQuery,
GetPeopleQueryVariables,
Person,
} from '~/generated/graphql';
import { GET_PEOPLE } from '../queries/getPeople';
function optimisticEffectResolver({
cache,
entities,
variables,
}: {
cache: ApolloCache<Person>;
entities: Person[];
variables: GetPeopleQueryVariables;
}) {
const existingData = cache.readQuery<GetPeopleQuery>({
query: GET_PEOPLE,
variables: { orderBy: variables.orderBy, where: variables.where },
});
if (!existingData) {
return;
}
cache.writeQuery({
query: GET_PEOPLE,
variables: { orderBy: variables.orderBy, where: variables.where },
data: {
people: [...entities, ...existingData.people],
},
});
}
export function getPeopleOptimisticEffect(variables: GetPeopleQueryVariables) {
return {
key: 'generic-entity-table-data-person',
variables: variables,
typename: 'Person',
resolver: optimisticEffectResolver,
};
}

View File

@ -0,0 +1,18 @@
import { Person } from '~/generated/graphql';
import { GET_PEOPLE } from '../queries/getPeople';
export const getPeopleOptimisticEffectDefinition = {
key: 'generic-entity-table-data-people',
typename: 'Person',
query: GET_PEOPLE,
resolver: ({
currentData,
newData,
}: {
currentData: Person[];
newData: Person[];
}) => {
return [...newData, ...currentData];
},
};

View File

@ -1,5 +1,5 @@
import { peopleAvailableColumnDefinitions } from '@/people/constants/peopleAvailableColumnDefinitions'; import { peopleAvailableColumnDefinitions } from '@/people/constants/peopleAvailableColumnDefinitions';
import { getPeopleOptimisticEffect } from '@/people/graphql/optimistic-effect-callback/getPeopleOptimisticEffect'; import { getPeopleOptimisticEffectDefinition } from '@/people/graphql/optimistic-effect-definitions/getPeopleOptimisticEffectDefinition';
import { usePersonTableContextMenuEntries } from '@/people/hooks/usePeopleTableContextMenuEntries'; import { usePersonTableContextMenuEntries } from '@/people/hooks/usePeopleTableContextMenuEntries';
import { usePersonTableActionBarEntries } from '@/people/hooks/usePersonTableActionBarEntries'; import { usePersonTableActionBarEntries } from '@/people/hooks/usePersonTableActionBarEntries';
import { useSpreadsheetPersonImport } from '@/people/hooks/useSpreadsheetPersonImport'; import { useSpreadsheetPersonImport } from '@/people/hooks/useSpreadsheetPersonImport';
@ -52,7 +52,9 @@ export function PeopleTable() {
<GenericEntityTableData <GenericEntityTableData
getRequestResultKey="people" getRequestResultKey="people"
useGetRequest={useGetPeopleQuery} useGetRequest={useGetPeopleQuery}
getRequestOptimisticEffect={getPeopleOptimisticEffect} getRequestOptimisticEffectDefinition={
getPeopleOptimisticEffectDefinition
}
orderBy={sortsOrderBy} orderBy={sortsOrderBy}
whereFilters={filtersWhere} whereFilters={filtersWhere}
filterDefinitionArray={peopleFilters} filterDefinitionArray={peopleFilters}

View File

@ -11,7 +11,7 @@ export const SEARCH_COMPANY_QUERY = gql`
take: $limit take: $limit
orderBy: $orderBy orderBy: $orderBy
) { ) {
...CompanyFieldsFragment ...companyFieldsFragment
} }
} }
`; `;

View File

@ -98,8 +98,6 @@ export function useFilteredSearchEntityQuery<
}; };
}); });
console.log(searchFilter);
const { const {
loading: filteredSelectedEntitiesLoading, loading: filteredSelectedEntitiesLoading,
data: filteredSelectedEntitiesData, data: filteredSelectedEntitiesData,

View File

@ -27,7 +27,7 @@ export function ShowPageAddButton({
const openCreateActivity = useOpenCreateActivityDrawer(); const openCreateActivity = useOpenCreateActivityDrawer();
function handleSelect(type: ActivityType) { function handleSelect(type: ActivityType) {
openCreateActivity(type, [entity]); openCreateActivity({ type, targetableEntities: [entity] });
closeDropdownButton(); closeDropdownButton();
} }

View File

@ -9,7 +9,7 @@ import {
import { isNavbarOpenedState } from '@/ui/layout/states/isNavbarOpenedState'; import { isNavbarOpenedState } from '@/ui/layout/states/isNavbarOpenedState';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
const StyledCollapseButton = styled.button<{ const StyledCollapseButton = styled.div<{
hide: boolean; hide: boolean;
}>` }>`
align-items: center; align-items: center;

View File

@ -1,7 +1,7 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect'; import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
import { OptimisticEffect } from '@/apollo/optimistic-effect/types/OptimisticEffect'; import { OptimisticEffectDefinition } from '@/apollo/optimistic-effect/types/OptimisticEffectDefinition';
import { useSetEntityTableData } from '@/ui/table/hooks/useSetEntityTableData'; import { useSetEntityTableData } from '@/ui/table/hooks/useSetEntityTableData';
import { FilterDefinition } from '@/ui/view-bar/types/FilterDefinition'; import { FilterDefinition } from '@/ui/view-bar/types/FilterDefinition';
import { SortOrder } from '~/generated/graphql'; import { SortOrder } from '~/generated/graphql';
@ -9,7 +9,7 @@ import { SortOrder } from '~/generated/graphql';
export function GenericEntityTableData({ export function GenericEntityTableData({
useGetRequest, useGetRequest,
getRequestResultKey, getRequestResultKey,
getRequestOptimisticEffect, getRequestOptimisticEffectDefinition,
orderBy = [ orderBy = [
{ {
createdAt: SortOrder.Desc, createdAt: SortOrder.Desc,
@ -22,7 +22,7 @@ export function GenericEntityTableData({
}: { }: {
useGetRequest: any; useGetRequest: any;
getRequestResultKey: string; getRequestResultKey: string;
getRequestOptimisticEffect: (variables: any) => OptimisticEffect<any, any>; getRequestOptimisticEffectDefinition: OptimisticEffectDefinition<any>;
orderBy?: any; orderBy?: any;
whereFilters?: any; whereFilters?: any;
filterDefinitionArray: FilterDefinition[]; filterDefinitionArray: FilterDefinition[];
@ -37,9 +37,10 @@ export function GenericEntityTableData({
onCompleted: (data: any) => { onCompleted: (data: any) => {
const entities = data[getRequestResultKey] ?? []; const entities = data[getRequestResultKey] ?? [];
setEntityTableData(entities, filterDefinitionArray); setEntityTableData(entities, filterDefinitionArray);
registerOptimisticEffect( registerOptimisticEffect({
getRequestOptimisticEffect({ orderBy, where: whereFilters }), variables: { orderBy, where: whereFilters },
); definition: getRequestOptimisticEffectDefinition,
});
}, },
}); });

View File

@ -43,21 +43,6 @@ export function Companies() {
address: '', address: '',
}, },
}, },
optimisticResponse: {
__typename: 'Mutation',
createOneCompany: {
__typename: 'Company',
id: newCompanyId,
name: '',
domainName: '',
address: '',
createdAt: new Date().toISOString(),
accountOwner: null,
linkedinUrl: '',
idealCustomerProfile: false,
employees: null,
},
},
update: (_cache, { data }) => { update: (_cache, { data }) => {
if (data?.createOneCompany) { if (data?.createOneCompany) {
upsertTableRowIds(data?.createOneCompany.id); upsertTableRowIds(data?.createOneCompany.id);

View File

@ -40,17 +40,6 @@ export function People() {
lastName: '', lastName: '',
}, },
}, },
optimisticResponse: {
__typename: 'Mutation',
createOnePerson: {
__typename: 'Person',
id: newPersonId,
firstName: '',
lastName: '',
displayName: '',
createdAt: '',
},
},
update: (_cache, { data }) => { update: (_cache, { data }) => {
if (data?.createOnePerson) { if (data?.createOnePerson) {
upsertTableRowIds(data?.createOnePerson.id); upsertTableRowIds(data?.createOnePerson.id);

View File

@ -1,12 +1,10 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext'; import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext';
import { PageAddTaskButton } from '@/activities/tasks/components/PageAddTaskButton';
import { TaskGroups } from '@/activities/tasks/components/TaskGroups'; import { TaskGroups } from '@/activities/tasks/components/TaskGroups';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { IconArchive, IconCheck, IconCheckbox } from '@/ui/icon/index'; import { IconArchive, IconCheck, IconCheckbox } from '@/ui/icon/index';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { PageAddButton } from '@/ui/layout/components/PageAddButton';
import { PageBody } from '@/ui/layout/components/PageBody'; import { PageBody } from '@/ui/layout/components/PageBody';
import { PageContainer } from '@/ui/layout/components/PageContainer'; import { PageContainer } from '@/ui/layout/components/PageContainer';
import { PageHeader } from '@/ui/layout/components/PageHeader'; import { PageHeader } from '@/ui/layout/components/PageHeader';
@ -14,7 +12,6 @@ import { TabList } from '@/ui/tab/components/TabList';
import { TopBar } from '@/ui/top-bar/TopBar'; import { TopBar } from '@/ui/top-bar/TopBar';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { FilterDropdownButton } from '@/ui/view-bar/components/FilterDropdownButton'; import { FilterDropdownButton } from '@/ui/view-bar/components/FilterDropdownButton';
import { ActivityType } from '~/generated/graphql';
const StyledTasksContainer = styled.div` const StyledTasksContainer = styled.div`
display: flex; display: flex;
@ -32,8 +29,6 @@ const StyledTabListContainer = styled.div`
`; `;
export function Tasks() { export function Tasks() {
const openCreateActivity = useOpenCreateActivityDrawer();
const TASK_TABS = [ const TASK_TABS = [
{ {
id: 'to-do', id: 'to-do',
@ -49,16 +44,12 @@ export function Tasks() {
return ( return (
<PageContainer> <PageContainer>
<PageHeader title="Tasks" Icon={IconCheckbox}> <RecoilScope SpecificContext={TasksRecoilScopeContext}>
<RecoilScope SpecificContext={DropdownRecoilScopeContext}> <PageHeader title="Tasks" Icon={IconCheckbox}>
<PageAddButton <PageAddTaskButton />
onClick={() => openCreateActivity(ActivityType.Task)} </PageHeader>
/> <PageBody>
</RecoilScope> <StyledTasksContainer>
</PageHeader>
<PageBody>
<StyledTasksContainer>
<RecoilScope SpecificContext={TasksRecoilScopeContext}>
<TopBar <TopBar
leftComponent={ leftComponent={
<StyledTabListContainer> <StyledTabListContainer>
@ -74,9 +65,9 @@ export function Tasks() {
} }
/> />
<TaskGroups /> <TaskGroups />
</RecoilScope> </StyledTasksContainer>
</StyledTasksContainer> </PageBody>
</PageBody> </RecoilScope>
</PageContainer> </PageContainer>
); );
} }

View File

@ -191,10 +191,10 @@ export function PageChangeEffect() {
type: CommandType.Create, type: CommandType.Create,
Icon: IconCheckbox, Icon: IconCheckbox,
onCommandClick: () => onCommandClick: () =>
openCreateActivity( openCreateActivity({
ActivityType.Task, type: ActivityType.Task,
entity ? [entity] : undefined, targetableEntities: entity ? [entity] : undefined,
), }),
}, },
{ {
to: '', to: '',
@ -202,10 +202,10 @@ export function PageChangeEffect() {
type: CommandType.Create, type: CommandType.Create,
Icon: IconNotes, Icon: IconNotes,
onCommandClick: () => onCommandClick: () =>
openCreateActivity( openCreateActivity({
ActivityType.Note, type: ActivityType.Note,
entity ? [entity] : undefined, targetableEntities: entity ? [entity] : undefined,
), }),
}, },
]); ]);
break; break;
@ -225,10 +225,10 @@ export function PageChangeEffect() {
type: CommandType.Create, type: CommandType.Create,
Icon: IconCheckbox, Icon: IconCheckbox,
onCommandClick: () => onCommandClick: () =>
openCreateActivity( openCreateActivity({
ActivityType.Task, type: ActivityType.Task,
entity ? [entity] : undefined, targetableEntities: entity ? [entity] : undefined,
), }),
}, },
{ {
to: '', to: '',
@ -236,10 +236,10 @@ export function PageChangeEffect() {
type: CommandType.Create, type: CommandType.Create,
Icon: IconNotes, Icon: IconNotes,
onCommandClick: () => onCommandClick: () =>
openCreateActivity( openCreateActivity({
ActivityType.Note, type: ActivityType.Note,
entity ? [entity] : undefined, targetableEntities: entity ? [entity] : undefined,
), }),
}, },
]); ]);
break; break;
@ -251,7 +251,8 @@ export function PageChangeEffect() {
label: 'Create Task', label: 'Create Task',
type: CommandType.Create, type: CommandType.Create,
Icon: IconCheckbox, Icon: IconCheckbox,
onCommandClick: () => openCreateActivity(ActivityType.Task), onCommandClick: () =>
openCreateActivity({ type: ActivityType.Task }),
}, },
]); ]);
break; break;