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:
@ -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 <></>;
|
||||
}
|
||||
@ -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);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -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',
|
||||
},
|
||||
];
|
||||
72
front/src/modules/people/hooks/useSetEntityTableData.ts
Normal file
72
front/src/modules/people/hooks/useSetEntityTableData.ts
Normal 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);
|
||||
},
|
||||
[],
|
||||
);
|
||||
}
|
||||
18
front/src/modules/people/hooks/useUpdatePeopleField.ts
Normal file
18
front/src/modules/people/hooks/useUpdatePeopleField.ts
Normal 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 },
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -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 <></>;
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
53
front/src/modules/people/table/components/PeopleTableV2.tsx
Normal file
53
front/src/modules/people/table/components/PeopleTableV2.tsx
Normal 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}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user