Feat/editable fields update (#743)

* Removed console log

* Used current scope as default parent scope for fields

* Finished editable fields on people show page

* Added stories

* Console log

* Lint
This commit is contained in:
Lucas Bordeau 2023-07-19 02:43:16 +02:00 committed by GitHub
parent 5ee8eaa985
commit 7ecb098c55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 521 additions and 49 deletions

View File

@ -2160,7 +2160,7 @@ export type GetPersonQueryVariables = Exact<{
}>;
export type GetPersonQuery = { __typename?: 'Query', findUniquePerson: { __typename?: 'Person', id: string, firstName: string, lastName: string, displayName: string, email: string, createdAt: string, _commentThreadCount: number, company?: { __typename?: 'Company', id: string } | null } };
export type GetPersonQuery = { __typename?: 'Query', findUniquePerson: { __typename?: 'Person', id: string, firstName: string, lastName: string, displayName: string, email: string, createdAt: string, city: string, phone: string, _commentThreadCount: number, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null } };
export type UpdatePeopleMutationVariables = Exact<{
id?: InputMaybe<Scalars['String']>;
@ -3629,9 +3629,13 @@ export const GetPersonDocument = gql`
displayName
email
createdAt
city
phone
_commentThreadCount
company {
id
name
domainName
}
}
}

View File

@ -2,7 +2,6 @@ import styled from '@emotion/styled';
import { CompanyAccountOwnerPicker } from '@/companies/components/CompanyAccountOwnerPicker';
import { useEditableField } from '@/ui/editable-field/hooks/useEditableField';
import { HotkeyScope } from '@/ui/hotkey/types/HotkeyScope';
import { Company, User } from '~/generated/graphql';
const CompanyAccountOwnerPickerContainer = styled.div`
@ -17,7 +16,6 @@ export type OwnProps = {
};
onSubmit?: () => void;
onCancel?: () => void;
parentHotkeyScope?: HotkeyScope;
};
export function CompanyAccountOwnerPickerFieldEditMode({

View File

@ -1,8 +1,8 @@
import { useEffect, useState } from 'react';
import { EditableField } from '@/ui/editable-field/components/EditableField';
import { EditableFieldEditModeDate } from '@/ui/editable-field/components/EditableFieldEditModeDate';
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
import { EditableFieldEditModeDate } from '@/ui/editable-field/variants/components/EditableFieldEditModeDate';
import { IconCalendar } from '@/ui/icon';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
import { Company, useUpdateCompanyMutation } from '~/generated/graphql';
@ -56,6 +56,7 @@ export function CompanyCreatedAtEditableField({ company }: OwnProps) {
? formatToHumanReadableDate(parseDate(internalValue).toJSDate())
: 'No date'
}
isDisplayModeContentEmpty={!(internalValue !== '')}
/>
</RecoilScope>
);

View File

@ -56,6 +56,7 @@ export function CompanyDomainNameEditableField({ company }: OwnProps) {
}
displayModeContent={<FieldDisplayURL URL={internalValue} />}
useEditButton
isDisplayModeContentEmpty={!(internalValue !== '')}
/>
</RecoilScope>
);

View File

@ -70,6 +70,7 @@ export function CompanyEmployeesEditableField({ company }: OwnProps) {
/>
}
displayModeContent={internalValue}
isDisplayModeContentEmpty={!(internalValue && internalValue !== '0')}
/>
</RecoilScope>
);

View File

@ -0,0 +1,84 @@
import {
IconCalendar,
IconMail,
IconMap,
IconPhone,
} from '@tabler/icons-react';
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
import { DateEditableField } from '@/ui/editable-field/variants/components/DateEditableField';
import { PhoneEditableField } from '@/ui/editable-field/variants/components/PhoneEditableField';
import { TextEditableField } from '@/ui/editable-field/variants/components/TextEditableField';
import { Company, Person, useUpdatePeopleMutation } from '~/generated/graphql';
import { PeopleCompanyEditableField } from '../editable-field/components/PeopleCompanyEditableField';
type OwnProps = {
person: Pick<
Person,
'id' | 'city' | 'email' | 'displayName' | 'phone' | 'createdAt'
> & {
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
};
};
export function PersonPropertyBox({ person }: OwnProps) {
const [updatePerson] = useUpdatePeopleMutation();
return (
<PropertyBox extraPadding={true}>
<TextEditableField
value={person.email}
icon={<IconMail />}
placeholder={'Email'}
onSubmit={(newEmail) => {
updatePerson({
variables: {
id: person.id,
email: newEmail,
},
});
}}
/>
<PhoneEditableField
value={person.phone}
icon={<IconPhone />}
placeholder={'Phone'}
onSubmit={(newPhone) => {
updatePerson({
variables: {
id: person.id,
phone: newPhone,
},
});
}}
/>
<DateEditableField
value={person.createdAt}
icon={<IconCalendar />}
onSubmit={(newDate) => {
updatePerson({
variables: {
id: person.id,
createdAt: newDate,
},
});
}}
/>
<PeopleCompanyEditableField people={person} />
<TextEditableField
value={person.city}
icon={<IconMap />}
placeholder={'City'}
onSubmit={(newCity) => {
updatePerson({
variables: {
id: person.id,
city: newCity,
},
});
}}
/>
</PropertyBox>
);
}

View File

@ -0,0 +1,47 @@
import { IconBuildingSkyscraper } from '@tabler/icons-react';
import { CompanyChip } from '@/companies/components/CompanyChip';
import { EditableField } from '@/ui/editable-field/components/EditableField';
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
import { RelationPickerHotkeyScope } from '@/ui/relation-picker/types/RelationPickerHotkeyScope';
import { Company, Person } from '~/generated/graphql';
import { getLogoUrlFromDomainName } from '~/utils';
import { PeopleCompanyEditableFieldEditMode } from './PeopleCompanyEditableFieldEditMode';
export type OwnProps = {
people: Pick<Person, 'id'> & {
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
};
};
export function PeopleCompanyEditableField({ people }: OwnProps) {
return (
<RecoilScope SpecificContext={FieldContext}>
<RecoilScope>
<EditableField
customEditHotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
iconLabel={<IconBuildingSkyscraper />}
editModeContent={
<PeopleCompanyEditableFieldEditMode people={people} />
}
displayModeContent={
people.company ? (
<CompanyChip
id={people.company.id}
name={people.company.name}
picture={getLogoUrlFromDomainName(people.company.domainName)}
/>
) : (
<></>
)
}
isDisplayModeContentEmpty={!people.company}
/>
</RecoilScope>
</RecoilScope>
);
}

View File

@ -0,0 +1,51 @@
import { useFilteredSearchCompanyQuery } from '@/companies/queries';
import { useEditableField } from '@/ui/editable-field/hooks/useEditableField';
import { useRecoilScopedState } from '@/ui/recoil-scope/hooks/useRecoilScopedState';
import { SingleEntitySelect } from '@/ui/relation-picker/components/SingleEntitySelect';
import { relationPickerSearchFilterScopedState } from '@/ui/relation-picker/states/relationPickerSearchFilterScopedState';
import { EntityForSelect } from '@/ui/relation-picker/types/EntityForSelect';
import { Company, Person, useUpdatePeopleMutation } from '~/generated/graphql';
export type OwnProps = {
people: Pick<Person, 'id'> & { company?: Pick<Company, 'id'> | null };
};
export function PeopleCompanyEditableFieldEditMode({ people }: OwnProps) {
const { closeEditableField } = useEditableField();
const [searchFilter] = useRecoilScopedState(
relationPickerSearchFilterScopedState,
);
const [updatePeople] = useUpdatePeopleMutation();
const companies = useFilteredSearchCompanyQuery({
searchFilter,
selectedIds: people.company?.id ? [people.company.id] : [],
});
async function handleEntitySelected(entity: EntityForSelect) {
await updatePeople({
variables: {
...people,
companyId: entity.id,
},
});
closeEditableField();
}
function handleCancel() {
closeEditableField();
}
return (
<SingleEntitySelect
onEntitySelected={handleEntitySelected}
entities={{
entitiesToSelect: companies.entitiesToSelect,
selectedEntity: companies.selectedEntities[0],
loading: companies.loading,
}}
onCancel={handleCancel}
/>
);
}

View File

@ -11,9 +11,13 @@ export const GET_PERSON = gql`
displayName
email
createdAt
city
phone
_commentThreadCount
company {
id
name
domainName
}
}
}

View File

@ -1,7 +1,7 @@
import { formatToHumanReadableDate } from '~/utils';
type OwnProps = {
value: Date;
value: Date | null;
};
export function InplaceInputDateDisplayMode({ value }: OwnProps) {

View File

@ -16,7 +16,6 @@ export const EditableFieldNormalModeOuterContainer = styled.div<
padding: ${({ theme }) => theme.spacing(1)};
${(props) => {
console.log(props.isDisplayModeContentEmpty);
if (props.isDisplayModeContentEmpty) {
return css`
min-width: 50px;

View File

@ -1,8 +1,13 @@
import { useEffect } from 'react';
import { useRecoilCallback } from 'recoil';
import { currentHotkeyScopeState } from '@/ui/hotkey/states/internal/currentHotkeyScopeState';
import { HotkeyScope } from '@/ui/hotkey/types/HotkeyScope';
import { isSameHotkeyScope } from '@/ui/hotkey/utils/isSameHotkeyScope';
import { useContextScopeId } from '@/ui/recoil-scope/hooks/useContextScopeId';
import { useRecoilScopedState } from '@/ui/recoil-scope/hooks/useRecoilScopedState';
import { getSnapshotScopedState } from '@/ui/recoil-scope/utils/getSnapshotScopedState';
import { getSnapshotState } from '@/ui/recoil-scope/utils/getSnapshotState';
import { customEditHotkeyScopeForFieldScopedState } from '../states/customEditHotkeyScopeForFieldScopedState';
import { FieldContext } from '../states/FieldContext';
@ -21,8 +26,7 @@ export function useBindFieldHotkeyScope({
FieldContext,
);
const [parentHotkeyScopeForField, setParentHotkeyScopeForField] =
useRecoilScopedState(parentHotkeyScopeForFieldScopedState, FieldContext);
const fieldContextScopeId = useContextScopeId(FieldContext);
useEffect(() => {
if (
@ -37,16 +41,35 @@ export function useBindFieldHotkeyScope({
setCustomEditHotkeyScopeForField,
]);
const setParentHotkeyScopeForField = useRecoilCallback(
({ snapshot, set }) =>
(parentHotkeyScopeToSet: HotkeyScope | null | undefined) => {
const currentHotkeyScope = getSnapshotState({
snapshot,
state: currentHotkeyScopeState,
});
const parentHotkeyScopeForField = getSnapshotScopedState({
snapshot,
state: parentHotkeyScopeForFieldScopedState,
contextScopeId: fieldContextScopeId,
});
if (!parentHotkeyScopeToSet) {
set(
parentHotkeyScopeForFieldScopedState(fieldContextScopeId),
currentHotkeyScope,
);
} else if (
!isSameHotkeyScope(parentHotkeyScopeToSet, parentHotkeyScopeForField)
) {
setParentHotkeyScopeForField(parentHotkeyScopeToSet);
}
},
[fieldContextScopeId],
);
useEffect(() => {
if (
parentHotkeyScope &&
!isSameHotkeyScope(parentHotkeyScope, parentHotkeyScopeForField)
) {
setParentHotkeyScopeForField(parentHotkeyScope);
}
}, [
parentHotkeyScope,
parentHotkeyScopeForField,
setParentHotkeyScopeForField,
]);
setParentHotkeyScopeForField(parentHotkeyScope);
}, [parentHotkeyScope, setParentHotkeyScopeForField]);
}

View File

@ -7,7 +7,6 @@ import { isFieldInEditModeScopedState } from '../states/isFieldInEditModeScopedS
import { parentHotkeyScopeForFieldScopedState } from '../states/parentHotkeyScopeForFieldScopedState';
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
// TODO: use atoms for hotkey scopes
export function useEditableField() {
const [isFieldInEditMode, setIsFieldInEditMode] = useRecoilScopedState(
isFieldInEditModeScopedState,

View File

@ -0,0 +1,65 @@
import { useEffect, useState } from 'react';
import { InplaceInputDateDisplayMode } from '@/ui/display/component/InplaceInputDateDisplayMode';
import { EditableField } from '@/ui/editable-field/components/EditableField';
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
import { parseDate } from '~/utils/date-utils';
import { EditableFieldEditModeDate } from './EditableFieldEditModeDate';
type OwnProps = {
icon?: React.ReactNode;
value: string | null | undefined;
onSubmit?: (newValue: string) => void;
};
export function DateEditableField({ icon, value, onSubmit }: OwnProps) {
const [internalValue, setInternalValue] = useState(value);
useEffect(() => {
setInternalValue(value);
}, [value]);
async function handleChange(newValue: string) {
setInternalValue(newValue);
onSubmit?.(newValue);
}
async function handleSubmit() {
if (!internalValue) return;
onSubmit?.(internalValue);
}
async function handleCancel() {
setInternalValue(value);
}
const internalDateValue = internalValue
? parseDate(internalValue).toJSDate()
: null;
return (
<RecoilScope SpecificContext={FieldContext}>
<EditableField
onSubmit={handleSubmit}
onCancel={handleCancel}
iconLabel={icon}
editModeContent={
<EditableFieldEditModeDate
value={internalValue ?? ''}
onChange={(newValue: string) => {
handleChange(newValue);
}}
/>
}
displayModeContent={
<InplaceInputDateDisplayMode value={internalDateValue} />
}
isDisplayModeContentEmpty={!(internalValue !== '')}
/>
</RecoilScope>
);
}

View File

@ -2,7 +2,7 @@ import { HotkeyScope } from '@/ui/hotkey/types/HotkeyScope';
import { InplaceInputDate } from '@/ui/inplace-input/components/InplaceInputDate';
import { parseDate } from '~/utils/date-utils';
import { useEditableField } from '../hooks/useEditableField';
import { useEditableField } from '../../hooks/useEditableField';
type OwnProps = {
value: string;

View File

@ -0,0 +1,66 @@
import { useEffect, useState } from 'react';
import { InplaceInputPhoneDisplayMode } from '@/ui/display/component/InplaceInputPhoneDisplayMode';
import { EditableField } from '@/ui/editable-field/components/EditableField';
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
import { InplaceInputText } from '@/ui/inplace-input/components/InplaceInputText';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
type OwnProps = {
icon?: React.ReactNode;
placeholder?: string;
value: string | null | undefined;
onSubmit?: (newValue: string) => void;
};
export function PhoneEditableField({
icon,
placeholder,
value,
onSubmit,
}: OwnProps) {
const [internalValue, setInternalValue] = useState(value);
useEffect(() => {
setInternalValue(value);
}, [value]);
async function handleChange(newValue: string) {
setInternalValue(newValue);
}
async function handleSubmit() {
if (!internalValue) return;
onSubmit?.(internalValue);
}
async function handleCancel() {
setInternalValue(value);
}
return (
<RecoilScope SpecificContext={FieldContext}>
<EditableField
onSubmit={handleSubmit}
onCancel={handleCancel}
iconLabel={icon}
editModeContent={
<InplaceInputText
placeholder={placeholder ?? ''}
autoFocus
value={internalValue ?? ''}
onChange={(newValue: string) => {
handleChange(newValue);
}}
/>
}
displayModeContent={
<InplaceInputPhoneDisplayMode value={internalValue ?? ''} />
}
isDisplayModeContentEmpty={!(internalValue !== '')}
useEditButton
/>
</RecoilScope>
);
}

View File

@ -0,0 +1,23 @@
import type { Meta, StoryObj } from '@storybook/react';
import { IconCalendar } from '@tabler/icons-react';
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
import { DateEditableField } from '../DateEditableField';
const meta: Meta<typeof DateEditableField> = {
title: 'UI/EditableField/DateEditableField',
component: DateEditableField,
};
export default meta;
type Story = StoryObj<typeof DateEditableField>;
export const Default: Story = {
render: getRenderWrapperForComponent(
<DateEditableField
value={new Date().toISOString()}
icon={<IconCalendar />}
/>,
),
};

View File

@ -0,0 +1,24 @@
import type { Meta, StoryObj } from '@storybook/react';
import { IconCurrencyDollar } from '@tabler/icons-react';
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
import { NumberEditableField } from '../NumberEditableField';
const meta: Meta<typeof NumberEditableField> = {
title: 'UI/EditableField/NumberEditableField',
component: NumberEditableField,
};
export default meta;
type Story = StoryObj<typeof NumberEditableField>;
export const Default: Story = {
render: getRenderWrapperForComponent(
<NumberEditableField
value={10}
icon={<IconCurrencyDollar />}
placeholder="Number"
/>,
),
};

View File

@ -0,0 +1,27 @@
import { BrowserRouter } from 'react-router-dom';
import type { Meta, StoryObj } from '@storybook/react';
import { IconPhone } from '@tabler/icons-react';
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
import { PhoneEditableField } from '../PhoneEditableField';
const meta: Meta<typeof PhoneEditableField> = {
title: 'UI/EditableField/PhoneEditableField',
component: PhoneEditableField,
};
export default meta;
type Story = StoryObj<typeof PhoneEditableField>;
export const Default: Story = {
render: getRenderWrapperForComponent(
<BrowserRouter>
<PhoneEditableField
value={'+33714446494'}
icon={<IconPhone />}
placeholder="Phone"
/>
</BrowserRouter>,
),
};

View File

@ -0,0 +1,24 @@
import type { Meta, StoryObj } from '@storybook/react';
import { IconUser } from '@tabler/icons-react';
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
import { TextEditableField } from '../TextEditableField';
const meta: Meta<typeof TextEditableField> = {
title: 'UI/EditableField/TextEditableField',
component: TextEditableField,
};
export default meta;
type Story = StoryObj<typeof TextEditableField>;
export const Default: Story = {
render: getRenderWrapperForComponent(
<TextEditableField
value={'John Doe'}
icon={<IconUser />}
placeholder="Name"
/>,
),
};

View File

@ -1,5 +1,5 @@
import { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { useRecoilCallback } from 'recoil';
import { currentHotkeyScopeState } from '../states/internal/currentHotkeyScopeState';
import { CustomHotkeyScopes } from '../types/CustomHotkeyScope';
@ -13,8 +13,6 @@ export function usePreviousHotkeyScope() {
const setHotkeyScope = useSetHotkeyScope();
const currentHotkeyScope = useRecoilValue(currentHotkeyScopeState);
function goBackToPreviousHotkeyScope() {
if (previousHotkeyScope) {
setHotkeyScope(
@ -24,13 +22,19 @@ export function usePreviousHotkeyScope() {
}
}
function setHotkeyScopeAndMemorizePreviousScope(
scope: string,
customScopes?: CustomHotkeyScopes,
) {
setPreviousHotkeyScope(currentHotkeyScope);
setHotkeyScope(scope, customScopes);
}
const setHotkeyScopeAndMemorizePreviousScope = useRecoilCallback(
({ snapshot }) =>
(scope: string, customScopes?: CustomHotkeyScopes) => {
const currentHotkeyScope = snapshot
.getLoadable(currentHotkeyScopeState)
.valueOrThrow();
setHotkeyScope(scope, customScopes);
setPreviousHotkeyScope(currentHotkeyScope);
},
[setPreviousHotkeyScope],
);
return {
setHotkeyScopeAndMemorizePreviousScope,

View File

@ -19,12 +19,12 @@ function isCustomScopesEqual(
export function useSetHotkeyScope() {
return useRecoilCallback(
({ snapshot, set }) =>
async (HotkeyScopeToSet: string, customScopes?: CustomHotkeyScopes) => {
async (hotkeyScopeToSet: string, customScopes?: CustomHotkeyScopes) => {
const currentHotkeyScope = await snapshot.getPromise(
currentHotkeyScopeState,
);
if (currentHotkeyScope.scope === HotkeyScopeToSet) {
if (currentHotkeyScope.scope === hotkeyScopeToSet) {
if (!isDefined(customScopes)) {
if (
isCustomScopesEqual(
@ -47,7 +47,7 @@ export function useSetHotkeyScope() {
}
set(currentHotkeyScopeState, {
scope: HotkeyScopeToSet,
scope: hotkeyScopeToSet,
customScopes: {
commandMenu: customScopes?.commandMenu ?? true,
goto: customScopes?.goto ?? false,

View File

@ -48,7 +48,7 @@ export const DatePickerContainer = ({ children }: DatePickerContainerProps) => {
};
type OwnProps = {
value: Date;
value: Date | null | undefined;
onChange: (newDate: Date) => void;
};
@ -56,7 +56,7 @@ export function InplaceInputDate({ onChange, value }: OwnProps) {
return (
<InplaceInputContainer>
<DatePicker
date={value}
date={value ?? new Date()}
onChangeHandler={onChange}
customInput={<DateDisplay />}
customCalendarContainer={DatePickerContainer}

View File

@ -0,0 +1,12 @@
import { Context, useContext } from 'react';
export function useContextScopeId(SpecificContext: Context<string | null>) {
const recoilScopeId = useContext(SpecificContext);
if (!recoilScopeId)
throw new Error(
`Using useContextScopedId outside of the specified context : ${SpecificContext.displayName}, verify that you are using a RecoilScope with the specific context you want to use.`,
);
return recoilScopeId;
}

View File

@ -0,0 +1,13 @@
import { RecoilState, Snapshot } from 'recoil';
export function getSnapshotScopedState<T>({
snapshot,
state,
contextScopeId,
}: {
snapshot: Snapshot;
state: (scopeId: string) => RecoilState<T>;
contextScopeId: string;
}) {
return snapshot.getLoadable(state(contextScopeId)).valueOrThrow();
}

View File

@ -0,0 +1,11 @@
import { RecoilState, Snapshot } from 'recoil';
export function getSnapshotState<T>({
snapshot,
state,
}: {
snapshot: Snapshot;
state: RecoilState<T>;
}) {
return snapshot.getLoadable(state).valueOrThrow();
}

View File

@ -25,6 +25,7 @@ export function EditableCellDateEditMode({
function handleDateChange(newDate: Date) {
onChange(newDate);
closeEditableCell();
}

View File

@ -112,7 +112,6 @@ export function PasswordLogin() {
}
navigate('/auth/create/workspace');
} catch (err: any) {
console.log('err', err);
enqueueSnackBar(err?.message, {
variant: 'error',
});

View File

@ -99,7 +99,6 @@ export const EditNote: Story = {
graphql.query(
getOperationName(GET_COMMENT_THREAD) ?? '',
(req, res, ctx) => {
console.log('coucou');
return res(
ctx.data({
findManyCommentThreads: [mockedCommentThreads[0]],

View File

@ -2,10 +2,9 @@ import { useParams } from 'react-router-dom';
import { useTheme } from '@emotion/react';
import { Timeline } from '@/activities/timeline/components/Timeline';
import { PersonPropertyBox } from '@/people/components/PersonPropertyBox';
import { usePersonQuery } from '@/people/queries';
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
import { PropertyBoxItem } from '@/ui/editable-field/property-box/components/PropertyBoxItem';
import { IconLink, IconUser } from '@/ui/icon';
import { IconUser } from '@/ui/icon';
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer';
import { ShowPageRightContainer } from '@/ui/layout/show-page/components/ShowPageRightContainer';
@ -33,14 +32,7 @@ export function PersonShow() {
title={person?.displayName ?? 'No name'}
date={person?.createdAt ?? ''}
/>
<PropertyBox extraPadding={true}>
<>
<PropertyBoxItem
icon={<IconLink />}
value={person?.firstName ?? 'No First name'}
/>
</>
</PropertyBox>
{person && <PersonPropertyBox person={person} />}
</ShowPageLeftContainer>
<ShowPageRightContainer>
<Timeline