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,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
}
}
`;

View File

@ -1,9 +0,0 @@
import { gql } from '@apollo/client';
export const INSERT_MANY_PERSON = gql`
mutation InsertManyPerson($data: [PersonCreateManyInput!]!) {
createManyPerson(data: $data) {
count
}
}
`;

View File

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

View File

@ -1,10 +0,0 @@
import { gql } from '@apollo/client';
export const REMOVE_PERSON_PICTURE = gql`
mutation RemovePersonPicture($where: PersonWhereUniqueInput!) {
updateOnePerson(data: { avatarUrl: null }, where: $where) {
id
avatarUrl
}
}
`;

View File

@ -1,12 +0,0 @@
import { gql } from '@apollo/client';
export const UPDATE_ONE_PERSON = gql`
mutation UpdateOnePerson(
$where: PersonWhereUniqueInput!
$data: PersonUpdateInput!
) {
updateOnePerson(data: $data, where: $where) {
...personFieldsFragment
}
}
`;

View File

@ -1,7 +0,0 @@
import { gql } from '@apollo/client';
export const UPDATE_PERSON_PICTURE = gql`
mutation UploadPersonPicture($id: String!, $file: Upload!) {
uploadPersonPicture(id: $id, file: $file)
}
`;

View File

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

View File

@ -1,16 +0,0 @@
import { gql } from '@apollo/client';
import { PERSON_FIELDS_FRAGMENT } from '../fragments/personFieldsFragment';
export const GET_PEOPLE = gql`
${PERSON_FIELDS_FRAGMENT}
query GetPeople(
$orderBy: [PersonOrderByWithRelationInput!]
$where: PersonWhereInput
$limit: Int
) {
people: findManyPerson(orderBy: $orderBy, where: $where, take: $limit) {
...personFieldsFragment
}
}
`;

View File

@ -1,18 +0,0 @@
import { gql } from '@apollo/client';
export const GET_PERSON = gql`
query GetPerson($id: String!) {
findUniquePerson(id: $id) {
...personFieldsFragment
Favorite {
id
person {
id
}
company {
id
}
}
}
}
`;

View File

@ -1,10 +0,0 @@
import { gql } from '@apollo/client';
export const GET_PERSON_CITY = gql`
query GetPersonCityById($id: String!) {
person: findUniquePerson(id: $id) {
id
city
}
}
`;

View File

@ -1,10 +0,0 @@
import { gql } from '@apollo/client';
export const GET_PERSON_COMMENT_COUNT = gql`
query GetPersonCommentCountById($id: String!) {
person: findUniquePerson(id: $id) {
id
_activityCount
}
}
`;

View File

@ -1,14 +0,0 @@
import { gql } from '@apollo/client';
export const GET_PERSON_COMPANY = gql`
query GetPersonCompanyById($id: String!) {
person: findUniquePerson(id: $id) {
id
company {
id
name
domainName
}
}
}
`;

View File

@ -1,10 +0,0 @@
import { gql } from '@apollo/client';
export const GET_PERSON_CREATED_AT = gql`
query GetPersonCreatedAtById($id: String!) {
person: findUniquePerson(id: $id) {
id
createdAt
}
}
`;

View File

@ -1,10 +0,0 @@
import { gql } from '@apollo/client';
export const GET_PERSON_EMAIL = gql`
query GetPersonEmailById($id: String!) {
person: findUniquePerson(id: $id) {
id
email
}
}
`;

View File

@ -1,13 +0,0 @@
import { gql } from '@apollo/client';
export const GET_PERSON_NAMES_AND_COMMENT_COUNT = gql`
query GetPersonNamesAndCommentCountById($id: String!) {
person: findUniquePerson(id: $id) {
id
firstName
lastName
displayName
_activityCount
}
}
`;

View File

@ -1,10 +0,0 @@
import { gql } from '@apollo/client';
export const GET_PERSON_PHONE = gql`
query GetPersonPhoneById($id: String!) {
person: findUniquePerson(id: $id) {
id
phone
}
}
`;

View File

@ -1,39 +0,0 @@
import { useRecoilCallback } from 'recoil';
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { entityFieldsFamilyState } from '@/ui/object/field/states/entityFieldsFamilyState';
import { selectedRowIdsSelector } from '@/ui/object/record-table/states/selectors/selectedRowIdsSelector';
import { ActivityType, Person } from '~/generated/graphql';
export const useCreateActivityForPeople = () => {
const openCreateActivityRightDrawer =
useOpenCreateActivityDrawerForSelectedRowIds();
return useRecoilCallback(
({ snapshot }) =>
(type: ActivityType) => {
const relatedEntites: ActivityTargetableEntity[] = [];
const selectedRowIds = Object.keys(
snapshot.getLoadable(selectedRowIdsSelector).getValue(),
);
for (const id of selectedRowIds) {
const person = snapshot
.getLoadable(entityFieldsFamilyState(id))
.getValue() as Person;
if (
person?.company?.id &&
!relatedEntites.find((x) => x.id === person?.company?.id)
) {
relatedEntites.push({
id: person.company.id,
type: 'Company',
});
}
}
openCreateActivityRightDrawer(type, 'Person', relatedEntites);
},
[openCreateActivityRightDrawer],
);
};

View File

@ -1,6 +1,8 @@
import { ActivityTargetableEntityForSelect } from '@/activities/types/ActivityTargetableEntityForSelect';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { useSearchPeopleQuery } from '~/generated/graphql';
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 useFilteredSearchPeopleQuery = ({
searchFilter,
@ -10,24 +12,32 @@ export const useFilteredSearchPeopleQuery = ({
searchFilter: string;
selectedIds?: string[];
limit?: number;
}) =>
useFilteredSearchEntityQuery({
queryHook: useSearchPeopleQuery,
}) => {
const { findManyQuery } = useFindOneObjectMetadataItem({
objectNameSingular: 'person',
});
const useFindManyPeople = (options: any) => useQuery(findManyQuery, options);
return useFilteredSearchEntityQueryV2({
queryHook: useFindManyPeople,
filters: [
{
fieldNames: ['firstName', 'lastName'],
fieldNames: ['name.firstName', 'name.lastName'],
filter: searchFilter,
},
],
orderByField: 'lastName',
orderByField: 'createdAt',
mappingFunction: (person) => ({
entityType: Entity.Person,
id: person.id,
name: person.name.firstName + ' ' + person.name.lastName,
avatarType: 'rounded',
avatarUrl: '',
originalEntity: person,
}),
selectedIds: selectedIds,
mappingFunction: (entity) =>
({
id: entity.id,
entityType: 'Person',
name: `${entity.firstName} ${entity.lastName}`,
avatarUrl: entity.avatarUrl,
avatarType: 'rounded',
} as ActivityTargetableEntityForSelect),
objectNamePlural: 'workspaceMembers',
limit,
});
};

View File

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

View File

@ -1,155 +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,
useDeleteManyPersonMutation,
useGetFavoritesQuery,
} from '~/generated/graphql';
import { GET_PEOPLE } from '../graphql/queries/getPeople';
import { useCreateActivityForPeople } from './useCreateActivityForPeople';
export const usePersonTableContextMenuEntries = () => {
const setContextMenuEntries = useSetRecoilState(contextMenuEntriesState);
const setActionBarEntriesState = useSetRecoilState(actionBarEntriesState);
const createActivityForPeople = useCreateActivityForPeople();
const setTableRowIds = useSetRecoilState(tableRowIdsState);
const { resetTableRowSelection } = useRecordTable({
recordTableScopeId: 'people',
});
const { data } = useGetFavoritesQuery();
const favorites = data?.findFavorites;
const { createFavorite, deleteFavorite } = useFavorites({
objectNamePlural: 'people',
});
const handleFavoriteButtonClick = useRecoilCallback(({ snapshot }) => () => {
const selectedRowIds = snapshot
.getLoadable(selectedRowIdsSelector)
.getValue();
const selectedPersonId =
selectedRowIds.length === 1 ? selectedRowIds[0] : '';
const isFavorite =
!!selectedPersonId &&
!!favorites?.find((favorite) => favorite.person?.id === selectedPersonId);
resetTableRowSelection();
if (isFavorite) deleteFavorite(selectedPersonId);
else createFavorite('person', selectedPersonId);
});
const [deleteManyPerson] = useDeleteManyPersonMutation({
refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
});
const handleDeleteClick = useRecoilCallback(({ snapshot }) => async () => {
const rowIdsToDelete = snapshot
.getLoadable(selectedRowIdsSelector)
.getValue();
resetTableRowSelection();
await deleteManyPerson({
variables: {
ids: rowIdsToDelete,
},
optimisticResponse: {
__typename: 'Mutation',
deleteManyPerson: {
count: rowIdsToDelete.length,
},
},
update: () => {
setTableRowIds((tableRowIds) =>
tableRowIds.filter((id) => !rowIdsToDelete.includes(id)),
);
},
});
});
return {
setContextMenuEntries: useRecoilCallback(({ snapshot }) => () => {
const selectedRowIds = snapshot
.getLoadable(selectedRowIdsSelector)
.getValue();
const selectedPersonId =
selectedRowIds.length === 1 ? selectedRowIds[0] : '';
const isFavorite =
!!selectedPersonId &&
!!favorites?.find(
(favorite) => favorite.person?.id === selectedPersonId,
);
setContextMenuEntries([
{
label: 'New task',
Icon: IconCheckbox,
onClick: () => createActivityForPeople(ActivityType.Task),
},
{
label: 'New note',
Icon: IconNotes,
onClick: () => createActivityForPeople(ActivityType.Note),
},
...(!!selectedPersonId
? [
{
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: () => createActivityForPeople(ActivityType.Task),
},
{
label: 'Note',
Icon: IconNotes,
onClick: () => createActivityForPeople(ActivityType.Note),
},
{
label: 'Delete',
Icon: IconTrash,
accent: 'danger',
onClick: () => handleDeleteClick(),
},
]);
}),
};
};

View File

@ -1,129 +0,0 @@
import { useLocation } from 'react-router-dom';
import { useRecoilCallback } from 'recoil';
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
import { isFetchingRecordTableDataState } from '@/ui/object/record-table/states/isFetchingRecordTableDataState';
import { numberOfTableRowsState } from '@/ui/object/record-table/states/numberOfTableRowsState';
import { tableRowIdsState } from '@/ui/object/record-table/states/tableRowIdsState';
import { currentPageLocationState } from '@/ui/utilities/loading-state/states/currentPageLocationState';
import { GetPeopleQuery } from '~/generated/graphql';
import { peopleCityFamilyState } from '../states/peopleCityFamilyState';
import { peopleCompanyFamilyState } from '../states/peopleCompanyFamilyState';
import { peopleCreatedAtFamilyState } from '../states/peopleCreatedAtFamilyState';
import { peopleEmailFamilyState } from '../states/peopleEmailFamilyState';
import { peopleJobTitleFamilyState } from '../states/peopleJobTitleFamilyState';
import { peopleLinkedinUrlFamilyState } from '../states/peopleLinkedinUrlFamilyState';
import { peopleNameCellFamilyState } from '../states/peopleNamesFamilyState';
import { peoplePhoneFamilyState } from '../states/peoplePhoneFamilyState';
export const useSetPeopleRecordTable = () => {
const { resetTableRowSelection } = useRecordTable();
const currentLocation = useLocation().pathname;
return useRecoilCallback(
({ set, snapshot }) =>
(newPeopleArray: GetPeopleQuery['people']) => {
for (const person of newPeopleArray) {
const currentEmail = snapshot
.getLoadable(peopleEmailFamilyState(person.id))
.valueOrThrow();
if (currentEmail !== person.email) {
set(peopleEmailFamilyState(person.id), person.email ?? null);
}
const currentCity = snapshot
.getLoadable(peopleCityFamilyState(person.id))
.valueOrThrow();
if (currentCity !== person.city) {
set(peopleCityFamilyState(person.id), person.city ?? null);
}
const currentCompany = snapshot
.getLoadable(peopleCompanyFamilyState(person.id))
.valueOrThrow();
if (
JSON.stringify(currentCompany) !== JSON.stringify(person.company)
) {
set(peopleCompanyFamilyState(person.id), person.company);
}
const currentPhone = snapshot
.getLoadable(peoplePhoneFamilyState(person.id))
.valueOrThrow();
if (currentPhone !== person.phone) {
set(peoplePhoneFamilyState(person.id), person.phone ?? null);
}
const currentCreatedAt = snapshot
.getLoadable(peopleCreatedAtFamilyState(person.id))
.valueOrThrow();
if (currentCreatedAt !== person.createdAt) {
set(peopleCreatedAtFamilyState(person.id), person.createdAt);
}
const currentJobTitle = snapshot
.getLoadable(peopleJobTitleFamilyState(person.id))
.valueOrThrow();
if (currentJobTitle !== person.jobTitle) {
set(peopleJobTitleFamilyState(person.id), person.jobTitle ?? null);
}
const currentLinkedinUrl = snapshot
.getLoadable(peopleLinkedinUrlFamilyState(person.id))
.valueOrThrow();
if (currentLinkedinUrl !== person.linkedinUrl) {
set(
peopleLinkedinUrlFamilyState(person.id),
person.linkedinUrl ?? null,
);
}
const currentNameCell = snapshot
.getLoadable(peopleNameCellFamilyState(person.id))
.valueOrThrow();
if (
currentNameCell.firstName !== person.firstName ||
currentNameCell.lastName !== person.lastName ||
currentNameCell.commentCount !== person._activityCount
) {
set(peopleNameCellFamilyState(person.id), {
firstName: person.firstName ?? null,
lastName: person.lastName ?? null,
commentCount: person._activityCount,
displayName: person.displayName ?? null,
avatarUrl: person.avatarUrl ?? null,
});
}
}
const peopleIds = newPeopleArray.map((people) => people.id);
set(tableRowIdsState, (currentRowIds) => {
if (JSON.stringify(currentRowIds) !== JSON.stringify(peopleIds)) {
return peopleIds;
}
return currentRowIds;
});
resetTableRowSelection();
set(numberOfTableRowsState, peopleIds.length);
set(currentPageLocationState, currentLocation);
set(isFetchingRecordTableDataState, false);
},
[currentLocation, resetTableRowSelection],
);
};

View File

@ -3,7 +3,6 @@ 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 { useInsertManyPersonMutation } from '~/generated/graphql';
import { fieldsForPerson } from '../utils/fieldsForPerson';
@ -13,8 +12,6 @@ export const useSpreadsheetPersonImport = () => {
const { openSpreadsheetImport } = useSpreadsheetImport<FieldPersonMapping>();
const { enqueueSnackBar } = useSnackBar();
const [createManyPerson] = useInsertManyPersonMutation();
const openPersonSpreadsheetImport = (
options?: Omit<
SpreadsheetOptions<FieldPersonMapping>,
@ -37,22 +34,23 @@ export const useSpreadsheetPersonImport = () => {
city: person.city as string | undefined,
}));
try {
const result = await createManyPerson({
variables: {
data: createInputs,
},
refetchQueries: 'active',
});
// TODO : abstract this part for any object
// try {
// const result = await createManyPerson({
// variables: {
// data: createInputs,
// },
// refetchQueries: 'active',
// });
if (result.errors) {
throw result.errors;
}
} catch (error: any) {
enqueueSnackBar(error?.message || 'Something went wrong', {
variant: 'error',
});
}
// if (result.errors) {
// throw result.errors;
// }
// } catch (error: any) {
// enqueueSnackBar(error?.message || 'Something went wrong', {
// variant: 'error',
// });
// }
},
fields: fieldsForPerson,
});

View File

@ -1,6 +0,0 @@
import { atomFamily } from 'recoil';
export const peopleCityFamilyState = atomFamily<string | null, string>({
key: 'peopleCityFamilyState',
default: null,
});

View File

@ -1,11 +0,0 @@
import { atomFamily } from 'recoil';
import { GetPeopleQuery } from '~/generated/graphql';
export const peopleCompanyFamilyState = atomFamily<
GetPeopleQuery['people'][0]['company'] | null,
string
>({
key: 'peopleCompanyFamilyState',
default: null,
});

View File

@ -1,6 +0,0 @@
import { atomFamily } from 'recoil';
export const peopleCreatedAtFamilyState = atomFamily<string | null, string>({
key: 'peopleCreatedAtFamilyState',
default: null,
});

View File

@ -1,6 +0,0 @@
import { atomFamily } from 'recoil';
export const peopleEmailFamilyState = atomFamily<string | null, string>({
key: 'peopleEmailFamilyState',
default: null,
});

View File

@ -1,6 +0,0 @@
import { atomFamily } from 'recoil';
export const peopleJobTitleFamilyState = atomFamily<string | null, string>({
key: 'peopleJobTitleFamilyState',
default: null,
});

View File

@ -1,6 +0,0 @@
import { atomFamily } from 'recoil';
export const peopleLinkedinUrlFamilyState = atomFamily<string | null, string>({
key: 'peopleLinkedinUrlFamilyState',
default: null,
});

View File

@ -1,21 +0,0 @@
import { atomFamily } from 'recoil';
export const peopleNameCellFamilyState = atomFamily<
{
firstName: string | null;
lastName: string | null;
commentCount: number | null;
displayName: string | null;
avatarUrl: string | null;
},
string
>({
key: 'peopleNameCellFamilyState',
default: {
firstName: null,
lastName: null,
commentCount: null,
displayName: null,
avatarUrl: null,
},
});

View File

@ -1,6 +0,0 @@
import { atomFamily } from 'recoil';
export const peoplePhoneFamilyState = atomFamily<string | null, string>({
key: 'peoplePhoneFamilyState',
default: null,
});

View File

@ -1,133 +0,0 @@
import styled from '@emotion/styled';
import { peopleAvailableFieldDefinitions } from '@/people/constants/peopleAvailableFieldDefinitions';
import { getPeopleOptimisticEffectDefinition } from '@/people/graphql/optimistic-effect-definitions/getPeopleOptimisticEffectDefinition';
import { usePersonTableContextMenuEntries } from '@/people/hooks/usePersonTableContextMenuEntries';
import { useSpreadsheetPersonImport } from '@/people/hooks/useSpreadsheetPersonImport';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
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 { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
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 {
UpdateOnePersonMutationVariables,
useGetPeopleQuery,
useUpdateOnePersonMutation,
} from '~/generated/graphql';
import { personTableFilterDefinitions } from '~/pages/people/constants/personTableFilterDefinitions';
import { personTableSortDefinitions } from '~/pages/people/constants/personTableSortDefinitions';
import PersonTableEffect from './PersonTableEffect';
export const PersonTable = () => {
const viewScopeId = 'person-table-view';
const tableScopeId = 'people';
const {
setTableFilters,
setTableSorts,
setTableColumns,
upsertRecordTableItem,
} = useRecordTable({
recordTableScopeId: tableScopeId,
});
const [updateEntityMutation] = useUpdateOnePersonMutation();
const { persistViewFields } = useViewFields(viewScopeId);
const { setContextMenuEntries, setActionBarEntries } =
usePersonTableContextMenuEntries();
const updatePerson = async (variables: UpdateOnePersonMutationVariables) => {
updateEntityMutation({
variables: variables,
onCompleted: (data) => {
if (!data.updateOnePerson) {
return;
}
upsertRecordTableItem(data.updateOnePerson);
},
});
};
const handleColumnChange = (columns: ColumnDefinition<FieldMetadata>[]) => {
persistViewFields(mapColumnDefinitionsToViewFields(columns));
};
const { openPersonSpreadsheetImport: onImport } =
useSpreadsheetPersonImport();
const { setEntityCountInCurrentView } = useView({ viewScopeId });
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
overflow: auto;
`;
return (
<ViewScope
viewScopeId={viewScopeId}
onViewFieldsChange={(viewFields) => {
setTableColumns(
mapViewFieldsToColumnDefinitions(
viewFields,
peopleAvailableFieldDefinitions,
),
);
}}
onViewFiltersChange={(viewFilters) => {
setTableFilters(mapViewFiltersToFilters(viewFilters));
}}
onViewSortsChange={(viewSorts) => {
setTableSorts(mapViewSortsToSorts(viewSorts));
}}
>
<StyledContainer>
<RecordTableScope
recordTableScopeId={tableScopeId}
onColumnsChange={handleColumnChange}
onEntityCountChange={(entityCount) => {
setEntityCountInCurrentView(entityCount);
}}
>
<ViewBar
optionsDropdownButton={<TableOptionsDropdown onImport={onImport} />}
optionsDropdownScopeId={TableOptionsDropdownId}
/>
<PersonTableEffect />
<RecordTableEffect
getRequestResultKey="people"
useGetRequest={useGetPeopleQuery}
getRequestOptimisticEffectDefinition={
getPeopleOptimisticEffectDefinition
}
filterDefinitionArray={personTableFilterDefinitions}
sortDefinitionArray={personTableSortDefinitions}
setContextMenuEntries={setContextMenuEntries}
setActionBarEntries={setActionBarEntries}
/>
<RecordTableV1
updateEntityMutation={({
variables,
}: {
variables: UpdateOnePersonMutationVariables;
}) => updatePerson(variables)}
/>
</RecordTableScope>
</StyledContainer>
</ViewScope>
);
};

View File

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

View File

@ -0,0 +1,24 @@
export type Person = {
id: string;
createdAt: string;
updatedAt: string;
deletedAt: string | null;
name: {
firstName: string;
lastName: string;
};
avatarUrl: string;
jobTitle: string;
linkedinLink: {
url: string;
label: string;
};
xLink: {
url: string;
label: string;
};
city: string;
email: string;
phone: string;
companyId: string;
};