Merge commit 'cd3a32e55503dc1e6b9873d812dd401bf7d51045' into context-menu-vertical

This commit is contained in:
brendanlaschke
2023-08-14 22:00:49 +02:00
179 changed files with 1971 additions and 3230 deletions

View File

@ -1,7 +1,6 @@
import { useEffect } from 'react';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { v4 } from 'uuid';
import { useOpenActionBar } from '@/people/hooks/useOpenActionBar';
@ -11,8 +10,9 @@ import { IconUser } from '@/ui/icon';
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
import { EntityTableActionBar } from '@/ui/table/action-bar/components/EntityTableActionBar';
import { EntityTableContextMenu } from '@/ui/table/context-menu/components/EntityTableContextMenu';
import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem';
import { useUpsertTableRowId } from '@/ui/table/hooks/useUpsertTableRowId';
import { TableContext } from '@/ui/table/states/TableContext';
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useInsertOnePersonMutation } from '~/generated/graphql';
@ -23,7 +23,8 @@ const StyledTableContainer = styled.div`
export function People() {
const [insertOnePerson] = useInsertOnePersonMutation();
const [tableRowIds, setTableRowIds] = useRecoilState(tableRowIdsState);
const upsertEntityTableItem = useUpsertEntityTableItem();
const upsertTableRowIds = useUpsertTableRowId();
async function handleAddButtonClick() {
const newPersonId: string = v4();
@ -47,8 +48,10 @@ export function People() {
},
},
update: (cache, { data }) => {
data?.createOnePerson?.id &&
setTableRowIds([data?.createOnePerson.id, ...tableRowIds]);
if (data?.createOnePerson) {
upsertTableRowIds(data?.createOnePerson.id);
upsertEntityTableItem(data?.createOnePerson);
}
},
});
}

View File

@ -3,34 +3,44 @@ import { getOperationName } from '@apollo/client/utilities';
import { useTheme } from '@emotion/react';
import { Timeline } from '@/activities/timeline/components/Timeline';
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
import { useFavorites } from '@/favorites/hooks/useFavorites';
import { PersonPropertyBox } from '@/people/components/PersonPropertyBox';
import { GET_PERSON, usePersonQuery } from '@/people/queries';
import { GenericEditableField } from '@/ui/editable-field/components/GenericEditableField';
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
import { EditableFieldDefinitionContext } from '@/ui/editable-field/states/EditableFieldDefinitionContext';
import { EditableFieldEntityIdContext } from '@/ui/editable-field/states/EditableFieldEntityIdContext';
import { EditableFieldMutationContext } from '@/ui/editable-field/states/EditableFieldMutationContext';
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';
import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSummaryCard';
import {
CommentableType,
useUpdateOnePersonMutation,
useUploadPersonPictureMutation,
} from '~/generated/graphql';
import { PeopleFullNameEditableField } from '../../modules/people/editable-field/components/PeopleFullNameEditableField';
import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageContainer';
import { personShowFieldDefinition } from './constants/personShowFieldDefinition';
export function PersonShow() {
const personId = useParams().personId ?? '';
const { insertPersonFavorite, deletePersonFavorite } = useFavorites();
const theme = useTheme();
const { data } = usePersonQuery(personId);
const person = data?.findUniquePerson;
const isFavorite =
person?.Favorite && person?.Favorite?.length > 0 ? true : false;
const theme = useTheme();
const [uploadPicture] = useUploadPersonPictureMutation();
if (!person) return <></>;
const isFavorite =
person.Favorite && person.Favorite?.length > 0 ? true : false;
async function onUploadPicture(file: File) {
if (!file || !person?.id) {
return;
@ -38,7 +48,7 @@ export function PersonShow() {
await uploadPicture({
variables: {
file,
id: person?.id,
id: person.id,
},
refetchQueries: [getOperationName(GET_PERSON) ?? ''],
});
@ -51,7 +61,7 @@ export function PersonShow() {
return (
<WithTopBarContainer
title={person?.firstName ?? ''}
title={person.firstName ?? ''}
icon={<IconUser size={theme.icon.size.md} />}
hasBackButton
isFavorite={isFavorite}
@ -60,20 +70,40 @@ export function PersonShow() {
<ShowPageContainer>
<ShowPageLeftContainer>
<ShowPageSummaryCard
id={person?.id}
title={person?.displayName ?? 'No name'}
logoOrAvatar={person?.avatarUrl ?? undefined}
date={person?.createdAt ?? ''}
id={person.id}
title={person.displayName ?? 'No name'}
logoOrAvatar={person.avatarUrl ?? undefined}
date={person.createdAt ?? ''}
renderTitleEditComponent={() =>
person ? <PeopleFullNameEditableField people={person} /> : <></>
}
onUploadPicture={onUploadPicture}
/>
{person && <PersonPropertyBox person={person} />}
<PropertyBox extraPadding={true}>
<EditableFieldMutationContext.Provider
value={useUpdateOnePersonMutation}
>
<EditableFieldEntityIdContext.Provider value={person.id}>
{personShowFieldDefinition.map((fieldDefinition) => {
return (
<EditableFieldDefinitionContext.Provider
value={fieldDefinition}
key={fieldDefinition.id}
>
<GenericEditableField />
</EditableFieldDefinitionContext.Provider>
);
})}
</EditableFieldEntityIdContext.Provider>
</EditableFieldMutationContext.Provider>
</PropertyBox>
</ShowPageLeftContainer>
<ShowPageRightContainer>
<Timeline
entity={{ id: person?.id ?? '', type: CommentableType.Person }}
entity={{
id: person.id ?? '',
type: ActivityTargetableEntityType.Person,
}}
/>
</ShowPageRightContainer>
</ShowPageContainer>

View File

@ -3,6 +3,7 @@ import type { Meta } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library';
import assert from 'assert';
import { AppPath } from '@/types/AppPath';
import {
PageDecorator,
type PageDecoratorArgs,
@ -18,7 +19,7 @@ const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/People/FilterBy',
component: People,
decorators: [PageDecorator],
args: { currentPath: '/people' },
args: { routePath: AppPath.PeoplePage },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,

View File

@ -6,6 +6,7 @@ import { graphql } from 'msw';
import { UPDATE_ONE_PERSON } from '@/people/queries';
import { SEARCH_COMPANY_QUERY } from '@/search/queries/search';
import { AppPath } from '@/types/AppPath';
import { Company } from '~/generated/graphql';
import {
PageDecorator,
@ -25,7 +26,7 @@ const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/People/Input',
component: People,
decorators: [PageDecorator],
args: { currentPath: '/people' },
args: { routePath: AppPath.PeoplePage },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,
@ -216,14 +217,6 @@ export const EditRelation: Story = {
await userEvent.click(airbnbChip);
});
await step(
'Click on last row company cell to exit relation picker',
async () => {
const otherCell = await canvas.findByText('Janice Dane');
await userEvent.click(otherCell);
},
);
await step('Check if Airbnb is in second row company cell', async () => {
await canvas.findByText('Airbnb');
});

View File

@ -2,6 +2,7 @@ import { expect } from '@storybook/jest';
import type { Meta } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library';
import { AppPath } from '@/types/AppPath';
import {
PageDecorator,
type PageDecoratorArgs,
@ -17,7 +18,7 @@ const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/People/SortBy',
component: People,
decorators: [PageDecorator],
args: { currentPath: '/people' },
args: { routePath: AppPath.PeoplePage },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,

View File

@ -1,5 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react';
import { AppPath } from '@/types/AppPath';
import {
PageDecorator,
type PageDecoratorArgs,
@ -12,7 +13,7 @@ const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/People',
component: People,
decorators: [PageDecorator],
args: { currentPath: '/people' },
args: { routePath: AppPath.PeoplePage },
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,

View File

@ -1,6 +1,6 @@
import { Route, Routes } from 'react-router-dom';
import type { Meta, StoryObj } from '@storybook/react';
import { AppPath } from '@/types/AppPath';
import {
PageDecorator,
type PageDecoratorArgs,
@ -13,15 +13,11 @@ import { PersonShow } from '../PersonShow';
const meta: Meta<PageDecoratorArgs> = {
title: 'Pages/People/Person',
component: PersonShow,
decorators: [
(Story) => (
<Routes>
<Route path="/person/:personId" element={<Story />} />
</Routes>
),
PageDecorator,
],
args: { currentPath: `/person/${mockedPeopleData[0].id}` },
decorators: [PageDecorator],
args: {
routePath: AppPath.PersonShowPage,
routeParams: { ':personId': mockedPeopleData[0].id },
},
parameters: {
docs: { story: 'inline', iframeHeight: '500px' },
msw: graphqlMocks,

View File

@ -0,0 +1,69 @@
import { FieldDefinition } from '@/ui/editable-field/types/FieldDefinition';
import {
FieldDateMetadata,
FieldMetadata,
FieldPhoneMetadata,
FieldRelationMetadata,
FieldTextMetadata,
} from '@/ui/editable-field/types/FieldMetadata';
import {
IconBuildingSkyscraper,
IconCalendar,
IconMail,
IconMap,
IconPhone,
} from '@/ui/icon';
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
export const personShowFieldDefinition: FieldDefinition<FieldMetadata>[] = [
{
id: 'email',
label: 'Email',
icon: <IconMail />,
type: 'text',
metadata: {
fieldName: 'email',
placeHolder: 'Email',
},
} satisfies FieldDefinition<FieldTextMetadata>,
{
id: 'phone',
label: 'Phone',
icon: <IconPhone />,
type: 'phone',
metadata: {
fieldName: 'phone',
placeHolder: 'Phone',
},
} satisfies FieldDefinition<FieldPhoneMetadata>,
{
id: 'createdAt',
label: 'Created at',
icon: <IconCalendar />,
type: 'date',
metadata: {
fieldName: 'createdAt',
},
} satisfies FieldDefinition<FieldDateMetadata>,
{
id: 'company',
label: 'Company',
icon: <IconBuildingSkyscraper />,
type: 'relation',
metadata: {
fieldName: 'company',
relationType: Entity.Company,
useEditButton: true,
},
} satisfies FieldDefinition<FieldRelationMetadata>,
{
id: 'city',
label: 'City',
icon: <IconMap />,
type: 'text',
metadata: {
fieldName: 'city',
placeHolder: 'City',
},
} satisfies FieldDefinition<FieldTextMetadata>,
];