Removing Prisma and Grapql-nestjs-prisma resolvers (#2574)

* Some cleaning

* Fix seeds

* Fix all sign in, sign up flow and apiKey optimistic rendering

* Fix
This commit is contained in:
Charles Bochet
2023-11-19 18:25:47 +01:00
committed by GitHub
parent 18dac1a2b6
commit f5e1d7825a
616 changed files with 2220 additions and 23073 deletions

View File

@ -1,99 +0,0 @@
import { Activity } from '@/activities/types/Activity';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import {
useGetWorkspaceMembersLazyQuery,
useSearchUserQuery,
} from '~/generated/graphql';
export type ActivityAssigneePickerProps = {
activity: Pick<Activity, 'id'> & {
accountOwner?: Pick<WorkspaceMember, 'id' | 'name'> | null;
};
onSubmit?: () => void;
onCancel?: () => void;
};
type UserForSelect = EntityForSelect & {
entityType: Entity.User;
};
export const ActivityAssigneePicker = ({
activity,
onSubmit,
onCancel,
}: ActivityAssigneePickerProps) => {
const [relationPickerSearchFilter] = useRecoilScopedState(
relationPickerSearchFilterScopedState,
);
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: 'ActivityV2',
});
const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery();
const users = useFilteredSearchEntityQuery({
queryHook: useSearchUserQuery,
filters: [
{
fieldNames: ['firstName', 'lastName'],
filter: relationPickerSearchFilter,
},
],
orderByField: 'firstName',
mappingFunction: (user) => ({
entityType: Entity.User,
id: user.id,
name: user.displayName,
firstName: user.firstName,
lastName: user.lastName,
avatarType: 'rounded',
avatarUrl: user.avatarUrl ?? '',
originalEntity: user,
}),
selectedIds: activity?.accountOwner?.id ? [activity?.accountOwner?.id] : [],
});
const handleEntitySelected = async (
selectedUser: UserForSelect | null | undefined,
) => {
if (selectedUser) {
const workspaceMemberAssignee = (
await getWorkspaceMember({
variables: {
where: {
userId: { equals: selectedUser.id },
},
},
})
).data?.workspaceMembers?.[0];
updateOneObject?.({
idToUpdate: activity.id,
input: {
assignee: { connect: { id: selectedUser.id } },
workspaceMemberAssignee: {
connect: { id: workspaceMemberAssignee?.id },
},
},
});
}
onSubmit?.();
};
return (
<SingleEntitySelect
entitiesToSelect={users.entitiesToSelect}
loading={users.loading}
onCancel={onCancel}
onEntitySelected={handleEntitySelected}
selectedEntity={users.selectedEntities[0]}
/>
);
};

View File

@ -23,7 +23,7 @@ export const ActivityBodyEditor = ({
}: ActivityBodyEditorProps) => {
const [body, setBody] = useState<string | null>(null);
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: 'ActivityV2',
objectNameSingular: 'Activity',
});
useEffect(() => {

View File

@ -63,7 +63,7 @@ export const ActivityComments = ({
}: ActivityCommentsProps) => {
const currentUser = useRecoilValue(currentUserState);
const { createOneObject } = useCreateOneObjectRecord({
objectNameSingular: 'commentV2',
objectNameSingular: 'comment',
});
if (!currentUser) {

View File

@ -9,13 +9,10 @@ import { ActivityTarget } from '@/activities/types/ActivityTarget';
import { Comment } from '@/activities/types/Comment';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { PropertyBox } from '@/ui/object/record-inline-cell/property-box/components/PropertyBox';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { debounce } from '~/utils/debounce';
import { ActivityAssigneeEditableField } from '../editable-fields/components/ActivityAssigneeEditableField';
import { ActivityEditorDateField } from '../editable-fields/components/ActivityEditorDateField';
import { ActivityRelationEditableField } from '../editable-fields/components/ActivityRelationEditableField';
import { ActivityTitle } from './ActivityTitle';
@ -84,7 +81,7 @@ export const ActivityEditor = ({
);
const containerRef = useRef<HTMLDivElement>(null);
const { updateOneObject } = useUpdateOneObjectRecord<Activity>({
objectNameSingular: 'activityV2',
objectNameSingular: 'activity',
});
const updateTitle = useCallback(
@ -144,12 +141,12 @@ export const ActivityEditor = ({
<PropertyBox>
{activity.type === 'Task' && (
<>
<RecoilScope>
{/* <RecoilScope>
<ActivityEditorDateField activityId={activity.id} />
</RecoilScope>
<RecoilScope>
<ActivityAssigneeEditableField activity={activity} />
</RecoilScope>
</RecoilScope> */}
</>
)}
<ActivityRelationEditableField activity={activity} />

View File

@ -2,8 +2,9 @@ import styled from '@emotion/styled';
import { ActivityTarget } from '@/activities/types/ActivityTarget';
import { CompanyChip } from '@/companies/components/CompanyChip';
import { Company } from '@/companies/types/Company';
import { PersonChip } from '@/people/components/PersonChip';
import { Company, Person } from '~/generated/graphql';
import { Person } from '@/people/types/Person';
import { getLogoUrlFromDomainName } from '~/utils';
const StyledContainer = styled.div`
@ -17,10 +18,7 @@ export const ActivityTargetChips = ({
}: {
targets?: Array<
Pick<ActivityTarget, 'id'> & {
person?: Pick<
Person,
'id' | 'firstName' | 'lastName' | 'avatarUrl'
> | null;
person?: Pick<Person, 'id' | 'name' | 'avatarUrl'> | null;
company?: Pick<Company, 'id' | 'domainName' | 'name'> | null;
}
> | null;
@ -47,7 +45,7 @@ export const ActivityTargetChips = ({
<PersonChip
key={person.id}
id={person.id}
name={person.firstName + ' ' + person.lastName}
name={person.name.firstName + ' ' + person.name.lastName}
pictureUrl={person.avatarUrl ?? undefined}
/>
);

View File

@ -1,78 +0,0 @@
import React, { useMemo } from 'react';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { IconUserCircle } from '@/ui/display/icon';
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition';
import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata';
import { RecordInlineCell } from '@/ui/object/record-inline-cell/components/RecordInlineCell';
import { InlineCellHotkeyScope } from '@/ui/object/record-inline-cell/types/InlineCellHotkeyScope';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { Company, User } from '~/generated/graphql';
type ActivityAssigneeEditableFieldProps = {
activity: Pick<Company, 'id' | 'accountOwnerId'> & {
assignee?: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
};
};
export const ActivityAssigneeEditableField = ({
activity,
}: ActivityAssigneeEditableFieldProps) => {
const useUpdateOneObjectMutation: () => [(params: any) => any, any] = () => {
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: 'activityV2',
});
const updateEntity = ({
variables,
}: {
variables: {
where: { id: string };
data: {
[fieldName: string]: any;
};
};
}) => {
updateOneObject?.({
idToUpdate: variables.where.id,
input: variables.data,
});
};
return [updateEntity, { loading: false }];
};
const value = useMemo(
() => ({
entityId: activity.id,
recoilScopeId: 'assignee',
fieldDefinition: {
fieldMetadataId: 'assignee',
label: 'Assignee',
Icon: IconUserCircle,
type: 'RELATION',
metadata: {
fieldName: 'assignee',
relationType: Entity.User,
},
entityChipDisplayMapper: (dataObject: User) => {
return {
name: dataObject?.displayName,
pictureUrl: dataObject?.avatarUrl ?? undefined,
avatarType: 'rounded',
};
},
} satisfies FieldDefinition<FieldRelationMetadata>,
useUpdateEntityMutation: useUpdateOneObjectMutation,
hotkeyScope: InlineCellHotkeyScope.InlineCell,
}),
[activity.id],
);
return (
<FieldContext.Provider value={value}>
<RecordInlineCell />
</FieldContext.Provider>
);
};

View File

@ -1,47 +0,0 @@
import styled from '@emotion/styled';
import { ActivityAssigneePicker } from '@/activities/components/ActivityAssigneePicker';
import { useInlineCell } from '@/ui/object/record-inline-cell/hooks/useInlineCell';
import { Activity, User } from '~/generated/graphql';
const StyledContainer = styled.div`
left: 0px;
position: absolute;
top: -8px;
`;
export type ActivityAssigneeEditableFieldEditModeProps = {
activity: Pick<Activity, 'id'> & {
assignee?: Pick<User, 'id' | 'displayName'> | null;
};
onSubmit?: () => void;
onCancel?: () => void;
};
export const ActivityAssigneeEditableFieldEditMode = ({
activity,
onSubmit,
onCancel,
}: ActivityAssigneeEditableFieldEditModeProps) => {
const { closeInlineCell: closeEditableField } = useInlineCell();
const handleSubmit = () => {
closeEditableField();
onSubmit?.();
};
const handleCancel = () => {
closeEditableField();
onCancel?.();
};
return (
<StyledContainer>
<ActivityAssigneePicker
activity={activity}
onCancel={handleCancel}
onSubmit={handleSubmit}
/>
</StyledContainer>
);
};

View File

@ -1,64 +0,0 @@
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { IconCalendar } from '@/ui/display/icon/index';
import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition';
import { FieldDateMetadata } from '@/ui/object/field/types/FieldMetadata';
import { RecordInlineCell } from '@/ui/object/record-inline-cell/components/RecordInlineCell';
import { InlineCellHotkeyScope } from '@/ui/object/record-inline-cell/types/InlineCellHotkeyScope';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
type ActivityEditorDateFieldProps = {
activityId: string;
};
export const ActivityEditorDateField = ({
activityId,
}: ActivityEditorDateFieldProps) => {
const useUpdateOneObjectMutation: () => [(params: any) => any, any] = () => {
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: 'activityV2',
});
const updateEntity = ({
variables,
}: {
variables: {
where: { id: string };
data: {
[fieldName: string]: any;
};
};
}) => {
updateOneObject?.({
idToUpdate: variables.where.id,
input: variables.data,
});
};
return [updateEntity, { loading: false }];
};
return (
<RecoilScope>
<FieldContext.Provider
value={{
entityId: activityId,
recoilScopeId: 'activityDueAt',
fieldDefinition: {
fieldMetadataId: 'activityDueAt',
label: 'Due date',
Icon: IconCalendar,
type: 'DATE',
metadata: {
fieldName: 'dueAt',
},
} satisfies FieldDefinition<FieldDateMetadata>,
useUpdateEntityMutation: useUpdateOneObjectMutation,
hotkeyScope: InlineCellHotkeyScope.InlineCell,
}}
>
<RecordInlineCell />
</FieldContext.Provider>
</RecoilScope>
);
};

View File

@ -1,12 +1,13 @@
import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips';
import { Activity } from '@/activities/types/Activity';
import { ActivityTarget } from '@/activities/types/ActivityTarget';
import { Company } from '@/companies/types/Company';
import { Person } from '@/people/types/Person';
import { IconArrowUpRight, IconPencil } from '@/ui/display/icon';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { RecordInlineCellContainer } from '@/ui/object/record-inline-cell/components/RecordInlineCellContainer';
import { FieldRecoilScopeContext } from '@/ui/object/record-inline-cell/states/recoil-scope-contexts/FieldRecoilScopeContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { Company, Person } from '~/generated/graphql';
import { ActivityRelationEditableFieldEditMode } from './ActivityRelationEditableFieldEditMode';
@ -14,7 +15,7 @@ type ActivityRelationEditableFieldProps = {
activity?: Pick<Activity, 'id'> & {
activityTargets?: Array<
Pick<ActivityTarget, 'id' | 'personId' | 'companyId'> & {
person?: Pick<Person, 'id' | 'firstName' | 'lastName'> | null;
person?: Pick<Person, 'id' | 'name' | 'avatarUrl'> | null;
company?: Pick<Company, 'id' | 'domainName' | 'name'> | null;
}
> | null;

View File

@ -92,14 +92,9 @@ export const ActivityRelationEditableFieldEditMode = ({
const { closeInlineCell: closeEditableField } = useInlineCell();
const handleSubmit = useCallback(() => {
handleCheckItemsChange(selectedEntityIds, entitiesToSelect);
//handleCheckItemsChange(selectedEntityIds, entitiesToSelect);
closeEditableField();
}, [
handleCheckItemsChange,
selectedEntityIds,
entitiesToSelect,
closeEditableField,
]);
}, [closeEditableField]);
const handleCancel = () => {
closeEditableField();

View File

@ -17,10 +17,10 @@ export const useHandleCheckableActivityTargetChange = ({
};
}) => {
const { createOneObject } = useCreateOneObjectRecord<ActivityTarget>({
objectNameSingular: 'activityTargetV2',
objectNameSingular: 'activityTarget',
});
const { deleteOneObject } = useDeleteOneObjectRecord({
objectNameSingular: 'activityTargetV2',
objectNameSingular: 'activityTarget',
});
return async (

View File

@ -18,11 +18,11 @@ export const useOpenCreateActivityDrawer = () => {
const { openRightDrawer } = useRightDrawer();
const { createOneObject: createOneActivityTarget } =
useCreateOneObjectRecord<ActivityTarget>({
objectNameSingular: 'activityTargetV2',
objectNameSingular: 'activityTarget',
});
const { createOneObject: createOneActivity } =
useCreateOneObjectRecord<Activity>({
objectNameSingular: 'activityV2',
objectNameSingular: 'activity',
});
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const setHotkeyScope = useSetHotkeyScope();

View File

@ -6,7 +6,6 @@ import { useNotes } from '@/activities/notes/hooks/useNotes';
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
import { ActivityType } from '~/generated/graphql';
const StyledTaskGroupEmptyContainer = styled.div`
align-items: center;
@ -61,7 +60,7 @@ export const Notes = ({ entity }: { entity: ActivityTargetableEntity }) => {
variant="secondary"
onClick={() =>
openCreateActivity({
type: ActivityType.Note,
type: 'Note',
targetableEntities: [entity],
})
}
@ -83,7 +82,7 @@ export const Notes = ({ entity }: { entity: ActivityTargetableEntity }) => {
title="Add note"
onClick={() =>
openCreateActivity({
type: ActivityType.Note,
type: 'Note',
targetableEntities: [entity],
})
}

View File

@ -5,7 +5,7 @@ import { ActivityTargetableEntity } from '../../types/ActivityTargetableEntity';
export const useNotes = (entity: ActivityTargetableEntity) => {
const { objects: notes } = useFindManyObjectRecords({
objectNamePlural: 'activitiesV2',
objectNamePlural: 'activities',
filter: {
type: { equals: 'None' },
activityTargets: {

View File

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

View File

@ -2,7 +2,6 @@ import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateAct
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
import { ActivityType } from '~/generated/graphql';
export const AddTaskButton = ({
activityTargetEntity,
@ -23,7 +22,7 @@ export const AddTaskButton = ({
title="Add task"
onClick={() =>
openCreateActivity({
type: ActivityType.Task,
type: 'Task',
targetableEntities: [activityTargetEntity],
})
}

View File

@ -1,7 +1,6 @@
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
import { ActivityType } from '~/generated/graphql';
export const PageAddTaskButton = () => {
const { selectedFilter } = useFilter();
@ -9,7 +8,7 @@ export const PageAddTaskButton = () => {
const handleClick = () => {
openCreateActivity({
type: ActivityType.Task,
type: 'Task',
assigneeId: selectedFilter?.value,
});
};

View File

@ -8,7 +8,6 @@ import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
import { activeTabIdScopedState } from '@/ui/layout/tab/states/activeTabIdScopedState';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { ActivityType } from '~/generated/graphql';
import { AddTaskButton } from './AddTaskButton';
import { TaskList } from './TaskList';
@ -84,7 +83,7 @@ export const TaskGroups = ({ entity, showAddButton }: TaskGroupsProps) => {
variant={'secondary'}
onClick={() =>
openCreateActivity({
type: ActivityType.Task,
type: 'Task',
targetableEntities: entity ? [entity] : undefined,
})
}

View File

@ -1,13 +1,13 @@
import { useCallback } from 'react';
import { Activity } from '@/activities/types/Activity';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { Activity } from '~/generated/graphql';
type Task = Pick<Activity, 'id' | 'completedAt'>;
export const useCompleteTask = (task: Task) => {
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: 'activityV2',
objectNameSingular: 'activity',
});
const completeTask = useCallback(

View File

@ -9,7 +9,7 @@ export const useCurrentUserTaskCount = () => {
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const { objects } = useFindManyObjectRecords({
objectNamePlural: 'activitiesV2',
objectNamePlural: 'activities',
filter: {
type: { eq: 'Task' },
completedAt: { eq: null },

View File

@ -4,8 +4,7 @@ import { Activity } from '@/activities/types/Activity';
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
import { turnFilterIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFilterIntoWhereClause';
import { SortOrder } from '~/generated/graphql';
import { turnFiltersIntoWhereClauseV2 } from '@/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClauseV2';
import { parseDate } from '~/utils/date-utils';
export const useTasks = (entity?: ActivityTargetableEntity) => {
@ -22,10 +21,10 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
},
},
}
: Object.assign({}, turnFilterIntoWhereClause(selectedFilter));
: Object.assign({}, turnFiltersIntoWhereClauseV2([], []));
const { objects: completeTasksData } = useFindManyObjectRecords({
objectNamePlural: 'activitiesV2',
objectNamePlural: 'activities',
skip: !entity && !selectedFilter,
filter: {
type: { equals: 'Task' },
@ -34,13 +33,13 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
},
orderBy: [
{
createdAt: SortOrder.Desc,
createdAt: 'AscNullIsFirst',
},
],
});
const { objects: incompleteTaskData } = useFindManyObjectRecords({
objectNamePlural: 'activitiesV2',
objectNamePlural: 'activities',
skip: !entity && !selectedFilter,
filter: {
type: { equals: 'Task' },
@ -49,7 +48,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
},
orderBy: [
{
createdAt: SortOrder.Desc,
createdAt: 'DescNullIsFirst',
},
],
});

View File

@ -49,7 +49,7 @@ const StyledEmptyTimelineSubTitle = styled.div`
export const Timeline = ({ entity }: { entity: ActivityTargetableEntity }) => {
const { objects: activityTargets, loading } = useFindManyObjectRecords({
objectNamePlural: 'activityTargetsV2',
objectNamePlural: 'activityTargets',
filter: {
or: {
companyId: { eq: entity.id },
@ -60,7 +60,7 @@ export const Timeline = ({ entity }: { entity: ActivityTargetableEntity }) => {
const { objects: activities } = useFindManyObjectRecords({
skip: !activityTargets?.length,
objectNamePlural: 'activitiesV2',
objectNamePlural: 'activities',
filter: {
activityTargets: { in: activityTargets?.map((at) => at.id) },
},

View File

@ -1,5 +1,6 @@
import { Activity } from '@/activities/types/Activity';
import { Company, Person } from '~/generated-metadata/graphql';
import { Company } from '@/companies/types/Company';
import { Person } from '@/people/types/Person';
export type ActivityTarget = {
id: string;
@ -8,7 +9,7 @@ export type ActivityTarget = {
companyId: string | null;
personId: string | null;
activity: Pick<Activity, 'id' | 'createdAt' | 'updatedAt'>;
person?: Pick<Person, 'id' | 'firstName' | 'lastName' | 'avatarUrl'> | null;
person?: Pick<Person, 'id' | 'name' | 'avatarUrl'> | null;
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
[key: string]: any;
};

View File

@ -7,7 +7,6 @@ import { tokenPairState } from '@/auth/states/tokenPairState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { AppPath } from '@/types/AppPath';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { ActivityTarget } from '~/generated/graphql';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { useUpdateEffect } from '~/hooks/useUpdateEffect';
@ -25,23 +24,7 @@ export const useApolloFactory = () => {
const apolloClient = useMemo(() => {
apolloRef.current = new ApolloFactory({
uri: `${REACT_APP_SERVER_BASE_URL}/graphql`,
cache: new InMemoryCache({
typePolicies: {
Activity: {
fields: {
activityTargets: {
merge: (
_existing: ActivityTarget[] = [],
incoming: ActivityTarget[],
) => {
return [...incoming];
},
},
},
},
ViewField: { keyFields: ['viewId', 'key'] },
},
}),
cache: new InMemoryCache(),
defaultOptions: {
query: {
fetchPolicy: 'cache-first',

View File

@ -7,19 +7,11 @@ import {
import { isNonEmptyArray } from '@sniptt/guards';
import { useRecoilCallback } from 'recoil';
import { GET_COMPANIES } from '@/companies/graphql/queries/getCompanies';
import {
EMPTY_QUERY,
useFindOneObjectMetadataItem,
} from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { GET_PEOPLE } from '@/people/graphql/queries/getPeople';
import { GET_API_KEYS } from '@/settings/developers/graphql/queries/getApiKeys';
import {
GetApiKeysQuery,
GetCompaniesQuery,
GetPeopleQuery,
} from '~/generated/graphql';
import { optimisticEffectState } from '../states/optimisticEffectState';
import { OptimisticEffect } from '../types/internal/OptimisticEffect';
@ -59,7 +51,6 @@ export const useOptimisticEffect = ({
newData,
query,
variables,
isUsingFlexibleBackend,
objectMetadataItem,
}: {
cache: ApolloCache<unknown>;
@ -69,7 +60,7 @@ export const useOptimisticEffect = ({
isUsingFlexibleBackend?: boolean;
objectMetadataItem?: ObjectMetadataItem;
}) => {
if (isUsingFlexibleBackend && objectMetadataItem) {
if (objectMetadataItem) {
const existingData = cache.readQuery({
query: findManyQuery,
variables,
@ -104,50 +95,6 @@ export const useOptimisticEffect = ({
if (!existingData) {
return;
}
if (query === GET_PEOPLE) {
cache.writeQuery({
query,
variables,
data: {
people: definition.resolver({
currentData: (existingData as GetPeopleQuery).people as T[],
newData: newData as T[],
variables,
}),
},
});
}
if (query === GET_COMPANIES) {
cache.writeQuery({
query,
variables,
data: {
companies: definition.resolver({
currentData: (existingData as GetCompaniesQuery)
.companies as T[],
newData: newData as T[],
variables,
}),
},
});
}
if (query === GET_API_KEYS) {
cache.writeQuery({
query,
variables,
data: {
findManyApiKey: definition.resolver({
currentData: (existingData as GetApiKeysQuery)
.findManyApiKey as T[],
newData: newData as T[],
variables,
}),
},
});
}
};
const optimisticEffect = {

View File

@ -0,0 +1,6 @@
export type Attachment = {
id: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
};

View File

@ -1,13 +0,0 @@
import { gql } from '@apollo/client';
export const USER_QUERY_FRAGMENT = gql`
fragment UserQueryFragment on User {
id
email
displayName
firstName
lastName
canImpersonate
supportUserHash
}
`;

View File

@ -0,0 +1,9 @@
import { gql } from '@apollo/client';
export const GENERATE_ONE_API_KEY_TOKEN = gql`
mutation GenerateApiKeyToken($apiKeyId: String!, $expiresAt: String!) {
generateApiKeyToken(apiKeyId: $apiKeyId, expiresAt: $expiresAt) {
token
}
}
`;

View File

@ -10,13 +10,11 @@ import {
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
import { CREATE_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/mutation/createOneWorkspaceMember';
import { FIND_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/queries/findOneWorkspaceMember';
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
import { REACT_APP_SERVER_AUTH_URL } from '~/config';
import {
useChallengeMutation,
useCheckUserExistsLazyQuery,
useGetCurrentWorkspaceLazyQuery,
useSignUpMutation,
useVerifyMutation,
} from '~/generated/graphql';
@ -38,7 +36,6 @@ export const useAuth = () => {
const [verify] = useVerifyMutation();
const [checkUserExistsQuery, { data: checkUserExistsData }] =
useCheckUserExistsLazyQuery();
const [getCurrentWorkspaceQuery] = useGetCurrentWorkspaceLazyQuery();
const client = useApolloClient();
@ -81,31 +78,26 @@ export const useAuth = () => {
}
setTokenPair(verifyResult.data?.verify.tokens);
const workspaceMember = await client.query({
query: FIND_ONE_WORKSPACE_MEMBER_V2,
variables: {
filter: {
userId: { eq: verifyResult.data?.verify.user.id },
},
},
});
const currentWorkspace = await getCurrentWorkspaceQuery();
setCurrentUser(verifyResult.data?.verify.user);
setCurrentWorkspaceMember(workspaceMember.data?.findMany);
setCurrentWorkspace(currentWorkspace.data?.currentWorkspace ?? null);
const user = verifyResult.data?.verify.user;
const workspaceMember = {
...user.workspaceMember,
colorScheme: user.workspaceMember?.colorScheme as ColorScheme,
};
const workspace = user.defaultWorkspace ?? null;
setCurrentUser(user);
setCurrentWorkspaceMember(workspaceMember);
setCurrentWorkspace(workspace);
return {
user: verifyResult.data?.verify.user,
workspaceMember: workspaceMember.data?.findMany,
workspace: currentWorkspace.data?.currentWorkspace,
user,
workspaceMember,
workspace,
tokens: verifyResult.data?.verify.tokens,
};
},
[
verify,
setTokenPair,
client,
getCurrentWorkspaceQuery,
setCurrentUser,
setCurrentWorkspaceMember,
setCurrentWorkspace,
@ -161,38 +153,15 @@ export const useAuth = () => {
throw new Error('No login token');
}
const { user, workspace } = await handleVerify(
const { user, workspace, workspaceMember } = await handleVerify(
signUpResult.data?.signUp.loginToken.token,
);
const workspaceMember = await client.mutate({
mutation: CREATE_ONE_WORKSPACE_MEMBER_V2,
variables: {
input: {
name: {
firstName: user.firstName ?? '',
lastName: user.lastName ?? '',
},
colorScheme: 'Light',
userId: user.id,
allowImpersonation: true,
locale: 'en',
},
},
});
setCurrentWorkspaceMember(workspaceMember.data?.createWorkspaceMemberV2);
setIsVerifyPendingState(false);
return { user, workspaceMember, workspace };
},
[
setIsVerifyPendingState,
signUp,
handleVerify,
client,
setCurrentWorkspaceMember,
],
[setIsVerifyPendingState, signUp, handleVerify],
);
const handleGoogleLogin = useCallback((workspaceInviteHash?: string) => {

View File

@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import { authProvidersState } from '@/client-config/states/authProvidersState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
@ -11,13 +11,12 @@ import { useGetClientConfigQuery } from '~/generated/graphql';
export const ClientConfigProvider: React.FC<React.PropsWithChildren> = ({
children,
}) => {
const [, setAuthProviders] = useRecoilState(authProvidersState);
const [, setIsDebugMode] = useRecoilState(isDebugModeState);
const setAuthProviders = useSetRecoilState(authProvidersState);
const setIsDebugMode = useSetRecoilState(isDebugModeState);
const [, setIsSignInPrefilled] = useRecoilState(isSignInPrefilledState);
const setIsSignInPrefilled = useSetRecoilState(isSignInPrefilledState);
const [, setTelemetry] = useRecoilState(telemetryState);
const [setIsLoading] = useState(true);
const setTelemetry = useSetRecoilState(telemetryState);
const setSupportChat = useSetRecoilState(supportChatState);
const { data, loading } = useGetClientConfigQuery();
@ -41,10 +40,8 @@ export const ClientConfigProvider: React.FC<React.PropsWithChildren> = ({
setIsDebugMode,
setIsSignInPrefilled,
setTelemetry,
setIsLoading,
loading,
setSupportChat,
]);
return <>{children}</>;
return loading ? <></> : <>{children}</>;
};

View File

@ -2,17 +2,8 @@ import { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
import { IconNotes } from '@/ui/display/icon';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { Avatar } from '@/users/components/Avatar';
import {
QueryMode,
useSearchActivityQuery,
useSearchCompanyQuery,
useSearchPeopleQuery,
} from '~/generated/graphql';
import { getLogoUrlFromDomainName } from '~/utils';
import { useCommandMenu } from '../hooks/useCommandMenu';
import { commandMenuCommandsState } from '../states/commandMenuCommandsState';
@ -45,47 +36,47 @@ export const CommandMenu = () => {
[openCommandMenu, setSearch],
);
const { data: peopleData } = useSearchPeopleQuery({
skip: !isCommandMenuOpened,
variables: {
where: {
OR: [
{ firstName: { contains: search, mode: QueryMode.Insensitive } },
{ lastName: { contains: search, mode: QueryMode.Insensitive } },
],
},
limit: 3,
},
});
// const { data: peopleData } = useSearchPeopleQuery({
// skip: !isCommandMenuOpened,
// variables: {
// where: {
// OR: [
// { firstName: { contains: search, mode: QueryMode.Insensitive } },
// { lastName: { contains: search, mode: QueryMode.Insensitive } },
// ],
// },
// limit: 3,
// },
// });
const people = peopleData?.searchResults ?? [];
// const people = peopleData?.searchResults ?? [];
const { data: companyData } = useSearchCompanyQuery({
skip: !isCommandMenuOpened,
variables: {
where: {
OR: [{ name: { contains: search, mode: QueryMode.Insensitive } }],
},
limit: 3,
},
});
// const { data: companyData } = useSearchCompanyQuery({
// skip: !isCommandMenuOpened,
// variables: {
// where: {
// OR: [{ name: { contains: search, mode: QueryMode.Insensitive } }],
// },
// limit: 3,
// },
// });
const companies = companyData?.searchResults ?? [];
// const companies = companyData?.searchResults ?? [];
const { data: activityData } = useSearchActivityQuery({
skip: !isCommandMenuOpened,
variables: {
where: {
OR: [
{ title: { contains: search, mode: QueryMode.Insensitive } },
{ body: { contains: search, mode: QueryMode.Insensitive } },
],
},
limit: 3,
},
});
// const { data: activityData } = useSearchActivityQuery({
// skip: !isCommandMenuOpened,
// variables: {
// where: {
// OR: [
// { title: { contains: search, mode: QueryMode.Insensitive } },
// { body: { contains: search, mode: QueryMode.Insensitive } },
// ],
// },
// limit: 3,
// },
// });
const activities = activityData?.searchResults ?? [];
// const activities = activityData?.searchResults ?? [];
const checkInShortcuts = (cmd: Command, search: string) => {
return (cmd.firstHotKey + (cmd.secondHotKey ?? ''))
@ -158,7 +149,7 @@ export const CommandMenu = () => {
/>
))}
</CommandGroup>
<CommandGroup heading="People">
{/* <CommandGroup heading="People">
{people.map((person) => (
<CommandMenuItem
key={person.id}
@ -200,7 +191,7 @@ export const CommandMenu = () => {
onClick={() => openActivityRightDrawer(activity.id)}
/>
))}
</CommandGroup>
</CommandGroup> */}
</StyledList>
</StyledDialog>
);

View File

@ -10,7 +10,7 @@ import { Command, CommandType } from '../types/Command';
export const commandMenuCommands: Command[] = [
{
to: '/people',
to: '/objects/people',
label: 'Go to People',
type: CommandType.Navigate,
firstHotKey: 'G',
@ -18,7 +18,7 @@ export const commandMenuCommands: Command[] = [
Icon: IconUser,
},
{
to: '/companies',
to: '/objects/companies',
label: 'Go to Companies',
type: CommandType.Navigate,
firstHotKey: 'G',
@ -26,7 +26,7 @@ export const commandMenuCommands: Command[] = [
Icon: IconBuildingSkyscraper,
},
{
to: '/opportunities',
to: '/objects/opportunities',
label: 'Go to Opportunities',
type: CommandType.Navigate,
firstHotKey: 'G',

View File

@ -1,74 +0,0 @@
import { useEffect } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { Meta, StoryObj } from '@storybook/react';
import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
import { BoardCardIdContext } from '@/ui/layout/board/contexts/BoardCardIdContext';
import { boardCardFieldsScopedState } from '@/ui/layout/board/states/boardCardFieldsScopedState';
import { BoardColumnRecoilScopeContext } from '@/ui/layout/board/states/recoil-scope-contexts/BoardColumnRecoilScopeContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { ViewScope } from '@/views/scopes/ViewScope';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
import { graphqlMocks } from '~/testing/graphqlMocks';
import { mockedPipelineProgressData } from '~/testing/mock-data/pipeline-progress';
import { HooksCompanyBoardEffect } from '../components/HooksCompanyBoardEffect';
import { BoardContext } from '../states/contexts/BoardContext';
import { CompanyBoardRecoilScopeContext } from '../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
const meta: Meta<typeof CompanyBoardCard> = {
title: 'Modules/Companies/CompanyBoardCard',
component: CompanyBoardCard,
decorators: [
(Story, context) => {
const [, setBoardCardFields] = useRecoilScopedState(
boardCardFieldsScopedState,
context.parameters.customRecoilScopeContext,
);
useEffect(() => {
setBoardCardFields(pipelineAvailableFieldDefinitions);
}, [setBoardCardFields]);
return (
<MemoryRouter>
<ViewScope viewScopeId="company-board-view">
<RecoilScope
CustomRecoilScopeContext={BoardColumnRecoilScopeContext}
>
<BoardContext.Provider
value={{
BoardRecoilScopeContext:
context.parameters.customRecoilScopeContext,
}}
>
<HooksCompanyBoardEffect />
<BoardCardIdContext.Provider
value={mockedPipelineProgressData[1].id}
>
<Story />
</BoardCardIdContext.Provider>
</BoardContext.Provider>
</RecoilScope>
</ViewScope>
</MemoryRouter>
);
},
ComponentWithRecoilScopeDecorator,
ComponentDecorator,
],
args: {},
argTypes: {},
parameters: {
msw: graphqlMocks,
customRecoilScopeContext: CompanyBoardRecoilScopeContext,
},
};
export default meta;
type Story = StoryObj<typeof CompanyBoardCard>;
export const Default: Story = {};

View File

@ -1,38 +1,34 @@
import { Pipeline } from '~/generated/graphql';
import { PipelineStep } from '@/pipeline/types/PipelineStep';
export const pipeline = {
id: 'pipeline-1',
name: 'pipeline-1',
pipelineStages: [
{
id: 'pipeline-stage-1',
name: 'New',
position: 0,
color: 'red',
},
{
id: 'pipeline-stage-2',
name: 'Screening',
position: 1,
color: 'purple',
},
{
id: 'pipeline-stage-3',
name: 'Meeting',
position: 2,
color: 'sky',
},
{
id: 'pipeline-stage-4',
name: 'Proposal',
position: 3,
color: 'turquoise',
},
{
id: 'pipeline-stage-5',
name: 'Customer',
position: 4,
color: 'yellow',
},
],
} as Pipeline;
export const pipelineSteps = [
{
id: 'pipeline-stage-1',
name: 'New',
position: 0,
color: 'red',
},
{
id: 'pipeline-stage-2',
name: 'Screening',
position: 1,
color: 'purple',
},
{
id: 'pipeline-stage-3',
name: 'Meeting',
position: 2,
color: 'sky',
},
{
id: 'pipeline-stage-4',
name: 'Proposal',
position: 3,
color: 'turquoise',
},
{
id: 'pipeline-stage-5',
name: 'Customer',
position: 4,
color: 'yellow',
},
] as PipelineStep[];

View File

@ -1,5 +1,4 @@
import { useState } from 'react';
import { getOperationName } from '@apollo/client/utilities';
import styled from '@emotion/styled';
import { flip, offset, useFloating } from '@floating-ui/react';
import { v4 } from 'uuid';
@ -8,7 +7,6 @@ import {
PeoplePicker,
PersonForSelect,
} from '@/people/components/PeoplePicker';
import { GET_PEOPLE } from '@/people/graphql/queries/getPeople';
import { IconPlus } from '@/ui/display/icon';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
@ -16,10 +14,6 @@ import { DoubleTextInput } from '@/ui/object/field/meta-types/input/components/i
import { FieldDoubleText } from '@/ui/object/field/types/FieldDoubleText';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import {
useInsertOnePersonMutation,
useUpdateOnePersonMutation,
} from '~/generated/graphql';
const StyledContainer = styled.div`
position: static;
@ -56,8 +50,6 @@ export const AddPersonToCompany = ({
}) => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [isCreationDropdownOpen, setIsCreationDropdownOpen] = useState(false);
const [updatePerson] = useUpdateOnePersonMutation();
const [insertOnePerson] = useInsertOnePersonMutation();
const { refs, floatingStyles } = useFloating({
open: isDropdownOpen,
placement: 'right-start',
@ -77,17 +69,17 @@ export const AddPersonToCompany = ({
const handlePersonSelected =
(companyId: string) => async (newPerson: PersonForSelect | null) => {
if (newPerson) {
await updatePerson({
variables: {
where: {
id: newPerson.id,
},
data: {
company: { connect: { id: companyId } },
},
},
refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
});
// await updatePerson({
// variables: {
// where: {
// id: newPerson.id,
// },
// data: {
// company: { connect: { id: companyId } },
// },
// },
// refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
// });
handleClosePicker();
}
};
@ -114,17 +106,17 @@ export const AddPersonToCompany = ({
}: FieldDoubleText) => {
if (!firstValue && !secondValue) return;
const newPersonId = v4();
await insertOnePerson({
variables: {
data: {
company: { connect: { id: companyId } },
id: newPersonId,
firstName: firstValue,
lastName: secondValue,
},
},
refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
});
// await insertOnePerson({
// variables: {
// data: {
// company: { connect: { id: companyId } },
// id: newPersonId,
// firstName: firstValue,
// lastName: secondValue,
// },
// },
// refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
// });
setIsCreationDropdownOpen(false);
};

View File

@ -152,7 +152,7 @@ export const CompanyBoardCard = () => {
const useUpdateOneObjectMutation: () => [(params: any) => any, any] = () => {
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: 'opportunityV2',
objectNameSingular: 'opportunity',
});
const updateEntity = ({
@ -242,7 +242,7 @@ export const CompanyBoardCard = () => {
fieldDefinition: {
fieldMetadataId: viewField.fieldMetadataId,
label: viewField.label,
Icon: viewField.Icon,
iconName: viewField.iconName,
type: viewField.type,
metadata: viewField.metadata,
entityChipDisplayMapper:

View File

@ -18,7 +18,7 @@ export const CompanyChip = ({
}: CompanyChipProps) => (
<EntityChip
entityId={id}
linkToEntity={`/companies/${id}`}
linkToEntity={`/objects/companies/${id}`}
name={name}
avatarType="squared"
pictureUrl={pictureUrl}

View File

@ -33,7 +33,7 @@ export const CompanyPicker = ({
}, [initialSearchFilter, setRelationPickerSearchFilter]);
const { findManyQuery } = useFindOneObjectMetadataItem({
objectNamePlural: 'companiesV2',
objectNamePlural: 'companies',
});
const useFindManyCompanies = (options: any) =>
@ -57,7 +57,7 @@ export const CompanyPicker = ({
originalEntity: company,
}),
selectedIds: companyId ? [companyId] : [],
objectNamePlural: 'companiesV2',
objectNamePlural: 'companies',
});
const handleEntitySelected = async (

View File

@ -73,7 +73,7 @@ export const CompanyProgressPicker = ({
const selectedPipelineStage = useMemo(
() =>
currentPipelineStages.find(
(pipelineStage) => pipelineStage.id === selectedPipelineStageId,
(pipelineStage: any) => pipelineStage.id === selectedPipelineStageId,
),
[currentPipelineStages, selectedPipelineStageId],
);
@ -85,7 +85,7 @@ export const CompanyProgressPicker = ({
>
{isProgressSelectionUnfolded ? (
<DropdownMenuItemsContainer>
{currentPipelineStages.map((pipelineStage, index) => (
{currentPipelineStages.map((pipelineStage: any, index: number) => (
<MenuItem
key={pipelineStage.id}
testId={`select-pipeline-stage-${index}`}

View File

@ -1,7 +1,8 @@
import styled from '@emotion/styled';
import { Company } from '@/companies/types/Company';
import { PeopleCard } from '@/people/components/PeopleCard';
import { Company, useGetPeopleQuery } from '~/generated/graphql';
import { Person } from '@/people/types/Person';
import { AddPersonToCompany } from './AddPersonToCompany';
@ -43,16 +44,19 @@ const StyledTitle = styled.div`
`;
export const CompanyTeam = ({ company }: CompanyTeamProps) => {
const { data } = useGetPeopleQuery({
variables: {
orderBy: [],
where: {
companyId: {
equals: company.id,
},
},
},
});
// const { data } = useGetPeopleQuery({
// variables: {
// orderBy: [],
// where: {
// companyId: {
// equals: company.id,
// },
// },
// },
// });
const data = {
people: [],
};
const peopleIds = data?.people?.map(({ id }) => id);
@ -65,7 +69,7 @@ export const CompanyTeam = ({ company }: CompanyTeamProps) => {
<AddPersonToCompany companyId={company.id} peopleIds={peopleIds} />
</StyledTitleContainer>
<StyledListContainer>
{data?.people?.map((person, id) => (
{data?.people?.map((person: Person, id) => (
<PeopleCard
key={person.id}
person={person}

View File

@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useRecoilState, useRecoilValue } from 'recoil';
import { Company } from '@/companies/types/Company';
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
import { PaginatedObjectTypeResults } from '@/object-record/types/PaginatedObjectTypeResults';
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
@ -13,13 +14,11 @@ import { useBoardContextMenuEntries } from '@/ui/layout/board/hooks/useBoardCont
import { availableBoardCardFieldsScopedState } from '@/ui/layout/board/states/availableBoardCardFieldsScopedState';
import { boardCardFieldsScopedState } from '@/ui/layout/board/states/boardCardFieldsScopedState';
import { isBoardLoadedState } from '@/ui/layout/board/states/isBoardLoadedState';
import { turnFilterIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFilterIntoWhereClause';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
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 { Company } from '~/generated-metadata/graphql';
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
import { useUpdateCompanyBoardCardIds } from '../hooks/useUpdateBoardCardIds';
@ -62,7 +61,7 @@ export const HooksCompanyBoardEffect = () => {
const updateCompanyBoard = useUpdateCompanyBoard();
useFindManyObjectRecords({
objectNamePlural: 'pipelineStepsV2',
objectNamePlural: 'pipelineSteps',
filter: {},
onCompleted: useCallback(
(data: PaginatedObjectTypeResults<PipelineStep>) => {
@ -80,14 +79,14 @@ export const HooksCompanyBoardEffect = () => {
in: pipelineSteps.map((pipelineStep) => pipelineStep.id),
},
},
...(currentViewFilters?.map(turnFilterIntoWhereClause) || []),
...[],
],
};
}, [currentViewFilters, pipelineSteps]) as any;
}, [pipelineSteps]) as any;
useFindManyObjectRecords({
skip: !pipelineSteps.length,
objectNamePlural: 'opportunitiesV2',
objectNamePlural: 'opportunities',
filter: whereFilters,
onCompleted: useCallback(
(_data: PaginatedObjectTypeResults<Opportunity>) => {
@ -104,7 +103,7 @@ export const HooksCompanyBoardEffect = () => {
useFindManyObjectRecords({
skip: !opportunities.length,
objectNamePlural: 'companiesV2',
objectNamePlural: 'companies',
filter: {
id: {
in: opportunities.map((opportuntiy) => opportuntiy.companyId || ''),

View File

@ -9,7 +9,6 @@ import { BoardColumnContext } from '@/ui/layout/board/contexts/BoardColumnContex
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useCreateCompanyProgress } from '../hooks/useCreateCompanyProgress';
import { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompanyQuery';
export const NewCompanyProgressButton = () => {
@ -25,8 +24,6 @@ export const NewCompanyProgressButton = () => {
setHotkeyScopeAndMemorizePreviousScope,
} = usePreviousHotkeyScope();
const createCompanyProgress = useCreateCompanyProgress();
const handleEntitySelect = (company: any) => {
setIsCreatingCard(false);
goBackToPreviousHotkeyScope();
@ -39,7 +36,7 @@ export const NewCompanyProgressButton = () => {
throw new Error('Pipeline stage id is not defined');
}
createCompanyProgress(company.id, pipelineStageId);
//createCompanyProgress(company.id, pipelineStageId);
};
const handleNewClick = useCallback(() => {

View File

@ -1,182 +0,0 @@
import {
IconBrandLinkedin,
IconBrandX,
IconBuildingSkyscraper,
IconCalendarEvent,
IconLink,
IconMap,
IconMoneybag,
IconTarget,
IconUserCircle,
IconUsers,
} from '@/ui/display/icon/index';
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
import {
FieldBooleanMetadata,
FieldChipMetadata,
FieldDateMetadata,
FieldMetadata,
FieldMoneyMetadata,
FieldNumberMetadata,
FieldRelationMetadata,
FieldTextMetadata,
FieldURLMetadata,
} from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
import { User } from '~/generated/graphql';
export const companiesAvailableFieldDefinitions: ColumnDefinition<FieldMetadata>[] =
[
{
fieldMetadataId: 'name',
label: 'Name',
Icon: IconBuildingSkyscraper,
size: 180,
position: 0,
type: 'CHIP',
metadata: {
urlFieldName: 'domainName',
contentFieldName: 'name',
relationType: Entity.Company,
placeHolder: 'Company Name',
},
isVisible: true,
infoTooltipContent: 'The company name.',
basePathToShowPage: '/companies/',
} satisfies ColumnDefinition<FieldChipMetadata>,
{
fieldMetadataId: 'domainName',
label: 'URL',
Icon: IconLink,
size: 100,
position: 1,
type: 'URL',
metadata: {
fieldName: 'domainName',
placeHolder: 'example.com',
},
isVisible: true,
infoTooltipContent:
'The company website URL. We use this url to fetch the company icon.',
} satisfies ColumnDefinition<FieldURLMetadata>,
{
fieldMetadataId: 'accountOwner',
label: 'Account Owner',
Icon: IconUserCircle,
size: 150,
position: 2,
type: 'RELATION',
metadata: {
fieldName: 'accountOwner',
relationType: Entity.User,
},
isVisible: true,
infoTooltipContent:
'Your team member responsible for managing the company account.',
entityChipDisplayMapper: (dataObject: User) => {
return {
name: dataObject?.displayName,
pictureUrl: dataObject?.avatarUrl ?? undefined,
avatarType: 'rounded',
};
},
} satisfies ColumnDefinition<FieldRelationMetadata>,
{
fieldMetadataId: 'createdAt',
label: 'Creation',
Icon: IconCalendarEvent,
size: 150,
position: 3,
type: 'DATE',
metadata: {
fieldName: 'createdAt',
},
isVisible: true,
infoTooltipContent: "Date when the company's record was created.",
} satisfies ColumnDefinition<FieldDateMetadata>,
{
fieldMetadataId: 'employees',
label: 'Employees',
Icon: IconUsers,
size: 150,
position: 4,
type: 'NUMBER',
metadata: {
fieldName: 'employees',
isPositive: true,
placeHolder: 'Employees',
},
isVisible: true,
infoTooltipContent: 'Number of employees in the company.',
} satisfies ColumnDefinition<FieldNumberMetadata>,
{
fieldMetadataId: 'linkedin',
label: 'LinkedIn',
Icon: IconBrandLinkedin,
size: 170,
position: 5,
type: 'URL',
metadata: {
fieldName: 'linkedinUrl',
placeHolder: 'LinkedIn URL',
},
isVisible: true,
infoTooltipContent: 'The company Linkedin account.',
} satisfies ColumnDefinition<FieldURLMetadata>,
{
fieldMetadataId: 'address',
label: 'Address',
Icon: IconMap,
size: 170,
position: 6,
type: 'TEXT',
metadata: {
fieldName: 'address',
placeHolder: 'Address', // Hack: Fake character to prevent password-manager from filling the field
},
isVisible: true,
infoTooltipContent: 'The company address.',
} satisfies ColumnDefinition<FieldTextMetadata>,
{
fieldMetadataId: 'idealCustomerProfile',
label: 'ICP',
Icon: IconTarget,
size: 150,
position: 7,
type: 'BOOLEAN',
metadata: {
fieldName: 'idealCustomerProfile',
},
isVisible: false,
infoTooltipContent:
'Ideal Customer Profile: Indicates whether the company is the most suitable and valuable customer for you.',
} satisfies ColumnDefinition<FieldBooleanMetadata>,
{
fieldMetadataId: 'annualRecurringRevenue',
label: 'ARR',
Icon: IconMoneybag,
size: 150,
position: 8,
type: 'MONEY_AMOUNT',
metadata: {
fieldName: 'annualRecurringRevenue',
placeHolder: 'ARR',
},
infoTooltipContent:
'Annual Recurring Revenue: The actual or estimated annual revenue of the company.',
} satisfies ColumnDefinition<FieldMoneyMetadata>,
{
fieldMetadataId: 'xUrl',
label: 'Twitter',
Icon: IconBrandX,
size: 150,
position: 9,
type: 'URL',
metadata: {
fieldName: 'xUrl',
placeHolder: 'X',
},
isVisible: false,
infoTooltipContent: 'The company Twitter account.',
} satisfies ColumnDefinition<FieldURLMetadata>,
];

View File

@ -1,90 +0,0 @@
import { useEffect, useState } from 'react';
import styled from '@emotion/styled';
import { FieldRecoilScopeContext } from '@/ui/object/record-inline-cell/states/recoil-scope-contexts/FieldRecoilScopeContext';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql';
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
type CompanyNameEditableFieldProps = {
company: Pick<Company, 'id' | 'name'>;
};
const StyledEditableTitleInput = styled.input<{
value: string;
}>`
background: transparent;
border: none;
color: ${({ theme, value }) =>
value ? theme.font.color.primary : theme.font.color.light};
display: flex;
flex-direction: column;
font-size: ${({ theme }) => theme.font.size.xl};
font-weight: ${({ theme }) => theme.font.weight.semiBold};
justify-content: center;
line-height: ${({ theme }) => theme.text.lineHeight.md};
outline: none;
&::placeholder {
color: ${({ theme }) => theme.font.color.light};
}
text-align: center;
width: calc(100% - ${({ theme }) => theme.spacing(2)});
`;
export const CompanyNameEditableField = ({
company,
}: CompanyNameEditableFieldProps) => {
const [internalValue, setInternalValue] = useState(company.name);
const [updateCompany] = useUpdateOneCompanyMutation();
const {
goBackToPreviousHotkeyScope,
setHotkeyScopeAndMemorizePreviousScope,
} = usePreviousHotkeyScope();
useEffect(() => {
setInternalValue(company.name);
}, [company.name]);
const handleChange = async (newValue: string) => {
setInternalValue(newValue);
};
const handleSubmit = async () => {
goBackToPreviousHotkeyScope();
await updateCompany({
variables: {
where: {
id: company.id,
},
data: {
name: internalValue ?? '',
},
},
});
};
const handleFocus = async () => {
setHotkeyScopeAndMemorizePreviousScope(
EditableFieldHotkeyScope.EditableField,
);
};
return (
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
<StyledEditableTitleInput
autoComplete="off"
onChange={(event) => handleChange(event.target.value)}
onBlur={handleSubmit}
onFocus={handleFocus}
value={internalValue}
/>
</RecoilScope>
);
};

View File

@ -1,39 +0,0 @@
import { gql } from '@apollo/client';
export const BASE_COMPANY_FIELDS_FRAGMENT = gql`
fragment baseCompanyFieldsFragment on Company {
address
annualRecurringRevenue
createdAt
domainName
employees
id
idealCustomerProfile
linkedinUrl
name
xUrl
_activityCount
}
`;
export const BASE_ACCOUNT_OWNER_FRAGMENT = gql`
fragment baseAccountOwnerFragment on User {
id
email
displayName
avatarUrl
firstName
lastName
}
`;
export const COMPANY_FIELDS_FRAGMENT = gql`
${BASE_COMPANY_FIELDS_FRAGMENT}
${BASE_ACCOUNT_OWNER_FRAGMENT}
fragment companyFieldsFragment on Company {
accountOwner {
...baseAccountOwnerFragment
}
...baseCompanyFieldsFragment
}
`;

View File

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

View File

@ -1,9 +0,0 @@
import { gql } from '@apollo/client';
export const INSERT_MANY_COMPANY = gql`
mutation InsertManyCompany($data: [CompanyCreateManyInput!]!) {
createManyCompany(data: $data) {
count
}
}
`;

View File

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

View File

@ -1,12 +0,0 @@
import { gql } from '@apollo/client';
export const UPDATE_ONE_COMPANY = gql`
mutation UpdateOneCompany(
$where: CompanyWhereUniqueInput!
$data: CompanyUpdateInput!
) {
updateOneCompany(data: $data, where: $where) {
...companyFieldsFragment
}
}
`;

View File

@ -1,18 +0,0 @@
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,15 +0,0 @@
import { gql } from '@apollo/client';
import { COMPANY_FIELDS_FRAGMENT } from '../fragments/companyFieldsFragment';
export const GET_COMPANIES = gql`
${COMPANY_FIELDS_FRAGMENT}
query GetCompanies(
$orderBy: [CompanyOrderByWithRelationInput!]
$where: CompanyWhereInput
) {
companies: findManyCompany(orderBy: $orderBy, where: $where) {
...companyFieldsFragment
}
}
`;

View File

@ -1,18 +0,0 @@
import { gql } from '@apollo/client';
export const GET_COMPANY = gql`
query GetCompany($where: CompanyWhereUniqueInput!) {
findUniqueCompany(where: $where) {
...companyFieldsFragment
Favorite {
id
person {
id
}
company {
id
}
}
}
}
`;

View File

@ -1,15 +0,0 @@
import { useSetRecoilState } from 'recoil';
import { entityFieldsFamilyState } from '@/ui/object/field/states/entityFieldsFamilyState';
import { useGetCompanyQuery } from '~/generated/graphql';
export const useCompanyQuery = (id: string) => {
const updateCompanyShowPage = useSetRecoilState(entityFieldsFamilyState(id));
return useGetCompanyQuery({
variables: { where: { id } },
onCompleted: (data) => {
updateCompanyShowPage(data?.findUniqueCompany);
},
});
};

View File

@ -1,157 +0,0 @@
import { getOperationName } from '@apollo/client/utilities';
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { useFavorites } from '@/favorites/hooks/useFavorites';
import {
IconCheckbox,
IconHeart,
IconHeartOff,
IconNotes,
IconTrash,
} from '@/ui/display/icon';
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
import { contextMenuEntriesState } from '@/ui/navigation/context-menu/states/contextMenuEntriesState';
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { selectedRowIdsSelector } from '@/ui/object/record-table/states/selectors/selectedRowIdsSelector';
import { tableRowIdsState } from '@/ui/object/record-table/states/tableRowIdsState';
import {
ActivityType,
useDeleteManyCompaniesMutation,
useGetFavoritesQuery,
} from '~/generated/graphql';
import { GET_COMPANY } from '../graphql/queries/getCompany';
import { useCreateActivityForCompany } from './useCreateActivityForCompany';
export const useCompanyTableContextMenuEntries = () => {
const setContextMenuEntries = useSetRecoilState(contextMenuEntriesState);
const setActionBarEntriesState = useSetRecoilState(actionBarEntriesState);
const createActivityForCompany = useCreateActivityForCompany();
const setTableRowIds = useSetRecoilState(tableRowIdsState);
const { resetTableRowSelection } = useRecordTable({
recordTableScopeId: 'companies',
});
const { data } = useGetFavoritesQuery();
const favorites = data?.findFavorites;
const { createFavorite, deleteFavorite } = useFavorites({
objectNamePlural: 'companies',
});
const handleFavoriteButtonClick = useRecoilCallback(({ snapshot }) => () => {
const selectedRowIds = snapshot
.getLoadable(selectedRowIdsSelector)
.getValue();
const selectedCompanyId =
selectedRowIds.length === 1 ? selectedRowIds[0] : '';
const isFavorite =
!!selectedCompanyId &&
!!favorites?.find(
(favorite) => favorite.company?.id === selectedCompanyId,
);
resetTableRowSelection();
if (isFavorite) deleteFavorite(selectedCompanyId);
else createFavorite('company', selectedCompanyId);
});
const [deleteManyCompany] = useDeleteManyCompaniesMutation({
refetchQueries: [getOperationName(GET_COMPANY) ?? ''],
});
const handleDeleteClick = useRecoilCallback(({ snapshot }) => async () => {
const rowIdsToDelete = snapshot
.getLoadable(selectedRowIdsSelector)
.getValue();
resetTableRowSelection();
await deleteManyCompany({
variables: {
ids: rowIdsToDelete,
},
optimisticResponse: {
__typename: 'Mutation',
deleteManyCompany: {
count: rowIdsToDelete.length,
},
},
update: () => {
setTableRowIds((tableRowIds) =>
tableRowIds.filter((id) => !rowIdsToDelete.includes(id)),
);
},
});
});
return {
setContextMenuEntries: useRecoilCallback(({ snapshot }) => () => {
const selectedRowIds = snapshot
.getLoadable(selectedRowIdsSelector)
.getValue();
const selectedCompanyId =
selectedRowIds.length === 1 ? selectedRowIds[0] : '';
const isFavorite =
!!selectedCompanyId &&
!!favorites?.find(
(favorite) => favorite.company?.id === selectedCompanyId,
);
setContextMenuEntries([
{
label: 'New task',
Icon: IconCheckbox,
onClick: () => createActivityForCompany(ActivityType.Task),
},
{
label: 'New note',
Icon: IconNotes,
onClick: () => createActivityForCompany(ActivityType.Note),
},
...(!!selectedCompanyId
? [
{
label: isFavorite
? 'Remove from favorites'
: 'Add to favorites',
Icon: isFavorite ? IconHeartOff : IconHeart,
onClick: () => handleFavoriteButtonClick(),
},
]
: []),
{
label: 'Delete',
Icon: IconTrash,
accent: 'danger',
onClick: () => handleDeleteClick(),
},
]);
}),
setActionBarEntries: useRecoilCallback(() => () => {
setActionBarEntriesState([
{
label: 'Task',
Icon: IconCheckbox,
onClick: () => createActivityForCompany(ActivityType.Task),
},
{
label: 'Note',
Icon: IconNotes,
onClick: () => createActivityForCompany(ActivityType.Note),
},
{
label: 'Delete',
Icon: IconTrash,
accent: 'danger',
onClick: () => handleDeleteClick(),
},
]);
}),
};
};

View File

@ -1,16 +0,0 @@
import { useRecoilCallback } from 'recoil';
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
import { ActivityType } from '@/activities/types/Activity';
export const useCreateActivityForCompany = () => {
const openCreateActivityRightDrawer =
useOpenCreateActivityDrawerForSelectedRowIds();
return useRecoilCallback(
() => (type: ActivityType) => {
openCreateActivityRightDrawer(type, 'Company');
},
[openCreateActivityRightDrawer],
);
};

View File

@ -1,38 +0,0 @@
import { useRecoilCallback, useRecoilState } from 'recoil';
import { v4 } from 'uuid';
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';
export const useCreateCompanyProgress = () => {
const { createOneObject: createOneOpportunity } =
useCreateOneObjectRecord<Opportunity>({
objectNameSingular: 'opportunityV2',
});
const [currentPipeline] = useRecoilState(currentPipelineState);
return useRecoilCallback(
({ set }) =>
async (companyId: string, pipelineStageId: string) => {
if (!currentPipeline?.id) {
throw new Error('Pipeline not found');
}
const newUuid = v4();
set(boardCardIdsByColumnIdFamilyState(pipelineStageId), (oldValue) => [
...oldValue,
newUuid,
]);
await createOneOpportunity?.({
pipelineStepId: pipelineStageId,
companyId: companyId,
});
},
[createOneOpportunity, currentPipeline?.id],
);
};

View File

@ -1,6 +1,8 @@
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { useSearchCompanyQuery } from '~/generated/graphql';
import { getLogoUrlFromDomainName } from '~/utils';
import { useQuery } from '@apollo/client';
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2';
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
export const useFilteredSearchCompanyQuery = ({
searchFilter,
@ -11,25 +13,32 @@ export const useFilteredSearchCompanyQuery = ({
selectedIds?: string[];
limit?: number;
}) => {
return useFilteredSearchEntityQuery({
queryHook: useSearchCompanyQuery,
const { findManyQuery } = useFindOneObjectMetadataItem({
objectNameSingular: 'company',
});
const useFindManyCompanies = (options: any) =>
useQuery(findManyQuery, options);
return useFilteredSearchEntityQueryV2({
queryHook: useFindManyCompanies,
filters: [
{
fieldNames: ['name'],
fieldNames: ['name.firstName', 'name.lastName'],
filter: searchFilter,
},
],
orderByField: 'name',
orderByField: 'createdAt',
mappingFunction: (company) => ({
entityType: Entity.Company,
id: company.id,
entityType: 'Company',
name: company.name,
avatarUrl: getLogoUrlFromDomainName(company.domainName),
domainName: company.domainName,
avatarType: 'squared',
avatarUrl: '',
originalEntity: company,
}),
selectedIds: selectedIds,
objectNamePlural: 'workspaceMembers',
limit,
});
};

View File

@ -1,59 +0,0 @@
import { v4 as uuidv4 } from 'uuid';
import { useSpreadsheetImport } from '@/spreadsheet-import/hooks/useSpreadsheetImport';
import { SpreadsheetOptions } from '@/spreadsheet-import/types';
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
import { useInsertManyCompanyMutation } from '~/generated/graphql';
import { fieldsForCompany } from '../utils/fieldsForCompany';
export type FieldCompanyMapping = (typeof fieldsForCompany)[number]['key'];
export const useSpreadsheetCompanyImport = () => {
const { openSpreadsheetImport } = useSpreadsheetImport<FieldCompanyMapping>();
const { enqueueSnackBar } = useSnackBar();
const [createManyCompany] = useInsertManyCompanyMutation();
const openCompanySpreadsheetImport = (
options?: Omit<
SpreadsheetOptions<FieldCompanyMapping>,
'fields' | 'isOpen' | 'onClose'
>,
) => {
openSpreadsheetImport({
...options,
onSubmit: async (data) => {
// TODO: Add better type checking in spreadsheet import later
const createInputs = data.validData.map((company) => ({
id: uuidv4(),
name: (company.name ?? '') as string,
domainName: (company.domainName ?? '') as string,
address: (company.address ?? '') as string,
employees: parseInt((company.employees ?? '') as string, 10),
linkedinUrl: (company.linkedinUrl ?? '') as string | undefined,
}));
try {
const result = await createManyCompany({
variables: {
data: createInputs,
},
refetchQueries: 'active',
});
if (result.errors) {
throw result.errors;
}
} catch (error: any) {
enqueueSnackBar(error?.message || 'Something went wrong', {
variant: 'error',
});
}
},
fields: fieldsForCompany,
});
};
return { openCompanySpreadsheetImport };
};

View File

@ -1,149 +0,0 @@
import styled from '@emotion/styled';
import { useRecoilCallback } from 'recoil';
import { companiesAvailableFieldDefinitions } from '@/companies/constants/companiesAvailableFieldDefinitions';
import { getCompaniesOptimisticEffectDefinition } from '@/companies/graphql/optimistic-effect-definitions/getCompaniesOptimisticEffectDefinition';
import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyTableContextMenuEntries';
import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport';
import { RecordTableEffect } from '@/ui/object/record-table/components/RecordTableEffect';
import { RecordTableV1 } from '@/ui/object/record-table/components/RecordTableV1';
import { TableOptionsDropdownId } from '@/ui/object/record-table/constants/TableOptionsDropdownId';
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { TableOptionsDropdown } from '@/ui/object/record-table/options/components/TableOptionsDropdown';
import { RecordTableScope } from '@/ui/object/record-table/scopes/RecordTableScope';
import { ViewBar } from '@/views/components/ViewBar';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { useView } from '@/views/hooks/useView';
import { ViewScope } from '@/views/scopes/ViewScope';
import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField';
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
import {
UpdateOneCompanyMutationVariables,
useGetCompaniesQuery,
useGetWorkspaceMembersLazyQuery,
useUpdateOneCompanyMutation,
} from '~/generated/graphql';
import { companyTableFilterDefinitions } from '~/pages/companies/constants/companyTableFilterDefinitions';
import { companyTableSortDefinitions } from '~/pages/companies/constants/companyTableSortDefinitions';
import CompanyTableEffect from './CompanyTableEffect';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
overflow: auto;
`;
export const CompanyTable = () => {
const viewScopeId = 'company-table-view';
const tableScopeId = 'companies';
const {
setTableFilters,
setTableSorts,
setTableColumns,
upsertRecordTableItem,
} = useRecordTable({
recordTableScopeId: tableScopeId,
});
const [updateEntityMutation] = useUpdateOneCompanyMutation();
const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery();
const { persistViewFields } = useViewFields(viewScopeId);
const { setEntityCountInCurrentView } = useView({ viewScopeId });
const { setContextMenuEntries, setActionBarEntries } =
useCompanyTableContextMenuEntries();
const updateCompany = async (
variables: UpdateOneCompanyMutationVariables,
) => {
if (variables.data.accountOwner?.connect?.id) {
const workspaceMemberAccountOwner = (
await getWorkspaceMember({
variables: {
where: {
userId: { equals: variables.data.accountOwner.connect?.id },
},
},
})
).data?.workspaceMembers?.[0];
variables.data.workspaceMemberAccountOwner = {
connect: { id: workspaceMemberAccountOwner?.id },
};
}
updateEntityMutation({
variables: variables,
onCompleted: (data) => {
if (!data.updateOneCompany) {
return;
}
upsertRecordTableItem(data.updateOneCompany);
},
});
};
const { openCompanySpreadsheetImport: onImport } =
useSpreadsheetCompanyImport();
return (
<ViewScope
viewScopeId={viewScopeId}
onViewFieldsChange={(viewFields) => {
setTableColumns(
mapViewFieldsToColumnDefinitions(
viewFields,
companiesAvailableFieldDefinitions,
),
);
}}
onViewFiltersChange={(viewFilters) => {
setTableFilters(mapViewFiltersToFilters(viewFilters));
}}
onViewSortsChange={(viewSorts) => {
setTableSorts(mapViewSortsToSorts(viewSorts));
}}
>
<StyledContainer>
<RecordTableScope
recordTableScopeId={tableScopeId}
onColumnsChange={useRecoilCallback(() => (columns) => {
persistViewFields(mapColumnDefinitionsToViewFields(columns));
})}
onEntityCountChange={useRecoilCallback(() => (entityCount) => {
setEntityCountInCurrentView(entityCount);
})}
>
<ViewBar
optionsDropdownButton={<TableOptionsDropdown onImport={onImport} />}
optionsDropdownScopeId={TableOptionsDropdownId}
/>
<CompanyTableEffect />
<RecordTableEffect
getRequestResultKey="companies"
useGetRequest={useGetCompaniesQuery}
getRequestOptimisticEffectDefinition={
getCompaniesOptimisticEffectDefinition
}
filterDefinitionArray={companyTableFilterDefinitions}
sortDefinitionArray={companyTableSortDefinitions}
setContextMenuEntries={setContextMenuEntries}
setActionBarEntries={setActionBarEntries}
/>
<RecordTableV1
updateEntityMutation={({
variables,
}: {
variables: UpdateOneCompanyMutationVariables;
}) => updateCompany(variables)}
/>
</RecordTableScope>
</StyledContainer>
</ViewScope>
);
};

View File

@ -1,41 +0,0 @@
import { useEffect } from 'react';
import { companiesAvailableFieldDefinitions } from '@/companies/constants/companiesAvailableFieldDefinitions';
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { useView } from '@/views/hooks/useView';
import { ViewType } from '@/views/types/ViewType';
import { companyTableFilterDefinitions } from '~/pages/companies/constants/companyTableFilterDefinitions';
import { companyTableSortDefinitions } from '~/pages/companies/constants/companyTableSortDefinitions';
const CompanyTableEffect = () => {
const {
setAvailableSortDefinitions,
setAvailableFilterDefinitions,
setAvailableFieldDefinitions,
setViewType,
setViewObjectMetadataId,
} = useView();
const { setAvailableTableColumns } = useRecordTable();
useEffect(() => {
setAvailableSortDefinitions?.(companyTableSortDefinitions);
setAvailableFilterDefinitions?.(companyTableFilterDefinitions);
setAvailableFieldDefinitions?.(companiesAvailableFieldDefinitions);
setViewObjectMetadataId?.('company');
setViewType?.(ViewType.Table);
setAvailableTableColumns(companiesAvailableFieldDefinitions);
}, [
setAvailableFieldDefinitions,
setAvailableFilterDefinitions,
setAvailableSortDefinitions,
setAvailableTableColumns,
setViewObjectMetadataId,
setViewType,
]);
return <></>;
};
export default CompanyTableEffect;

View File

@ -1,17 +0,0 @@
import { useEffect } from 'react';
import { companiesAvailableFieldDefinitions } from '@/companies/constants/companiesAvailableFieldDefinitions';
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { mockedCompaniesData } from './companies-mock-data';
export const CompanyTableMockDataEffect = () => {
const { setRecordTableData, setTableColumns } = useRecordTable();
useEffect(() => {
setRecordTableData(mockedCompaniesData);
setTableColumns(companiesAvailableFieldDefinitions);
}, [setRecordTableData, setTableColumns]);
return <></>;
};

View File

@ -1,42 +0,0 @@
import styled from '@emotion/styled';
import { RecordTable } from '@/ui/object/record-table/components/RecordTable';
import { TableOptionsDropdownId } from '@/ui/object/record-table/constants/TableOptionsDropdownId';
import { TableOptionsDropdown } from '@/ui/object/record-table/options/components/TableOptionsDropdown';
import { RecordTableScope } from '@/ui/object/record-table/scopes/RecordTableScope';
import { ViewBar } from '@/views/components/ViewBar';
import { ViewScope } from '@/views/scopes/ViewScope';
import { useUpdateOneCompanyMutation } from '~/generated/graphql';
import CompanyTableEffect from './CompanyTableEffect';
import { CompanyTableMockDataEffect } from './CompanyTableMockDataEffect';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
overflow: auto;
`;
export const CompanyTableMockMode = () => {
return (
<StyledContainer>
<ViewScope viewScopeId="company-table-mock-mode">
<RecordTableScope
recordTableScopeId="company-table-mock-mode-table"
onColumnsChange={() => {}}
onEntityCountChange={() => {}}
>
<CompanyTableEffect />
<CompanyTableMockDataEffect />
<ViewBar
optionsDropdownButton={<TableOptionsDropdown />}
optionsDropdownScopeId={TableOptionsDropdownId}
/>
<RecordTable updateEntityMutation={useUpdateOneCompanyMutation} />
</RecordTableScope>
</ViewScope>
</StyledContainer>
);
};

View File

@ -1,112 +0,0 @@
import { Favorite } from '@/favorites/types/Favorite';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { Company } from '../../../../generated/graphql';
type MockedCompany = Pick<
Company,
| 'id'
| 'name'
| 'domainName'
| '__typename'
| 'createdAt'
| 'address'
| 'employees'
| 'linkedinUrl'
| 'xUrl'
| 'annualRecurringRevenue'
| 'idealCustomerProfile'
| '_activityCount'
> & {
accountOwner: Pick<WorkspaceMember, 'id' | 'avatarUrl' | 'name'> | null;
} & { Favorite: Pick<Favorite, 'id'> | null };
export const mockedCompaniesData: Array<MockedCompany> = [
{
id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
domainName: 'airbnb.com',
name: 'Airbnb',
linkedinUrl: 'https://www.linkedin.com/company/airbnb/',
xUrl: 'https://twitter.com/airbnb',
annualRecurringRevenue: 500000,
idealCustomerProfile: true,
createdAt: '2023-04-26T10:08:54.724515+00:00',
address: 'San Francisco, CA',
employees: 5000,
Favorite: null,
_activityCount: 0,
accountOwner: {
name: {
firstName: 'Charles',
lastName: 'Test',
},
avatarUrl: null,
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
},
__typename: 'Company',
},
{
id: 'b396e6b9-dc5c-4643-bcff-61b6cf7523ae',
domainName: 'qonto.com',
name: 'Qonto',
linkedinUrl: 'https://www.linkedin.com/company/qonto/',
xUrl: 'https://twitter.com/qonto',
annualRecurringRevenue: 500000,
idealCustomerProfile: false,
createdAt: '2023-04-26T10:12:42.33625+00:00',
address: 'Paris, France',
employees: 800,
Favorite: null,
_activityCount: 0,
accountOwner: null,
__typename: 'Company',
},
{
id: 'a674fa6c-1455-4c57-afaf-dd5dc086361d',
domainName: 'stripe.com',
name: 'Stripe',
linkedinUrl: 'https://www.linkedin.com/company/stripe/',
xUrl: 'https://twitter.com/stripe',
annualRecurringRevenue: 5000000,
idealCustomerProfile: false,
createdAt: '2023-04-26T10:10:32.530184+00:00',
address: 'San Francisco, CA',
employees: 8000,
Favorite: null,
_activityCount: 0,
accountOwner: null,
__typename: 'Company',
},
{
id: 'b1cfd51b-a831-455f-ba07-4e30671e1dc3',
domainName: 'figma.com',
linkedinUrl: 'https://www.linkedin.com/company/figma/',
xUrl: 'https://twitter.com/figma',
annualRecurringRevenue: 50000,
idealCustomerProfile: true,
name: 'Figma',
createdAt: '2023-03-21T06:30:25.39474+00:00',
address: 'San Francisco, CA',
employees: 800,
Favorite: null,
_activityCount: 0,
accountOwner: null,
__typename: 'Company',
},
{
id: '5c21e19e-e049-4393-8c09-3e3f8fb09ecb',
domainName: 'notion.com',
linkedinUrl: 'https://www.linkedin.com/company/notion/',
xUrl: 'https://twitter.com/notion',
annualRecurringRevenue: 500000,
idealCustomerProfile: false,
name: 'Notion',
createdAt: '2023-04-26T10:13:29.712485+00:00',
address: 'San Francisco, CA',
employees: 400,
Favorite: null,
_activityCount: 0,
accountOwner: null,
__typename: 'Company',
},
];

View File

@ -0,0 +1,15 @@
export type Company = {
id: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
name: string;
domainName: string;
address: string;
accountOwnerId: string | null;
linkedinUrl: { url: string; label: string };
xUrl: { url: string; label: string };
annualRecurringRevenue: { amountMicros: number | null; currencyCode: string };
employees: number | null;
idealCustomerProfile: boolean;
};

View File

@ -1,5 +1,5 @@
import { Company } from '@/companies/types/Company';
import { Opportunity } from '@/pipeline/types/Opportunity';
import { Company } from '~/generated/graphql';
export type CompanyForBoard = Pick<Company, 'id' | 'name' | 'domainName'>;
export type PipelineProgressForBoard = Opportunity;

View File

@ -18,7 +18,7 @@ const StyledContainer = styled.div`
export const Favorites = () => {
// This is only temporary and will be refactored once we have main identifiers
const { favorites, handleReorderFavorite } = useFavorites({
objectNamePlural: 'companiesV2',
objectNamePlural: 'companies',
});
if (!favorites || favorites.length === 0) return <></>;

View File

@ -1,9 +0,0 @@
import { gql } from '@apollo/client';
export const DELETE_FAVORITE = gql`
mutation DeleteFavorite($where: FavoriteWhereInput!) {
deleteFavorite(where: $where) {
id
}
}
`;

View File

@ -1,14 +0,0 @@
import { gql } from '@apollo/client';
export const INSERT_COMPANY_FAVORITE = gql`
mutation InsertCompanyFavorite($data: FavoriteMutationForCompanyArgs!) {
createFavoriteForCompany(data: $data) {
id
company {
id
name
domainName
}
}
}
`;

View File

@ -1,15 +0,0 @@
import { gql } from '@apollo/client';
export const INSERT_PERSON_FAVORITE = gql`
mutation InsertPersonFavorite($data: FavoriteMutationForPersonArgs!) {
createFavoriteForPerson(data: $data) {
id
person {
id
firstName
lastName
displayName
}
}
}
`;

View File

@ -1,28 +0,0 @@
import { gql } from '@apollo/client';
export const UPDATE_FAVORITE = gql`
mutation UpdateOneFavorite(
$data: FavoriteUpdateInput!
$where: FavoriteWhereUniqueInput!
) {
updateOneFavorites(data: $data, where: $where) {
id
person {
id
firstName
lastName
avatarUrl
}
company {
id
name
domainName
accountOwner {
id
displayName
avatarUrl
}
}
}
}
`;

View File

@ -1,26 +0,0 @@
import { gql } from '@apollo/client';
export const GET_FAVORITES = gql`
query GetFavorites {
findFavorites {
id
position
person {
id
firstName
lastName
avatarUrl
}
company {
id
name
domainName
accountOwner {
id
displayName
avatarUrl
}
}
}
}
`;

View File

@ -4,12 +4,12 @@ import { OnDragEndResponder } from '@hello-pangea/dnd';
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { Company } from '@/companies/types/Company';
import { Favorite } from '@/favorites/types/Favorite';
import { mapFavorites } from '@/favorites/utils/mapFavorites';
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
import { PaginatedObjectTypeResults } from '@/object-record/types/PaginatedObjectTypeResults';
import { Company } from '~/generated/graphql';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { favoritesState } from '../states/favoritesState';
@ -25,7 +25,7 @@ export const useFavorites = ({
const { updateOneMutation, createOneMutation, deleteOneMutation } =
useFindOneObjectMetadataItem({
objectNamePlural: 'favoritesV2',
objectNamePlural: 'favorites',
});
const { foundObjectMetadataItem: favoriteTargetObjectMetadataItem } =
@ -44,7 +44,7 @@ export const useFavorites = ({
// This is only temporary and will be refactored once we have main identifiers
const { loading: companiesLoading } = useFindManyObjectRecords({
objectNamePlural: 'companiesV2',
objectNamePlural: 'companies',
onCompleted: async (
data: PaginatedObjectTypeResults<Required<Company>>,
) => {
@ -64,7 +64,7 @@ export const useFavorites = ({
});
const { loading: peopleLoading } = useFindManyObjectRecords({
objectNamePlural: 'peopleV2',
objectNamePlural: 'people',
onCompleted: async (data) => {
setAllPeople(
data.edges.reduce(
@ -84,7 +84,7 @@ export const useFavorites = ({
useFindManyObjectRecords({
skip: companiesLoading || peopleLoading,
objectNamePlural: 'favoritesV2',
objectNamePlural: 'favorites',
onCompleted: useRecoilCallback(
({ snapshot, set }) =>
async (data: PaginatedObjectTypeResults<Required<Favorite>>) => {
@ -112,8 +112,7 @@ export const useFavorites = ({
const favorites = snapshot.getLoadable(favoritesState).getValue();
const targetObjectName =
favoriteTargetObjectMetadataItem?.nameSingular.replace('V2', '') ??
'';
favoriteTargetObjectMetadataItem?.nameSingular ?? '';
const result = await apolloClient.mutate({
mutation: createOneMutation,

View File

@ -1,4 +1,5 @@
import { Company, Person } from '~/generated/graphql';
import { Company } from '@/companies/types/Company';
import { Person } from '@/people/types/Person';
import { getLogoUrlFromDomainName } from '~/utils';
import { assertNotNull } from '~/utils/assert';
import { isDefined } from '~/utils/isDefined';
@ -7,8 +8,8 @@ export const mapFavorites = (
favorites: any,
recordsDict: {
[key: string]: {
firstName?: Person['firstName'];
lastName?: Person['lastName'];
firstName?: Person['name']['firstName'];
lastName?: Person['name']['lastName'];
avatarUrl?: Person['avatarUrl'];
name?: Company['name'];
domainName?: Company['domainName'];

View File

@ -0,0 +1,9 @@
import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems';
export const ObjectMetadataItemsProvider = ({
children,
}: React.PropsWithChildren) => {
const { loading } = useFindManyObjectMetadataItems();
return loading ? <></> : <>{children}</>;
};

View File

@ -1,28 +1,19 @@
import { useNavigate } from 'react-router-dom';
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
import { Icon123 } from '@/ui/input/constants/icons';
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
import NavItem from '@/ui/navigation/navbar/components/NavItem';
import { useFindManyObjectMetadataItems } from '../hooks/useFindManyObjectMetadataItems';
export const ObjectMetadataNavItems = () => {
const { objectMetadataItems } = useFindManyObjectMetadataItems({
objectFilter: {
isSystem: { is: false },
},
fieldFilter: {
isSystem: { is: false },
},
});
const { activeObjectMetadataItems } = useObjectMetadataItemForSettings();
const navigate = useNavigate();
const { icons } = useLazyLoadIcons();
return (
<>
{objectMetadataItems.map((objectMetadataItem) => {
if (objectMetadataItem.nameSingular === 'opportunityV2') return null;
{activeObjectMetadataItems.map((objectMetadataItem) => {
if (objectMetadataItem.nameSingular === 'opportunity') return null;
return (
<NavItem
key={objectMetadataItem.id}

View File

@ -0,0 +1,46 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
import { formatFieldMetadataItemAsColumnDefinition } from '../utils/formatFieldMetadataItemAsColumnDefinition';
import { formatFieldMetadataItemsAsFilterDefinitions } from '../utils/formatFieldMetadataItemsAsFilterDefinitions';
import { formatFieldMetadataItemsAsSortDefinitions } from '../utils/formatFieldMetadataItemsAsSortDefinitions';
export const useComputeDefinitionsFromFieldMetadata = (
objectMetadataItem?: ObjectMetadataItem,
) => {
if (!objectMetadataItem) {
return {
columnDefinitions: [],
filterDefinitions: [],
sortDefinitions: [],
};
}
const activeFieldMetadataItems = objectMetadataItem.fields.filter(
({ isActive }) => isActive,
);
const columnDefinitions: ColumnDefinition<FieldMetadata>[] =
activeFieldMetadataItems.map((field, index) =>
formatFieldMetadataItemAsColumnDefinition({
position: index,
field,
objectMetadataItem: objectMetadataItem,
}),
);
const filterDefinitions = formatFieldMetadataItemsAsFilterDefinitions({
fields: activeFieldMetadataItems,
});
const sortDefinitions = formatFieldMetadataItemsAsSortDefinitions({
fields: activeFieldMetadataItems,
});
return {
columnDefinitions,
filterDefinitions,
sortDefinitions,
};
};

View File

@ -5,14 +5,8 @@ import { useGenerateDeleteOneObjectMutation } from '@/object-record/utils/useGen
import { useGenerateFindManyCustomObjectsQuery } from '@/object-record/utils/useGenerateFindManyCustomObjectsQuery';
import { useGenerateFindOneCustomObjectQuery } from '@/object-record/utils/useGenerateFindOneCustomObjectQuery';
import { useGenerateUpdateOneObjectMutation } from '@/object-record/utils/useGenerateUpdateOneObjectMutation';
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
import { ObjectMetadataItemIdentifier } from '../types/ObjectMetadataItemIdentifier';
import { formatFieldMetadataItemAsColumnDefinition } from '../utils/formatFieldMetadataItemAsColumnDefinition';
import { formatFieldMetadataItemsAsFilterDefinitions } from '../utils/formatFieldMetadataItemsAsFilterDefinitions';
import { formatFieldMetadataItemsAsSortDefinitions } from '../utils/formatFieldMetadataItemsAsSortDefinitions';
import { useFindManyObjectMetadataItems } from './useFindManyObjectMetadataItems';
@ -43,37 +37,10 @@ export const useFindOneObjectMetadataItem = ({
object.nameSingular === objectNameSingular,
);
const { icons } = useLazyLoadIcons();
const objectNotFoundInMetadata =
objectMetadataItems.length === 0 ||
(objectMetadataItems.length > 0 && !foundObjectMetadataItem);
const activeFields =
foundObjectMetadataItem?.fields.filter(({ isActive }) => isActive) ?? [];
const columnDefinitions: ColumnDefinition<FieldMetadata>[] =
foundObjectMetadataItem
? activeFields.map((field, index) =>
formatFieldMetadataItemAsColumnDefinition({
position: index,
field,
objectMetadataItem: foundObjectMetadataItem,
icons,
}),
)
: [];
const filterDefinitions = formatFieldMetadataItemsAsFilterDefinitions({
fields: activeFields,
icons,
});
const sortDefinitions = formatFieldMetadataItemsAsSortDefinitions({
fields: activeFields,
icons,
});
const findManyQuery = useGenerateFindManyCustomObjectsQuery({
objectMetadataItem: foundObjectMetadataItem,
});
@ -97,9 +64,6 @@ export const useFindOneObjectMetadataItem = ({
return {
foundObjectMetadataItem,
objectNotFoundInMetadata,
columnDefinitions,
filterDefinitions,
sortDefinitions,
findManyQuery,
findOneQuery,
createOneMutation,

View File

@ -8,20 +8,13 @@ import { useFindManyObjectMetadataItems } from './useFindManyObjectMetadataItems
import { useUpdateOneObjectMetadataItem } from './useUpdateOneObjectMetadataItem';
export const useObjectMetadataItemForSettings = () => {
const { objectMetadataItems, loading } = useFindManyObjectMetadataItems({
objectFilter: {
isSystem: { is: false },
},
fieldFilter: {
isSystem: { is: false },
},
});
const { objectMetadataItems, loading } = useFindManyObjectMetadataItems();
const activeObjectMetadataItems = objectMetadataItems.filter(
({ isActive }) => isActive,
({ isActive, isSystem }) => isActive && !isSystem,
);
const disabledObjectMetadataItems = objectMetadataItems.filter(
({ isActive }) => !isActive,
({ isActive, isSystem }) => !isActive && !isSystem,
);
const findActiveObjectMetadataItemBySlug = (slug: string) =>

View File

@ -1,11 +0,0 @@
import { atom } from 'recoil';
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
/**
* @deprecated Use `useFindManyObjectMetadataItems` instead.
*/
export const objectMetadataItemsState = atom<ObjectMetadataItem[]>({
key: 'objectMetadataItemsState',
default: [],
});

View File

@ -1,12 +0,0 @@
import { selector } from 'recoil';
import { ObjectMetadataItem } from '../../types/ObjectMetadataItem';
import { objectMetadataItemsState } from '../objectMetadataItemsState';
export const activeObjectMetadataItemsSelector = selector<ObjectMetadataItem[]>(
{
key: 'activeObjectMetadataItemsSelector',
get: ({ get }) =>
get(objectMetadataItemsState).filter(({ isActive }) => isActive),
},
);

View File

@ -1,12 +0,0 @@
import { selector } from 'recoil';
import { ObjectMetadataItem } from '../../types/ObjectMetadataItem';
import { objectMetadataItemsState } from '../objectMetadataItemsState';
export const disabledObjectMetadataItemsSelector = selector<
ObjectMetadataItem[]
>({
key: 'disabledObjectMetadataItemsSelector',
get: ({ get }) =>
get(objectMetadataItemsState).filter(({ isActive }) => !isActive),
});

View File

@ -1,5 +1,4 @@
import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelationType';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
@ -12,12 +11,10 @@ export const formatFieldMetadataItemAsColumnDefinition = ({
position,
field,
objectMetadataItem,
icons,
}: {
position: number;
field: FieldMetadataItem;
objectMetadataItem: Omit<ObjectMetadataItem, 'fields'>;
icons: Record<string, IconComponent>;
}): ColumnDefinition<FieldMetadata> => ({
position,
fieldMetadataId: field.id,
@ -28,7 +25,7 @@ export const formatFieldMetadataItemAsColumnDefinition = ({
fieldName: field.name,
placeHolder: field.label,
},
Icon: icons[field.icon ?? 'Icon123'],
iconName: field.icon ?? 'Icon123',
isVisible: true,
basePathToShowPage: `/object/${objectMetadataItem.nameSingular}/`,
relationType: parseFieldRelationType(field),

View File

@ -5,10 +5,8 @@ import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
export const formatFieldMetadataItemsAsFilterDefinitions = ({
fields,
icons,
}: {
fields: Array<ObjectMetadataItem['fields'][0]>;
icons: Record<string, any>;
}): FilterDefinition[] =>
fields.reduce((acc, field) => {
if (
@ -20,22 +18,17 @@ export const formatFieldMetadataItemsAsFilterDefinitions = ({
) {
return acc;
}
return [
...acc,
formatFieldMetadataItemAsFilterDefinition({ field, icons }),
];
return [...acc, formatFieldMetadataItemAsFilterDefinition({ field })];
}, [] as FilterDefinition[]);
const formatFieldMetadataItemAsFilterDefinition = ({
field,
icons,
}: {
field: ObjectMetadataItem['fields'][0];
icons: Record<string, any>;
}): FilterDefinition => ({
fieldMetadataId: field.id,
label: field.label,
Icon: icons[field.icon ?? 'Icon123'],
iconName: field.icon ?? 'Icon123',
type:
field.type === FieldMetadataType.Date
? 'DATE'

View File

@ -5,10 +5,8 @@ import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
export const formatFieldMetadataItemsAsSortDefinitions = ({
fields,
icons,
}: {
fields: Array<ObjectMetadataItem['fields'][0]>;
icons: Record<string, any>;
}): SortDefinition[] =>
fields.reduce((acc, field) => {
if (
@ -27,7 +25,7 @@ export const formatFieldMetadataItemsAsSortDefinitions = ({
{
fieldMetadataId: field.id,
label: field.label,
Icon: icons[field.icon ?? 'Icon123'],
iconName: field.icon ?? 'Icon123',
},
];
}, [] as SortDefinition[]);

View File

@ -7,7 +7,6 @@ import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOne
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { filterAvailableFieldMetadataItem } from '@/object-record/utils/filterAvailableFieldMetadataItem';
import { IconBuildingSkyscraper } from '@/ui/display/icon';
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
import { PageBody } from '@/ui/layout/page/PageBody';
import { PageContainer } from '@/ui/layout/page/PageContainer';
import { PageFavoriteButton } from '@/ui/layout/page/PageFavoriteButton';
@ -36,8 +35,6 @@ export const RecordShowPage = () => {
objectMetadataId: string;
}>();
const { icons } = useLazyLoadIcons();
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
objectNameSingular,
});
@ -91,7 +88,7 @@ export const RecordShowPage = () => {
if (isFavorite) deleteFavorite(object?.id);
else {
const additionalData =
objectNameSingular === 'personV2'
objectNameSingular === 'person'
? {
labelIdentifier:
object.name.firstName + ' ' + object.name.lastName,
@ -100,7 +97,7 @@ export const RecordShowPage = () => {
link: `/object/personV2/${object.id}`,
recordId: object.id,
}
: objectNameSingular === 'companyV2'
: objectNameSingular === 'company'
? {
labelIdentifier: object.name,
avatarUrl: getLogoUrlFromDomainName(object.domainName ?? ''),
@ -116,7 +113,7 @@ export const RecordShowPage = () => {
if (!object) return <></>;
const pageName =
objectNameSingular === 'personV2'
objectNameSingular === 'person'
? object.name.firstName + ' ' + object.name.lastName
: object.name;
@ -173,7 +170,6 @@ export const RecordShowPage = () => {
field: metadataField,
position: index,
objectMetadataItem: foundObjectMetadataItem,
icons,
}),
useUpdateEntityMutation: useUpdateOneObjectMutation,
hotkeyScope: InlineCellHotkeyScope.InlineCell,

View File

@ -1,6 +1,7 @@
import styled from '@emotion/styled';
import { useRecoilCallback } from 'recoil';
import { useComputeDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useComputeDefinitionsFromFieldMetadata';
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { RecordTable } from '@/ui/object/record-table/components/RecordTable';
import { TableOptionsDropdownId } from '@/ui/object/record-table/constants/TableOptionsDropdownId';
@ -9,7 +10,6 @@ import { TableOptionsDropdown } from '@/ui/object/record-table/options/component
import { RecordTableScope } from '@/ui/object/record-table/scopes/RecordTableScope';
import { ViewBar } from '@/views/components/ViewBar';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { useView } from '@/views/hooks/useView';
import { ViewScope } from '@/views/scopes/ViewScope';
import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField';
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
@ -32,10 +32,12 @@ export const RecordTableContainer = ({
}: {
objectNamePlural: string;
}) => {
const { columnDefinitions, foundObjectMetadataItem } =
useFindOneObjectMetadataItem({
objectNamePlural,
});
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
objectNamePlural,
});
const { columnDefinitions } = useComputeDefinitionsFromFieldMetadata(
foundObjectMetadataItem,
);
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: foundObjectMetadataItem?.nameSingular,
@ -50,8 +52,6 @@ export const RecordTableContainer = ({
recordTableScopeId: tableScopeId,
});
const { setEntityCountInCurrentView } = useView({ viewScopeId });
const updateEntity = ({
variables,
}: {
@ -89,9 +89,6 @@ export const RecordTableContainer = ({
onColumnsChange={useRecoilCallback(() => (columns) => {
persistViewFields(mapColumnDefinitionsToViewFields(columns));
})}
onEntityCountChange={(entityCount) => {
setEntityCountInCurrentView(entityCount);
}}
>
<ViewBar
optionsDropdownButton={<TableOptionsDropdown />}

View File

@ -1,5 +1,6 @@
import { useEffect } from 'react';
import { useComputeDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useComputeDefinitionsFromFieldMetadata';
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { useRecordTableContextMenuEntries } from '@/object-record/hooks/useRecordTableContextMenuEntries';
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
@ -8,24 +9,26 @@ import { useView } from '@/views/hooks/useView';
import { ViewType } from '@/views/types/ViewType';
export const RecordTableEffect = () => {
const { scopeId: objectNamePlural, setAvailableTableColumns } =
useRecordTable();
const {
columnDefinitions,
filterDefinitions,
sortDefinitions,
foundObjectMetadataItem,
} = useFindOneObjectMetadataItem({
scopeId: objectNamePlural,
setAvailableTableColumns,
setOnEntityCountChange,
} = useRecordTable();
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
objectNamePlural,
});
const { columnDefinitions, filterDefinitions, sortDefinitions } =
useComputeDefinitionsFromFieldMetadata(foundObjectMetadataItem);
const {
setAvailableSortDefinitions,
setAvailableFilterDefinitions,
setAvailableFieldDefinitions,
setViewType,
setViewObjectMetadataId,
setEntityCountInCurrentView,
} = useView();
useEffect(() => {
@ -65,5 +68,11 @@ export const RecordTableEffect = () => {
setContextMenuEntries?.();
}, [setActionBarEntries, setContextMenuEntries]);
useEffect(() => {
setOnEntityCountChange(
() => (entityCount: number) => setEntityCountInCurrentView(entityCount),
);
}, [setEntityCountInCurrentView, setOnEntityCountChange]);
return <></>;
};

View File

@ -19,6 +19,7 @@ import { RecordTableContainer } from './RecordTableContainer';
const StyledTableContainer = styled.div`
display: flex;
height: 100%;
width: 100%;
`;
@ -48,7 +49,8 @@ export const RecordTablePage = () => {
});
const handleAddButtonClick = async () => {
createOneObject?.({});
const createdObject = await createOneObject?.({});
console.log(createdObject);
};
return (

View File

@ -1,8 +1,8 @@
import { gql } from '@apollo/client';
export const CREATE_ONE_WORKSPACE_MEMBER_V2 = gql`
mutation CreateOneWorkspaceMemberV2($input: WorkspaceMemberV2CreateInput!) {
createWorkspaceMemberV2(data: $input) {
mutation CreateOneWorkspaceMember($input: WorkspaceMemberCreateInput!) {
createWorkspaceMember(data: $input) {
id
name {
firstName

View File

@ -23,6 +23,17 @@ export const getRecordOptimisticEffectDefinition = ({
const newRecordPaginatedCacheField = produce<
PaginatedObjectTypeResults<any>
>(currentData as PaginatedObjectTypeResults<any>, (draft) => {
if (!draft) {
return {
edges: [{ node: newData, cursor: '' }],
pageInfo: {
endCursor: '',
hasNextPage: false,
hasPreviousPage: false,
startCursor: '',
},
};
}
draft.edges.unshift({ node: newData, cursor: '' });
});

View File

@ -1,21 +0,0 @@
import { gql } from '@apollo/client';
export const FIND_ONE_WORKSPACE_MEMBER_V2 = gql`
query FindManyWorkspaceMembersV2($filter: WorkspaceMemberV2FilterInput) {
workspaceMembersV2(filter: $filter) {
edges {
node {
id
name {
firstName
lastName
}
colorScheme
avatarUrl
locale
allowImpersonation
}
}
}
}
`;

View File

@ -14,14 +14,14 @@ import { useFindManyObjectRecords } from './useFindManyObjectRecords';
export const useObjectRecordTable = () => {
const { scopeId: objectNamePlural } = useRecordTable();
const { registerOptimisticEffect } = useOptimisticEffect({
objectNameSingular: 'companyV2',
});
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
objectNamePlural,
});
const { registerOptimisticEffect } = useOptimisticEffect({
objectNameSingular: foundObjectMetadataItem?.nameSingular,
});
const { setRecordTableData } = useRecordTable();
const { tableFiltersState, tableSortsState } = useRecordTableScopedStates();

View File

@ -1,9 +1,9 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { getOperationName } from '@apollo/client/utilities';
import styled from '@emotion/styled';
import { autoUpdate, flip, offset, useFloating } from '@floating-ui/react';
import { Person } from '@/people/types/Person';
import { IconDotsVertical, IconLinkOff, IconTrash } from '@/ui/display/icon';
import { FloatingIconButton } from '@/ui/input/button/components/FloatingIconButton';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
@ -11,16 +11,9 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { Avatar } from '@/users/components/Avatar';
import {
Person,
useDeleteManyPersonMutation,
useUpdateOnePersonMutation,
} from '~/generated/graphql';
import { GET_PEOPLE } from '../graphql/queries/getPeople';
export type PeopleCardProps = {
person: Pick<Person, 'id' | 'avatarUrl' | 'displayName' | 'jobTitle'>;
person: Pick<Person, 'id' | 'avatarUrl' | 'name' | 'jobTitle'>;
hasBottomBorder?: boolean;
};
@ -78,8 +71,6 @@ export const PeopleCard = ({
const navigate = useNavigate();
const [isHovered, setIsHovered] = useState(false);
const [isOptionsOpen, setIsOptionsOpen] = useState(false);
const [updatePerson] = useUpdateOnePersonMutation();
const [deletePerson] = useDeleteManyPersonMutation();
const { refs, floatingStyles } = useFloating({
strategy: 'absolute',
@ -114,28 +105,28 @@ export const PeopleCard = ({
};
const handleDetachPerson = () => {
updatePerson({
variables: {
where: {
id: person.id,
},
data: {
company: {
disconnect: true,
},
},
},
refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
});
// updatePerson({
// variables: {
// where: {
// id: person.id,
// },
// data: {
// company: {
// disconnect: true,
// },
// },
// },
// refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
// });
};
const handleDeletePerson = () => {
deletePerson({
variables: {
ids: person.id,
},
refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
});
// deletePerson({
// variables: {
// ids: person.id,
// },
// refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
// });
};
return (
@ -149,11 +140,13 @@ export const PeopleCard = ({
<Avatar
size="lg"
type="rounded"
placeholder={person.displayName}
placeholder={person.name.firstName + ' ' + person.name.lastName}
avatarUrl={person.avatarUrl}
/>
<StyledCardInfo>
<StyledTitle>{person.displayName}</StyledTitle>
<StyledTitle>
{person.name.firstName + ' ' + person.name.lastName}
</StyledTitle>
{person.jobTitle && <StyledJobTitle>{person.jobTitle}</StyledJobTitle>}
</StyledCardInfo>
{isHovered && (

View File

@ -1,12 +1,13 @@
import { useEffect } from 'react';
import { useQuery } from '@apollo/client';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2';
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useSearchPeopleQuery } from '~/generated/graphql';
export type PeoplePickerProps = {
personId: string | null;
@ -40,7 +41,7 @@ export const PeoplePicker = ({
const queryFilters = [
{
fieldNames: ['firstName', 'lastName'],
fieldNames: ['name.firstName', 'name.lastName'],
filter: relationPickerSearchFilter,
},
];
@ -52,24 +53,32 @@ export const PeoplePicker = ({
});
}
const people = useFilteredSearchEntityQuery({
queryHook: useSearchPeopleQuery,
selectedIds: [personId ?? ''],
const { findManyQuery } = useFindOneObjectMetadataItem({
objectNameSingular: 'person',
});
const useFindManyPeople = (options: any) => useQuery(findManyQuery, options);
const people = useFilteredSearchEntityQueryV2({
queryHook: useFindManyPeople,
filters: queryFilters,
mappingFunction: (person) => ({
entityType: Entity.Person,
id: person.id,
name: `${person.firstName} ${person.lastName}`,
orderByField: 'createdAt',
mappingFunction: (workspaceMember) => ({
entityType: Entity.WorkspaceMember,
id: workspaceMember.id,
name:
workspaceMember.name.firstName + ' ' + workspaceMember.name.lastName,
avatarType: 'rounded',
avatarUrl: person.avatarUrl ?? '',
originalEntity: person,
avatarUrl: '',
originalEntity: workspaceMember,
}),
orderByField: 'firstName',
selectedIds: [personId ?? ''],
excludeEntityIds: excludePersonIds,
objectNamePlural: 'people',
});
const handleEntitySelected = async (
selectedPerson: PersonForSelect | null | undefined,
selectedPerson: any | null | undefined,
) => {
onSubmit(selectedPerson ?? null);
};

View File

@ -1,32 +0,0 @@
import {
PersonOrderByWithRelationInput,
SortOrder,
useGetPeopleQuery,
} from '~/generated/graphql';
import { useSetPeopleRecordTable } from '../hooks/useSetPeopleRecordTable';
export const PeopleRecordTableDataEffect = ({
orderBy = [
{
createdAt: SortOrder.Desc,
},
],
whereFilters,
}: {
orderBy?: PersonOrderByWithRelationInput[];
whereFilters?: any;
}) => {
const setPeopleRecordTable = useSetPeopleRecordTable();
useGetPeopleQuery({
variables: { orderBy, where: whereFilters },
onCompleted: (data) => {
const people = data.people ?? [];
setPeopleRecordTable(people);
},
});
return <></>;
};

View File

@ -1,17 +0,0 @@
import { Meta, StoryObj } from '@storybook/react';
import { mockedPeopleData } from '~/testing/mock-data/people';
import { PeopleFullNameEditableField } from '../../editable-field/components/PeopleFullNameEditableField';
const meta: Meta<typeof PeopleFullNameEditableField> = {
title: 'Modules/People/EditableFields/PeopleFullNameEditableField',
component: PeopleFullNameEditableField,
};
export default meta;
type Story = StoryObj<typeof PeopleFullNameEditableField>;
export const Default: Story = {
render: () => <PeopleFullNameEditableField people={mockedPeopleData[0]} />,
};

View File

@ -1,157 +0,0 @@
import {
IconBrandLinkedin,
IconBrandX,
IconBriefcase,
IconBuildingSkyscraper,
IconCalendarEvent,
IconMail,
IconMap,
IconPhone,
IconUser,
} from '@/ui/display/icon/index';
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
import {
FieldDateMetadata,
FieldDoubleTextChipMetadata,
FieldEmailMetadata,
FieldMetadata,
FieldPhoneMetadata,
FieldRelationMetadata,
FieldTextMetadata,
FieldURLMetadata,
} from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
import { Company } from '~/generated/graphql';
import { getLogoUrlFromDomainName } from '~/utils';
export const peopleAvailableFieldDefinitions: ColumnDefinition<FieldMetadata>[] =
[
{
fieldMetadataId: 'displayName',
label: 'People',
Icon: IconUser,
size: 210,
position: 0,
type: 'DOUBLE_TEXT_CHIP',
metadata: {
firstValueFieldName: 'firstName',
secondValueFieldName: 'lastName',
firstValuePlaceholder: 'First name', // Hack: Fake character to prevent password-manager from filling the field
secondValuePlaceholder: 'Last name', // Hack: Fake character to prevent password-manager from filling the field
avatarUrlFieldName: 'avatarUrl',
entityType: Entity.Person,
},
infoTooltipContent: 'Contacts first and last name.',
basePathToShowPage: '/person/',
} satisfies ColumnDefinition<FieldDoubleTextChipMetadata>,
{
fieldMetadataId: 'email',
label: 'Email',
Icon: IconMail,
size: 150,
type: 'EMAIL',
position: 1,
metadata: {
fieldName: 'email',
placeHolder: 'Email', // Hack: Fake character to prevent password-manager from filling the field
},
infoTooltipContent: 'Contacts Email.',
} satisfies ColumnDefinition<FieldEmailMetadata>,
{
fieldMetadataId: 'company',
label: 'Company',
Icon: IconBuildingSkyscraper,
size: 150,
position: 2,
type: 'RELATION',
metadata: {
fieldName: 'company',
relationType: Entity.Company,
},
infoTooltipContent: 'Contacts company.',
entityChipDisplayMapper: (dataObject: Company) => {
return {
name: dataObject?.name,
pictureUrl: getLogoUrlFromDomainName(dataObject?.domainName),
avatarType: 'squared',
};
},
} satisfies ColumnDefinition<FieldRelationMetadata>,
{
fieldMetadataId: 'phone',
label: 'Phone',
Icon: IconPhone,
size: 150,
position: 3,
type: 'PHONE',
metadata: {
fieldName: 'phone',
placeHolder: 'Phone', // Hack: Fake character to prevent password-manager from filling the field
},
infoTooltipContent: 'Contacts phone number.',
} satisfies ColumnDefinition<FieldPhoneMetadata>,
{
fieldMetadataId: 'createdAt',
label: 'Creation',
Icon: IconCalendarEvent,
size: 150,
position: 4,
type: 'DATE',
metadata: {
fieldName: 'createdAt',
},
infoTooltipContent: 'Date when the contact was added.',
} satisfies ColumnDefinition<FieldDateMetadata>,
{
fieldMetadataId: 'city',
label: 'City',
Icon: IconMap,
size: 150,
position: 5,
type: 'TEXT',
metadata: {
fieldName: 'city',
placeHolder: 'City', // Hack: Fake character to prevent password-manager from filling the field
},
infoTooltipContent: 'Contacts city.',
} satisfies ColumnDefinition<FieldTextMetadata>,
{
fieldMetadataId: 'jobTitle',
label: 'Job title',
Icon: IconBriefcase,
size: 150,
position: 6,
type: 'TEXT',
metadata: {
fieldName: 'jobTitle',
placeHolder: 'Job title',
},
infoTooltipContent: 'Contacts job title.',
} satisfies ColumnDefinition<FieldTextMetadata>,
{
fieldMetadataId: 'linkedin',
label: 'LinkedIn',
Icon: IconBrandLinkedin,
size: 150,
position: 7,
type: 'URL',
metadata: {
fieldName: 'linkedinUrl',
placeHolder: 'LinkedIn',
},
infoTooltipContent: 'Contacts Linkedin account.',
} satisfies ColumnDefinition<FieldURLMetadata>,
{
fieldMetadataId: 'x',
label: 'Twitter',
Icon: IconBrandX,
size: 150,
position: 8,
type: 'URL',
metadata: {
fieldName: 'xUrl',
placeHolder: 'X',
},
infoTooltipContent: 'Contacts Twitter account.',
} satisfies ColumnDefinition<FieldURLMetadata>,
];

View File

@ -1,61 +0,0 @@
import { useState } from 'react';
import { EntityTitleDoubleTextInput } from '@/ui/input/components/EntityTitleDoubleTextInput';
import { FieldRecoilScopeContext } from '@/ui/object/record-inline-cell/states/recoil-scope-contexts/FieldRecoilScopeContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { Person, useUpdateOnePersonMutation } from '~/generated/graphql';
type PeopleFullNameEditableFieldProps = {
people: Pick<Person, 'id' | 'firstName' | 'lastName'>;
};
export const PeopleFullNameEditableField = ({
people,
}: PeopleFullNameEditableFieldProps) => {
const [internalValueFirstName, setInternalValueFirstName] = useState(
people.firstName,
);
const [internalValueLastName, setInternalValueLastName] = useState(
people.lastName,
);
const [updatePeople] = useUpdateOnePersonMutation();
const handleChange = async (
newValueFirstName: string,
newValueLastName: string,
) => {
setInternalValueFirstName(newValueFirstName);
setInternalValueLastName(newValueLastName);
handleSubmit(newValueFirstName, newValueLastName);
};
const handleSubmit = async (
newValueFirstName: string,
newValueLastName: string,
) => {
await updatePeople({
variables: {
where: {
id: people.id,
},
data: {
firstName: newValueFirstName ?? '',
lastName: newValueLastName ?? '',
},
},
});
};
return (
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
<EntityTitleDoubleTextInput
firstValuePlaceholder="Empty"
secondValuePlaceholder="Empty"
firstValue={internalValueFirstName ?? ''}
secondValue={internalValueLastName ?? ''}
onChange={handleChange}
/>
</RecoilScope>
);
};

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More