Fix/company team crud (#2614)

* Fixed basePathToShowPage

* Fixed company team list

* Fixed : create, update, delete and detach people from company.
This commit is contained in:
Lucas Bordeau
2023-11-21 12:24:20 +01:00
committed by GitHub
parent 6f4a952381
commit b13d84fcda
9 changed files with 163 additions and 92 deletions

View File

@ -1,8 +1,11 @@
import { useState } from 'react'; import { useState } from 'react';
import { useMutation, useQuery } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { flip, offset, useFloating } from '@floating-ui/react'; import { flip, offset, useFloating } from '@floating-ui/react';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { IconPlus } from '@/ui/display/icon'; import { IconPlus } from '@/ui/display/icon';
import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
@ -62,23 +65,43 @@ export const AddPersonToCompany = ({
goBackToPreviousHotkeyScope, goBackToPreviousHotkeyScope,
} = usePreviousHotkeyScope(); } = usePreviousHotkeyScope();
const handlePersonSelected = // TODO: refactor with useObjectMetadataItem V2 with typed hooks
(companyId: string) => async (newPerson: any | null) => { const { findManyQuery, updateOneMutation, createOneMutation } =
if (newPerson) { useObjectMetadataItem({
// await updatePerson({ objectNameSingular: 'person',
// variables: { });
// where: {
// id: newPerson.id, const { data: peopleNotInCompany } = useQuery(findManyQuery, {
// }, variables: {
// data: { filter: {
// company: { connect: { id: companyId } }, companyId: {
// }, neq: companyId,
// }, },
// refetchQueries: [getOperationName(GET_PEOPLE) ?? ''], },
// }); },
handleClosePicker(); });
}
}; const [updatePerson] = useMutation(updateOneMutation);
const [createPerson] = useMutation(createOneMutation);
const handlePersonSelected = async ({
selectedPersonId,
companyId,
}: {
selectedPersonId: string;
companyId: string | null;
}) => {
await updatePerson({
variables: {
idToUpdate: selectedPersonId,
input: {
companyId: companyId,
},
},
refetchQueries: [getOperationName(findManyQuery) ?? ''],
});
handleClosePicker();
};
const handleClosePicker = () => { const handleClosePicker = () => {
if (isDropdownOpen) { if (isDropdownOpen) {
@ -88,31 +111,44 @@ export const AddPersonToCompany = ({
}; };
const handleOpenPicker = () => { const handleOpenPicker = () => {
if (!isDropdownOpen) { // TODO: TEMPORARY - example to implement when the picker is back
setIsDropdownOpen(true); handleCreatePerson({
setHotkeyScopeAndMemorizePreviousScope( firstValue: 'John',
RelationPickerHotkeyScope.RelationPicker, secondValue: 'Doe',
); });
} // handlePersonSelected({
// companyId,
// selectedPersonId: peopleNotInCompany.people.edges[0].node.id,
// });
// if (!isDropdownOpen) {
// setIsDropdownOpen(true);
// setHotkeyScopeAndMemorizePreviousScope(
// RelationPickerHotkeyScope.RelationPicker,
// );
// }
}; };
const handleInputKeyDown = async ({ const handleCreatePerson = async ({
firstValue, firstValue,
secondValue, secondValue,
}: FieldDoubleText) => { }: FieldDoubleText) => {
if (!firstValue && !secondValue) return; if (!firstValue && !secondValue) return;
const newPersonId = v4(); const newPersonId = v4();
// await insertOnePerson({
// variables: { await createPerson({
// data: { variables: {
// company: { connect: { id: companyId } }, input: {
// id: newPersonId, companyId: companyId,
// firstName: firstValue, id: newPersonId,
// lastName: secondValue, name: {
// }, firstName: firstValue,
// }, lastName: secondValue,
// refetchQueries: [getOperationName(GET_PEOPLE) ?? ''], },
// }); },
},
refetchQueries: [getOperationName(findManyQuery) ?? ''],
});
setIsCreationDropdownOpen(false); setIsCreationDropdownOpen(false);
}; };
@ -136,7 +172,7 @@ export const AddPersonToCompany = ({
firstValuePlaceholder="First Name" firstValuePlaceholder="First Name"
secondValuePlaceholder="Last Name" secondValuePlaceholder="Last Name"
onClickOutside={handleEscape} onClickOutside={handleEscape}
onEnter={handleInputKeyDown} onEnter={handleCreatePerson}
onEscape={handleEscape} onEscape={handleEscape}
hotkeyScope={RelationPickerHotkeyScope.RelationPicker} hotkeyScope={RelationPickerHotkeyScope.RelationPicker}
/> />

View File

@ -1,8 +1,11 @@
import { useQuery } from '@apollo/client';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { isNonEmptyArray } from '@sniptt/guards';
import { Company } from '@/companies/types/Company'; import { Company } from '@/companies/types/Company';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { mapPaginatedObjectsToObjects } from '@/object-record/utils/mapPaginatedObjectsToObjects';
import { PeopleCard } from '@/people/components/PeopleCard'; import { PeopleCard } from '@/people/components/PeopleCard';
import { Person } from '@/people/types/Person';
import { AddPersonToCompany } from './AddPersonToCompany'; import { AddPersonToCompany } from './AddPersonToCompany';
@ -43,37 +46,44 @@ const StyledTitle = styled.div`
line-height: ${({ theme }) => theme.text.lineHeight.lg}; line-height: ${({ theme }) => theme.text.lineHeight.lg};
`; `;
export const CompanyTeam = ({ company }: CompanyTeamProps) => { export const CompanyTeam = ({ company }: { company: any }) => {
// const { data } = useGetPeopleQuery({ const { findManyQuery } = useObjectMetadataItem({
// variables: { objectNameSingular: 'person',
// orderBy: [], });
// where: {
// companyId: {
// equals: company.id,
// },
// },
// },
// });
const data = {
people: [],
};
const peopleIds = data?.people?.map(({ id }) => id); const { data } = useQuery(findManyQuery, {
variables: {
filter: {
companyId: {
eq: company.id,
},
},
},
});
const people = mapPaginatedObjectsToObjects({
objectNamePlural: 'people',
pagedObjects: data ?? [],
});
const peopleIds = people.map((person) => person.id);
const hasPeople = isNonEmptyArray(peopleIds);
return ( return (
<> <>
{Boolean(data?.people?.length) && ( {hasPeople && (
<StyledContainer> <StyledContainer>
<StyledTitleContainer> <StyledTitleContainer>
<StyledTitle>Team</StyledTitle> <StyledTitle>Team</StyledTitle>
<AddPersonToCompany companyId={company.id} peopleIds={peopleIds} /> <AddPersonToCompany companyId={company.id} peopleIds={peopleIds} />
</StyledTitleContainer> </StyledTitleContainer>
<StyledListContainer> <StyledListContainer>
{data?.people?.map((person: Person, id) => ( {people.map((person: any) => (
<PeopleCard <PeopleCard
key={person.id} key={person.id}
person={person} person={person}
hasBottomBorder={id !== data.people.length - 1} hasBottomBorder={person.id !== people.length - 1}
/> />
))} ))}
</StyledListContainer> </StyledListContainer>

View File

@ -26,7 +26,7 @@ export const EMPTY_MUTATION = gql`
export const useObjectMetadataItem = ({ export const useObjectMetadataItem = ({
objectNamePlural, objectNamePlural,
objectNameSingular, objectNameSingular,
}: ObjectMetadataItemIdentifier & { skip?: boolean }) => { }: ObjectMetadataItemIdentifier) => {
const objectMetadataItem = useRecoilValue( const objectMetadataItem = useRecoilValue(
objectMetadataItemFamilySelector({ objectMetadataItemFamilySelector({
objectNamePlural, objectNamePlural,

View File

@ -2,6 +2,7 @@ import { useParams } from 'react-router-dom';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { CompanyTeam } from '@/companies/components/CompanyTeam';
import { useFavorites } from '@/favorites/hooks/useFavorites'; import { useFavorites } from '@/favorites/hooks/useFavorites';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition'; import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
@ -35,14 +36,12 @@ export const RecordShowPage = () => {
objectMetadataId: string; objectMetadataId: string;
}>(); }>();
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem( const { objectMetadataItem } = useObjectMetadataItem({
{ objectNameSingular,
objectNameSingular, });
},
);
const { favorites, createFavorite, deleteFavorite } = useFavorites({ const { favorites, createFavorite, deleteFavorite } = useFavorites({
objectNamePlural: foundObjectMetadataItem?.namePlural, objectNamePlural: objectMetadataItem?.namePlural,
}); });
const [, setEntityFields] = useRecoilState( const [, setEntityFields] = useRecoilState(
@ -152,8 +151,8 @@ export const RecordShowPage = () => {
avatarType="squared" avatarType="squared"
/> />
<PropertyBox extraPadding={true}> <PropertyBox extraPadding={true}>
{foundObjectMetadataItem && {objectMetadataItem &&
[...foundObjectMetadataItem.fields] [...objectMetadataItem.fields]
.sort((a, b) => .sort((a, b) =>
DateTime.fromISO(a.createdAt) DateTime.fromISO(a.createdAt)
.diff(DateTime.fromISO(b.createdAt)) .diff(DateTime.fromISO(b.createdAt))
@ -182,6 +181,13 @@ export const RecordShowPage = () => {
); );
})} })}
</PropertyBox> </PropertyBox>
{objectNameSingular === 'company' ? (
<>
<CompanyTeam company={object} />
</>
) : (
<></>
)}
</ShowPageLeftContainer> </ShowPageLeftContainer>
<ShowPageRightContainer <ShowPageRightContainer
entity={{ entity={{

View File

@ -54,7 +54,6 @@ export const useFindManyObjectRecords = <
findManyQuery, findManyQuery,
} = useObjectMetadataItem({ } = useObjectMetadataItem({
objectNamePlural, objectNamePlural,
skip,
}); });
const { enqueueSnackBar } = useSnackBar(); const { enqueueSnackBar } = useSnackBar();

View File

@ -16,7 +16,7 @@ export const mapPaginatedObjectsToObjects = <
objectNamePlural: string; objectNamePlural: string;
}) => { }) => {
const formattedObjects: ObjectType[] = const formattedObjects: ObjectType[] =
pagedObjects?.[objectNamePlural].edges.map((objectEdge: ObjectEdge) => ({ pagedObjects?.[objectNamePlural]?.edges?.map((objectEdge: ObjectEdge) => ({
...objectEdge.node, ...objectEdge.node,
})) ?? []; })) ?? [];

View File

@ -1,8 +1,11 @@
import { useState } from 'react'; import { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { autoUpdate, flip, offset, useFloating } from '@floating-ui/react'; import { autoUpdate, flip, offset, useFloating } from '@floating-ui/react';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { Person } from '@/people/types/Person'; import { Person } from '@/people/types/Person';
import { IconDotsVertical, IconLinkOff, IconTrash } from '@/ui/display/icon'; import { IconDotsVertical, IconLinkOff, IconTrash } from '@/ui/display/icon';
import { FloatingIconButton } from '@/ui/input/button/components/FloatingIconButton'; import { FloatingIconButton } from '@/ui/input/button/components/FloatingIconButton';
@ -104,29 +107,34 @@ export const PeopleCard = ({
setIsOptionsOpen(!isOptionsOpen); setIsOptionsOpen(!isOptionsOpen);
}; };
const handleDetachPerson = () => { // TODO: refactor with useObjectMetadataItem V2 with typed hooks
// updatePerson({ const { findManyQuery, updateOneMutation, deleteOneMutation } =
// variables: { useObjectMetadataItem({
// where: { objectNameSingular: 'person',
// id: person.id, });
// },
// data: { const [updatePerson] = useMutation(updateOneMutation);
// company: { const [deletePerson] = useMutation(deleteOneMutation);
// disconnect: true,
// }, const handleDetachPerson = async () => {
// }, await updatePerson({
// }, variables: {
// refetchQueries: [getOperationName(GET_PEOPLE) ?? ''], idToUpdate: person.id,
// }); input: {
companyId: null,
},
},
refetchQueries: [getOperationName(findManyQuery) ?? ''],
});
}; };
const handleDeletePerson = () => { const handleDeletePerson = () => {
// deletePerson({ deletePerson({
// variables: { variables: {
// ids: person.id, idToDelete: person.id,
// }, },
// refetchQueries: [getOperationName(GET_PEOPLE) ?? ''], refetchQueries: [getOperationName(findManyQuery) ?? ''],
// }); });
}; };
return ( return (

View File

@ -23,7 +23,7 @@ export type MenuItemProps = {
isTooltipOpen?: boolean; isTooltipOpen?: boolean;
className?: string; className?: string;
testId?: string; testId?: string;
onClick?: () => void; onClick?: (event: MouseEvent<HTMLLIElement>) => void;
}; };
export const MenuItem = ({ export const MenuItem = ({
@ -38,10 +38,17 @@ export const MenuItem = ({
}: MenuItemProps) => { }: MenuItemProps) => {
const showIconButtons = Array.isArray(iconButtons) && iconButtons.length > 0; const showIconButtons = Array.isArray(iconButtons) && iconButtons.length > 0;
const handleMenuItemClick = (event: MouseEvent<HTMLLIElement>) => {
event.preventDefault();
event.stopPropagation();
onClick?.(event);
};
return ( return (
<StyledHoverableMenuItemBase <StyledHoverableMenuItemBase
data-testid={testId ?? undefined} data-testid={testId ?? undefined}
onClick={onClick} onClick={handleMenuItemClick}
className={className} className={className}
accent={accent} accent={accent}
isMenuOpen={!!isTooltipOpen} isMenuOpen={!!isTooltipOpen}

View File

@ -1,11 +1,12 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useRecoilState } from 'recoil'; import { useRecoilState, useRecoilValue } from 'recoil';
import { FieldContext } from '@/ui/object/field/contexts/FieldContext'; import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
import { useIsFieldEmpty } from '@/ui/object/field/hooks/useIsFieldEmpty'; import { useIsFieldEmpty } from '@/ui/object/field/hooks/useIsFieldEmpty';
import { entityFieldInitialValueFamilyState } from '@/ui/object/field/states/entityFieldInitialValueFamilyState'; import { entityFieldInitialValueFamilyState } from '@/ui/object/field/states/entityFieldInitialValueFamilyState';
import { FieldInitialValue } from '@/ui/object/field/types/FieldInitialValue'; import { FieldInitialValue } from '@/ui/object/field/types/FieldInitialValue';
import { useRecordTableScopedStates } from '@/ui/object/record-table/hooks/internal/useRecordTableScopedStates';
import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect'; import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
@ -22,8 +23,13 @@ const DEFAULT_CELL_SCOPE: HotkeyScope = {
}; };
export const useTableCell = () => { export const useTableCell = () => {
const { setCurrentTableCellInEditMode } = useCurrentTableCellEditMode(); const { objectMetadataConfigState } = useRecordTableScopedStates();
const objectMetadataConfig = useRecoilValue(objectMetadataConfigState);
const basePathToShowPage = objectMetadataConfig?.basePathToShowPage;
const { setCurrentTableCellInEditMode } = useCurrentTableCellEditMode();
const setHotkeyScope = useSetHotkeyScope(); const setHotkeyScope = useSetHotkeyScope();
const { setDragSelectionStartEnabled } = useDragSelect(); const { setDragSelectionStartEnabled } = useDragSelect();
@ -37,8 +43,7 @@ export const useTableCell = () => {
const isEmpty = useIsFieldEmpty(); const isEmpty = useIsFieldEmpty();
const { entityId, fieldDefinition, basePathToShowPage } = const { entityId, fieldDefinition } = useContext(FieldContext);
useContext(FieldContext);
const [, setFieldInitialValue] = useRecoilState( const [, setFieldInitialValue] = useRecoilState(
entityFieldInitialValueFamilyState({ entityFieldInitialValueFamilyState({