Feat/improve editable cell (#959)

* Removed isSomeInputInEditMode

* Removed console.log

* Added a first version of generic cell text

* Removed metadata from entity table  V1

* Fix

* Fix

* Fix
This commit is contained in:
Lucas Bordeau
2023-07-27 07:53:57 +02:00
committed by GitHub
parent 13f415a859
commit 011d9e840f
27 changed files with 705 additions and 92 deletions

View File

@ -0,0 +1,31 @@
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
import { useSetEntityTableData } from '../hooks/useSetEntityTableData';
import { defaultOrderBy } from '../queries';
export function GenericEntityTableData({
useGetRequest,
getRequestResultKey,
orderBy = defaultOrderBy,
whereFilters,
fieldMetadataArray,
}: {
useGetRequest: any;
getRequestResultKey: string;
orderBy?: any;
whereFilters?: any;
fieldMetadataArray: EntityFieldMetadata[];
}) {
const setEntityTableData = useSetEntityTableData();
useGetRequest({
variables: { orderBy, where: whereFilters },
onCompleted: (data: any) => {
const entities = data[getRequestResultKey] ?? [];
setEntityTableData(entities, fieldMetadataArray);
},
});
return <></>;
}

View File

@ -1,7 +1,3 @@
import { useRecoilState } from 'recoil';
import { isFetchingEntityTableDataState } from '@/ui/table/states/isFetchingEntityTableDataState';
import { tableRowIdsState } from '@/ui/table/states/tableRowIdsState';
import {
PersonOrderByWithRelationInput,
useGetPeopleQuery,
@ -17,12 +13,6 @@ export function PeopleEntityTableData({
orderBy?: PersonOrderByWithRelationInput[];
whereFilters?: any;
}) {
const [, setTableRowIds] = useRecoilState(tableRowIdsState);
const [, setIsFetchingEntityTableData] = useRecoilState(
isFetchingEntityTableDataState,
);
const setPeopleEntityTable = useSetPeopleEntityTable();
useGetPeopleQuery({
@ -30,19 +20,7 @@ export function PeopleEntityTableData({
onCompleted: (data) => {
const people = data.people ?? [];
const peopleIds = people.map((person) => person.id);
setTableRowIds((currentRowIds) => {
if (JSON.stringify(currentRowIds) !== JSON.stringify(peopleIds)) {
return peopleIds;
}
return currentRowIds;
});
setPeopleEntityTable(people);
setIsFetchingEntityTableData(false);
},
});

View File

@ -0,0 +1,20 @@
import { IconBriefcase, IconMap } from '@tabler/icons-react';
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
export const peopleFieldMetadataArray: EntityFieldMetadata[] = [
{
fieldName: 'city',
label: 'City',
icon: <IconMap size={16} />,
columnSize: 150,
type: 'text',
},
{
fieldName: 'jobTitle',
label: 'Job title',
icon: <IconBriefcase size={16} />,
columnSize: 150,
type: 'text',
},
];

View File

@ -0,0 +1,72 @@
import { useRecoilCallback } from 'recoil';
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
import { entityFieldMetadataArrayState } from '@/ui/table/states/entityFieldMetadataArrayState';
import { tableEntitiesFamilyState } from '@/ui/table/states/tableEntitiesFamilyState';
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
import { availableFiltersScopedState } from '../../ui/filter-n-sort/states/availableFiltersScopedState';
import { useContextScopeId } from '../../ui/recoil-scope/hooks/useContextScopeId';
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';
export function useSetEntityTableData() {
const resetTableRowSelection = useResetTableRowSelection();
const tableContextScopeId = useContextScopeId(TableContext);
return useRecoilCallback(
({ set, snapshot }) =>
<T extends { id: string }>(
newEntityArray: T[],
entityFieldMetadataArray: EntityFieldMetadata[],
) => {
for (const entity of newEntityArray) {
const currentEntity = snapshot
.getLoadable(tableEntitiesFamilyState(entity.id))
.valueOrThrow();
if (JSON.stringify(currentEntity) !== JSON.stringify(entity)) {
set(tableEntitiesFamilyState(entity.id), entity);
}
}
const entityIds = newEntityArray.map((entity) => entity.id);
set(tableRowIdsState, (currentRowIds) => {
if (JSON.stringify(currentRowIds) !== JSON.stringify(entityIds)) {
return entityIds;
}
return currentRowIds;
});
resetTableRowSelection();
set(entityTableDimensionsState, {
numberOfColumns: entityFieldMetadataArray.length,
numberOfRows: entityIds.length,
});
const filters = entityFieldMetadataArray.map(
(fieldMetadata) =>
({
field: fieldMetadata.fieldName,
icon: fieldMetadata.filterIcon,
label: fieldMetadata.label,
type: fieldMetadata.type,
} as FilterDefinition),
);
set(availableFiltersScopedState(tableContextScopeId), filters);
set(entityFieldMetadataArrayState, entityFieldMetadataArray);
set(isFetchingEntityTableDataState, false);
},
[],
);
}

View File

@ -0,0 +1,18 @@
import { useUpdateOnePersonMutation } from '~/generated/graphql';
export function useUpdatePeopleField() {
const [updatePeople] = useUpdateOnePersonMutation();
return function updatePeopleField(
peopleId: string,
fieldName: string,
fieldValue: unknown,
) {
updatePeople({
variables: {
where: { id: peopleId },
data: { [fieldName]: fieldValue },
},
});
};
}

View File

@ -0,0 +1,23 @@
import { EntityFieldMetadata } from '@/ui/table/types/EntityFieldMetadata';
import { GenericEditableTextCell } from './GenericEditableTextCell';
type OwnProps = {
entityFieldMetadata: EntityFieldMetadata;
};
export function GenericEditableCell({ entityFieldMetadata }: OwnProps) {
switch (entityFieldMetadata.type) {
case 'text':
return (
<GenericEditableTextCell
fieldName={entityFieldMetadata.fieldName}
placeholder={entityFieldMetadata.label}
editModeHorizontalAlign="left"
/>
);
default:
return <></>;
}
}

View File

@ -0,0 +1,44 @@
import { useRecoilValue } from 'recoil';
import { InplaceInputTextDisplayMode } from '@/ui/display/component/InplaceInputTextDisplayMode';
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import { GenericEditableTextCellEditMode } from './GenericEditableTextCellEditMode';
type OwnProps = {
fieldName: string;
editModeHorizontalAlign?: 'left' | 'right';
placeholder?: string;
};
export function GenericEditableTextCell({
fieldName,
editModeHorizontalAlign,
placeholder,
}: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
const fieldValue = useRecoilValue<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName,
}),
);
return (
<EditableCell
editModeHorizontalAlign={editModeHorizontalAlign}
editModeContent={
<GenericEditableTextCellEditMode
fieldName={fieldName}
placeholder={placeholder}
/>
}
nonEditModeContent={
<InplaceInputTextDisplayMode>{fieldValue}</InplaceInputTextDisplayMode>
}
></EditableCell>
);
}

View File

@ -0,0 +1,47 @@
import { useRecoilState } from 'recoil';
import { InplaceInputTextEditMode } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
import { useEntityUpdateFieldHook } from '@/ui/table/hooks/useCellUpdateFieldHook';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
type OwnProps = {
fieldName: string;
placeholder?: string;
};
export function GenericEditableTextCellEditMode({
fieldName,
placeholder,
}: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
const [fieldValue, setFieldValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName,
}),
);
const useUpdateField = useEntityUpdateFieldHook();
const updateField = useUpdateField?.();
function handleSubmit(newText: string) {
if (newText === fieldValue) return;
setFieldValue(newText);
if (currentRowEntityId && updateField) {
updateField(currentRowEntityId, fieldName, newText);
}
}
return (
<InplaceInputTextEditMode
placeholder={placeholder ?? ''}
autoFocus
value={fieldValue ?? ''}
onSubmit={handleSubmit}
/>
);
}

View File

@ -0,0 +1,53 @@
import { useCallback, useMemo, useState } from 'react';
import { defaultOrderBy } from '@/companies/queries';
import { GenericEntityTableData } from '@/people/components/GenericEntityTableData';
import { peopleFieldMetadataArray } from '@/people/constants/peopleFieldMetadataArray';
import { useUpdatePeopleField } from '@/people/hooks/useUpdatePeopleField';
import { PeopleSelectedSortType } from '@/people/queries';
import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers';
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
import { IconList } from '@/ui/icon';
import { useRecoilScopedValue } from '@/ui/recoil-scope/hooks/useRecoilScopedValue';
import { EntityTable } from '@/ui/table/components/EntityTableV2';
import { TableContext } from '@/ui/table/states/TableContext';
import {
PersonOrderByWithRelationInput,
useGetPeopleQuery,
} from '~/generated/graphql';
import { availableSorts } from '~/pages/people/people-sorts';
export function PeopleTable() {
const [orderBy, setOrderBy] =
useState<PersonOrderByWithRelationInput[]>(defaultOrderBy);
const updateSorts = useCallback((sorts: Array<PeopleSelectedSortType>) => {
setOrderBy(sorts.length ? reduceSortsToOrderBy(sorts) : defaultOrderBy);
}, []);
const filters = useRecoilScopedValue(filtersScopedState, TableContext);
const whereFilters = useMemo(() => {
return { AND: filters.map(turnFilterIntoWhereClause) };
}, [filters]) as any;
return (
<>
<GenericEntityTableData
getRequestResultKey="people"
useGetRequest={useGetPeopleQuery}
orderBy={orderBy}
whereFilters={whereFilters}
fieldMetadataArray={peopleFieldMetadataArray}
/>
<EntityTable
viewName="All People"
viewIcon={<IconList size={16} />}
availableSorts={availableSorts}
onSortsUpdate={updateSorts}
useUpdateField={useUpdatePeopleField}
/>
</>
);
}