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,73 +0,0 @@
import { useFilteredSearchCompanyQuery } from '@/companies/queries';
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 { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell';
import { isCreateModeScopedState } from '@/ui/table/editable-cell/states/isCreateModeScopedState';
import { TableHotkeyScope } from '@/ui/table/types/TableHotkeyScope';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import {
Company,
Person,
useUpdateOnePersonMutation,
} from '~/generated/graphql';
export type OwnProps = {
people: Pick<Person, 'id'> & { company?: Pick<Company, 'id'> | null };
};
export function PeopleCompanyPicker({ people }: OwnProps) {
const [, setIsCreating] = useRecoilScopedState(isCreateModeScopedState);
const [searchFilter] = useRecoilScopedState(
relationPickerSearchFilterScopedState,
);
const [updatePerson] = useUpdateOnePersonMutation();
const { closeEditableCell } = useEditableCell();
const addToScopeStack = useSetHotkeyScope();
const companies = useFilteredSearchCompanyQuery({
searchFilter,
selectedIds: people.company?.id ? [people.company.id] : [],
});
async function handleEntitySelected(
entity: EntityForSelect | null | undefined,
) {
if (entity) {
await updatePerson({
variables: {
where: {
id: people.id,
},
data: {
company: { connect: { id: entity.id } },
},
},
});
}
closeEditableCell();
}
function handleCreate() {
setIsCreating(true);
addToScopeStack(TableHotkeyScope.CellDoubleTextInput);
}
return (
<SingleEntitySelect
onCreate={handleCreate}
onCancel={() => closeEditableCell()}
onEntitySelected={handleEntitySelected}
entities={{
entitiesToSelect: companies.entitiesToSelect,
selectedEntity: companies.selectedEntities[0],
loading: companies.loading,
}}
/>
);
}

View File

@ -1,104 +0,0 @@
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,
useUpdateOnePersonMutation,
} 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] = useUpdateOnePersonMutation();
return (
<PropertyBox extraPadding={true}>
<TextEditableField
value={person.email}
icon={<IconMail />}
placeholder={'Email'}
onSubmit={(newEmail) => {
updatePerson({
variables: {
where: {
id: person.id,
},
data: {
email: newEmail,
},
},
});
}}
/>
<PhoneEditableField
value={person.phone}
icon={<IconPhone />}
placeholder={'Phone'}
onSubmit={(newPhone) => {
updatePerson({
variables: {
where: {
id: person.id,
},
data: {
phone: newPhone,
},
},
});
}}
/>
<DateEditableField
value={person.createdAt}
icon={<IconCalendar />}
onSubmit={(newDate) => {
updatePerson({
variables: {
where: {
id: person.id,
},
data: {
createdAt: newDate,
},
},
});
}}
/>
<PeopleCompanyEditableField people={person} />
<TextEditableField
value={person.city}
icon={<IconMap />}
placeholder={'City'}
onSubmit={(newCity) => {
updatePerson({
variables: {
where: {
id: person.id,
},
data: {
city: newCity,
},
},
});
}}
/>
</PropertyBox>
);
}

View File

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

View File

@ -1,49 +0,0 @@
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 { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
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
useEditButton
customEditHotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
iconLabel={<IconBuildingSkyscraper />}
editModeContent={
<PeopleCompanyEditableFieldEditMode people={people} />
}
displayModeContent={
people.company ? (
<CompanyChip
id={people.company.id}
name={people.company.name}
pictureUrl={getLogoUrlFromDomainName(people.company.domainName)}
/>
) : (
<></>
)
}
isDisplayModeContentEmpty={!people.company}
isDisplayModeFixHeight
/>
</RecoilScope>
</RecoilScope>
);
}

View File

@ -1,74 +0,0 @@
import styled from '@emotion/styled';
import { useFilteredSearchCompanyQuery } from '@/companies/queries';
import { useEditableField } from '@/ui/editable-field/hooks/useEditableField';
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 { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import {
Company,
Person,
useUpdateOnePersonMutation,
} from '~/generated/graphql';
export type OwnProps = {
people: Pick<Person, 'id'> & { company?: Pick<Company, 'id'> | null };
};
const StyledContainer = styled.div`
left: 0px;
position: absolute;
top: -8px;
`;
export function PeopleCompanyEditableFieldEditMode({ people }: OwnProps) {
const { closeEditableField } = useEditableField();
const [searchFilter] = useRecoilScopedState(
relationPickerSearchFilterScopedState,
);
const [updatePerson] = useUpdateOnePersonMutation();
const companies = useFilteredSearchCompanyQuery({
searchFilter,
selectedIds: people.company?.id ? [people.company.id] : [],
});
async function handleEntitySelected(
entity: EntityForSelect | null | undefined,
) {
if (entity) {
await updatePerson({
variables: {
where: {
id: people.id,
},
data: {
company: { connect: { id: entity.id } },
},
},
});
}
closeEditableField();
}
function handleCancel() {
closeEditableField();
}
return (
<StyledContainer>
<SingleEntitySelect
onEntitySelected={handleEntitySelected}
entities={{
entitiesToSelect: companies.entitiesToSelect,
selectedEntity: companies.selectedEntities[0],
loading: companies.loading,
}}
onCancel={handleCancel}
/>
</StyledContainer>
);
}

View File

@ -2,17 +2,14 @@ import { getOperationName } from '@apollo/client/utilities';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
import { ActionBarEntry } from '@/ui/action-bar/components/ActionBarEntry';
import { IconCheckbox, IconNotes, IconTrash } from '@/ui/icon';
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
import { actionBarEntriesState } from '@/ui/table/states/ActionBarEntriesState';
import { selectedRowIdsSelector } from '@/ui/table/states/selectedRowIdsSelector';
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
import {
ActivityType,
CommentableType,
useDeleteManyPersonMutation,
} from '~/generated/graphql';
import { ActivityType, useDeleteManyPersonMutation } from '~/generated/graphql';
import { GET_PEOPLE } from '../queries';
@ -23,7 +20,7 @@ export function useOpenActionBar() {
useOpenCreateActivityDrawerForSelectedRowIds();
async function handleActivityClick(type: ActivityType) {
openCreateActivityRightDrawer(type, CommentableType.Person);
openCreateActivityRightDrawer(type, ActivityTargetableEntityType.Person);
}
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);

View File

@ -3,16 +3,13 @@ import { IconCheckbox, IconNotes, IconTrash } from '@tabler/icons-react';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
import { ContextMenuEntry } from '@/ui/context-menu/components/ContextMenuEntry';
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
import { contextMenuEntriesState } from '@/ui/table/states/ContextMenuEntriesState';
import { selectedRowIdsSelector } from '@/ui/table/states/selectedRowIdsSelector';
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
import {
ActivityType,
CommentableType,
useDeleteManyPersonMutation,
} from '~/generated/graphql';
import { ActivityType, useDeleteManyPersonMutation } from '~/generated/graphql';
import { GET_PEOPLE } from '../queries';
@ -23,7 +20,7 @@ export function useOpenContextMenu() {
useOpenCreateActivityDrawerForSelectedRowIds();
async function handleActivityClick(type: ActivityType) {
openCreateActivityRightDrawer(type, CommentableType.Person);
openCreateActivityRightDrawer(type, ActivityTargetableEntityType.Person);
}
const selectedRowIds = useRecoilValue(selectedRowIdsSelector);

View File

@ -1,17 +1,17 @@
import { useLocation } from 'react-router-dom';
import { useRecoilCallback } from 'recoil';
import { availableFiltersScopedState } from '@/ui/filter-n-sort/states/availableFiltersScopedState';
import { useResetTableRowSelection } from '@/ui/table/hooks/useResetTableRowSelection';
import { isFetchingEntityTableDataState } from '@/ui/table/states/isFetchingEntityTableDataState';
import { numberOfTableRowsState } from '@/ui/table/states/numberOfTableRowsState';
import { TableContext } from '@/ui/table/states/TableContext';
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
import { currentPageLocationState } from '@/ui/utilities/loading-state/states/currentPageLocationState';
import { useContextScopeId } from '@/ui/utilities/recoil-scope/hooks/useContextScopeId';
import { GetPeopleQuery } from '~/generated/graphql';
import { peopleFilters } from '~/pages/people/people-filters';
import { peopleFilters } from '../../../pages/people/people-filters';
import { availableFiltersScopedState } from '../../ui/filter-n-sort/states/availableFiltersScopedState';
import { useResetTableRowSelection } from '../../ui/table/hooks/useResetTableRowSelection';
import { entityTableDimensionsState } from '../../ui/table/states/entityTableDimensionsState';
import { isFetchingEntityTableDataState } from '../../ui/table/states/isFetchingEntityTableDataState';
import { TableContext } from '../../ui/table/states/TableContext';
import { tableRowIdsState } from '../../ui/table/states/tableRowIdsState';
import { peopleCityFamilyState } from '../states/peopleCityFamilyState';
import { peopleCompanyFamilyState } from '../states/peopleCompanyFamilyState';
import { peopleCreatedAtFamilyState } from '../states/peopleCreatedAtFamilyState';
@ -124,10 +124,7 @@ export function useSetPeopleEntityTable() {
resetTableRowSelection();
set(entityTableDimensionsState, {
numberOfColumns: 10,
numberOfRows: peopleIds.length,
});
set(numberOfTableRowsState, peopleIds.length);
set(availableFiltersScopedState(tableContextScopeId), peopleFilters);

View File

@ -1,10 +1,10 @@
import { gql } from '@apollo/client';
import { CommentableEntityForSelect } from '@/activities/types/CommentableEntityForSelect';
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
import { ActivityTargetableEntityForSelect } from '@/activities/types/ActivityTargetableEntityForSelect';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { SelectedSortType } from '@/ui/filter-n-sort/types/interface';
import {
CommentableType,
PersonOrderByWithRelationInput as People_Order_By,
PersonWhereInput as People_Bool_Exp,
SortOrder,
@ -69,11 +69,11 @@ export function useFilteredSearchPeopleQuery({
mappingFunction: (entity) =>
({
id: entity.id,
entityType: CommentableType.Person,
entityType: ActivityTargetableEntityType.Person,
name: `${entity.firstName} ${entity.lastName}`,
avatarUrl: entity.avatarUrl,
avatarType: 'rounded',
} as CommentableEntityForSelect),
} as ActivityTargetableEntityForSelect),
searchFilter,
limit,
});

View File

@ -1,5 +1,7 @@
import { gql } from '@apollo/client';
import { useSetRecoilState } from 'recoil';
import { genericEntitiesFamilyState } from '@/ui/editable-field/states/genericEntitiesFamilyState';
import { useGetPersonQuery } from '~/generated/graphql';
export const GET_PERSON = gql`
@ -37,5 +39,13 @@ export const GET_PERSON = gql`
`;
export function usePersonQuery(id: string) {
return useGetPersonQuery({ variables: { id } });
const updatePersonShowPage = useSetRecoilState(
genericEntitiesFamilyState(id),
);
return useGetPersonQuery({
variables: { id },
onCompleted: (data) => {
updatePersonShowPage(data?.findUniquePerson);
},
});
}

View File

@ -8,11 +8,14 @@ import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIn
import { IconList } from '@/ui/icon';
import { EntityTable } from '@/ui/table/components/EntityTable';
import { GenericEntityTableData } from '@/ui/table/components/GenericEntityTableData';
import { useUpsertEntityTableItem } from '@/ui/table/hooks/useUpsertEntityTableItem';
import { TableContext } from '@/ui/table/states/TableContext';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { useTableViewFields } from '@/views/hooks/useTableViewFields';
import { useViewSorts } from '@/views/hooks/useViewSorts';
import { currentViewIdState } from '@/views/states/currentViewIdState';
import {
UpdateOnePersonMutationVariables,
useGetPeopleQuery,
useUpdateOnePersonMutation,
} from '~/generated/graphql';
@ -24,6 +27,13 @@ import { defaultOrderBy } from '../../queries';
export function PeopleTable() {
const currentViewId = useRecoilValue(currentViewIdState);
const orderBy = useRecoilScopedValue(sortsOrderByScopedState, TableContext);
const [updateEntityMutation] = useUpdateOnePersonMutation();
const upsertEntityTableItem = useUpsertEntityTableItem();
const { handleColumnsChange } = useTableViewFields({
objectName: 'person',
viewFieldDefinitions: peopleViewFields,
});
const { updateSorts } = useViewSorts({
availableSorts,
Context: TableContext,
@ -38,20 +48,33 @@ export function PeopleTable() {
return (
<>
<GenericEntityTableData
objectName="person"
getRequestResultKey="people"
useGetRequest={useGetPeopleQuery}
orderBy={orderBy.length ? orderBy : defaultOrderBy}
whereFilters={whereFilters}
viewFieldDefinitions={peopleViewFields}
filterDefinitionArray={peopleFilters}
/>
<EntityTable
viewName="All People"
viewIcon={<IconList size={16} />}
availableSorts={availableSorts}
onColumnsChange={handleColumnsChange}
onSortsUpdate={currentViewId ? updateSorts : undefined}
useUpdateEntityMutation={useUpdateOnePersonMutation}
updateEntityMutation={({
variables,
}: {
variables: UpdateOnePersonMutationVariables;
}) =>
updateEntityMutation({
variables,
onCompleted: (data) => {
if (!data.updateOnePerson) {
return;
}
upsertEntityTableItem(data.updateOnePerson);
},
})
}
/>
</>
);