feat: create view from current table columns + persist view fields on… (#1308)
feat: create view from current table columns + persist view fields on Update View button click Closes #1302, Closes #1307
This commit is contained in:
@ -1,18 +1,17 @@
|
||||
import { useCallback } from 'react';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import {
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
ViewFieldTextMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import {
|
||||
tableColumnsByIdState,
|
||||
tableColumnsState,
|
||||
} from '@/ui/table/states/tableColumnsState';
|
||||
import { savedTableColumnsScopedState } from '@/ui/table/states/savedTableColumnsScopedState';
|
||||
import { savedTableColumnsByIdScopedSelector } from '@/ui/table/states/selectors/savedTableColumnsByIdScopedSelector';
|
||||
import { tableColumnsScopedState } from '@/ui/table/states/tableColumnsScopedState';
|
||||
import { currentTableViewIdState } from '@/ui/table/states/tableViewsState';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import {
|
||||
SortOrder,
|
||||
@ -20,8 +19,7 @@ import {
|
||||
useGetViewFieldsQuery,
|
||||
useUpdateViewFieldMutation,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { GET_VIEW_FIELDS } from '../graphql/queries/getViewFields';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
const DEFAULT_VIEW_FIELD_METADATA: ViewFieldTextMetadata = {
|
||||
type: 'text',
|
||||
@ -51,24 +49,34 @@ export const useTableViewFields = ({
|
||||
currentTableViewIdState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const setColumns = useSetRecoilState(tableColumnsState);
|
||||
const columnsById = useRecoilValue(tableColumnsByIdState);
|
||||
const [columns, setColumns] = useRecoilScopedState(
|
||||
tableColumnsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const setSavedColumns = useSetRecoilState(
|
||||
savedTableColumnsScopedState(currentViewId),
|
||||
);
|
||||
const savedColumnsById = useRecoilValue(
|
||||
savedTableColumnsByIdScopedSelector(currentViewId),
|
||||
);
|
||||
|
||||
const [createViewFieldsMutation] = useCreateViewFieldsMutation();
|
||||
const [updateViewFieldMutation] = useUpdateViewFieldMutation();
|
||||
|
||||
const createViewFields = useCallback(
|
||||
(columns: ViewFieldDefinition<ViewFieldMetadata>[]) => {
|
||||
(
|
||||
columns: ViewFieldDefinition<ViewFieldMetadata>[],
|
||||
viewId = currentViewId,
|
||||
) => {
|
||||
if (!columns.length) return;
|
||||
|
||||
return createViewFieldsMutation({
|
||||
variables: {
|
||||
data: columns.map((column) => ({
|
||||
...toViewFieldInput(objectName, column),
|
||||
viewId: currentViewId,
|
||||
viewId,
|
||||
})),
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_VIEW_FIELDS) ?? ''],
|
||||
});
|
||||
},
|
||||
[createViewFieldsMutation, currentViewId, objectName],
|
||||
@ -88,7 +96,6 @@ export const useTableViewFields = ({
|
||||
},
|
||||
where: { id: column.id },
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_VIEW_FIELDS) ?? ''],
|
||||
}),
|
||||
),
|
||||
);
|
||||
@ -96,7 +103,7 @@ export const useTableViewFields = ({
|
||||
[updateViewFieldMutation],
|
||||
);
|
||||
|
||||
useGetViewFieldsQuery({
|
||||
const { refetch } = useGetViewFieldsQuery({
|
||||
variables: {
|
||||
orderBy: { index: SortOrder.Asc },
|
||||
where: {
|
||||
@ -104,50 +111,44 @@ export const useTableViewFields = ({
|
||||
viewId: { equals: currentViewId ?? null },
|
||||
},
|
||||
},
|
||||
onCompleted: (data) => {
|
||||
if (data.viewFields.length) {
|
||||
setColumns(
|
||||
data.viewFields.map<ViewFieldDefinition<ViewFieldMetadata>>(
|
||||
(viewField) => ({
|
||||
...(viewFieldDefinitions.find(
|
||||
({ columnLabel }) => viewField.fieldName === columnLabel,
|
||||
) || { metadata: DEFAULT_VIEW_FIELD_METADATA }),
|
||||
id: viewField.id,
|
||||
columnLabel: viewField.fieldName,
|
||||
columnOrder: viewField.index,
|
||||
columnSize: viewField.sizeInPx,
|
||||
isVisible: viewField.isVisible,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
onCompleted: async (data) => {
|
||||
if (!data.viewFields.length) {
|
||||
// Populate if empty
|
||||
await createViewFields(viewFieldDefinitions);
|
||||
return refetch();
|
||||
}
|
||||
|
||||
// Populate if empty
|
||||
createViewFields(viewFieldDefinitions);
|
||||
const nextColumns = data.viewFields.map<
|
||||
ViewFieldDefinition<ViewFieldMetadata>
|
||||
>((viewField) => ({
|
||||
...(viewFieldDefinitions.find(
|
||||
({ columnLabel }) => viewField.fieldName === columnLabel,
|
||||
) || { metadata: DEFAULT_VIEW_FIELD_METADATA }),
|
||||
id: viewField.id,
|
||||
columnLabel: viewField.fieldName,
|
||||
columnOrder: viewField.index,
|
||||
columnSize: viewField.sizeInPx,
|
||||
isVisible: viewField.isVisible,
|
||||
}));
|
||||
|
||||
if (!isDeeplyEqual(columns, nextColumns)) {
|
||||
setSavedColumns(nextColumns);
|
||||
setColumns(nextColumns);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const handleColumnsChange = useCallback(
|
||||
async (nextColumns: ViewFieldDefinition<ViewFieldMetadata>[]) => {
|
||||
setColumns(nextColumns);
|
||||
const persistColumns = useCallback(async () => {
|
||||
const viewFieldsToUpdate = columns.filter(
|
||||
(column) =>
|
||||
savedColumnsById[column.id] &&
|
||||
(savedColumnsById[column.id].columnSize !== column.columnSize ||
|
||||
savedColumnsById[column.id].isVisible !== column.isVisible),
|
||||
);
|
||||
await updateViewFields(viewFieldsToUpdate);
|
||||
|
||||
const viewFieldsToCreate = nextColumns.filter(
|
||||
(nextColumn) => !columnsById[nextColumn.id],
|
||||
);
|
||||
await createViewFields(viewFieldsToCreate);
|
||||
return refetch();
|
||||
}, [columns, refetch, savedColumnsById, updateViewFields]);
|
||||
|
||||
const viewFieldsToUpdate = nextColumns.filter(
|
||||
(nextColumn) =>
|
||||
columnsById[nextColumn.id] &&
|
||||
(columnsById[nextColumn.id].columnSize !== nextColumn.columnSize ||
|
||||
columnsById[nextColumn.id].isVisible !== nextColumn.isVisible),
|
||||
);
|
||||
await updateViewFields(viewFieldsToUpdate);
|
||||
},
|
||||
[columnsById, createViewFields, setColumns, updateViewFields],
|
||||
);
|
||||
|
||||
return { handleColumnsChange };
|
||||
return { createViewFields, persistColumns };
|
||||
};
|
||||
|
||||
92
front/src/modules/views/hooks/useTableViews.ts
Normal file
92
front/src/modules/views/hooks/useTableViews.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import type {
|
||||
ViewFieldDefinition,
|
||||
ViewFieldMetadata,
|
||||
} from '@/ui/editable-field/types/ViewField';
|
||||
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||
import { sortsScopedState } from '@/ui/filter-n-sort/states/sortsScopedState';
|
||||
import type { FilterDefinitionByEntity } from '@/ui/filter-n-sort/types/FilterDefinitionByEntity';
|
||||
import type { SortType } from '@/ui/filter-n-sort/types/interface';
|
||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { tableColumnsScopedState } from '@/ui/table/states/tableColumnsScopedState';
|
||||
import { currentTableViewIdState } from '@/ui/table/states/tableViewsState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
import { useTableViewFields } from './useTableViewFields';
|
||||
import { useViewFilters } from './useViewFilters';
|
||||
import { useViews } from './useViews';
|
||||
import { useViewSorts } from './useViewSorts';
|
||||
|
||||
export const useTableViews = <Entity, SortField>({
|
||||
availableFilters,
|
||||
availableSorts,
|
||||
objectId,
|
||||
viewFieldDefinitions,
|
||||
}: {
|
||||
availableFilters: FilterDefinitionByEntity<Entity>[];
|
||||
availableSorts: SortType<SortField>[];
|
||||
objectId: 'company' | 'person';
|
||||
viewFieldDefinitions: ViewFieldDefinition<ViewFieldMetadata>[];
|
||||
}) => {
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
currentTableViewIdState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const currentColumns = useRecoilScopedValue(
|
||||
tableColumnsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const selectedFilters = useRecoilScopedValue(
|
||||
filtersScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const selectedSorts = useRecoilScopedValue(
|
||||
sortsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
const { createViewFields, persistColumns } = useTableViewFields({
|
||||
objectName: objectId,
|
||||
viewFieldDefinitions,
|
||||
});
|
||||
const { createViewFilters, persistFilters } = useViewFilters({
|
||||
availableFilters,
|
||||
currentViewId,
|
||||
scopeContext: TableRecoilScopeContext,
|
||||
});
|
||||
const { createViewSorts, persistSorts } = useViewSorts({
|
||||
availableSorts,
|
||||
currentViewId,
|
||||
scopeContext: TableRecoilScopeContext,
|
||||
});
|
||||
|
||||
const handleViewCreate = useCallback(
|
||||
async (viewId: string) => {
|
||||
await createViewFields(currentColumns, viewId);
|
||||
await createViewFilters(selectedFilters, viewId);
|
||||
await createViewSorts(selectedSorts, viewId);
|
||||
},
|
||||
[
|
||||
createViewFields,
|
||||
createViewFilters,
|
||||
createViewSorts,
|
||||
currentColumns,
|
||||
selectedFilters,
|
||||
selectedSorts,
|
||||
],
|
||||
);
|
||||
|
||||
const handleViewSubmit = useCallback(async () => {
|
||||
await persistColumns();
|
||||
await persistFilters();
|
||||
await persistSorts();
|
||||
}, [persistColumns, persistFilters, persistSorts]);
|
||||
|
||||
const { handleViewsChange } = useViews({
|
||||
objectId,
|
||||
onViewCreate: handleViewCreate,
|
||||
});
|
||||
|
||||
return { handleViewsChange, handleViewSubmit };
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react';
|
||||
import { Context, useCallback } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||
@ -6,10 +6,7 @@ import { savedFiltersScopedState } from '@/ui/filter-n-sort/states/savedFiltersS
|
||||
import { savedFiltersByKeyScopedSelector } from '@/ui/filter-n-sort/states/selectors/savedFiltersByKeyScopedSelector';
|
||||
import type { Filter } from '@/ui/filter-n-sort/types/Filter';
|
||||
import type { FilterDefinitionByEntity } from '@/ui/filter-n-sort/types/FilterDefinitionByEntity';
|
||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { currentTableViewIdState } from '@/ui/table/states/tableViewsState';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import {
|
||||
useCreateViewFiltersMutation,
|
||||
useDeleteViewFiltersMutation,
|
||||
@ -20,16 +17,16 @@ import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
export const useViewFilters = <Entity>({
|
||||
availableFilters,
|
||||
currentViewId,
|
||||
scopeContext,
|
||||
}: {
|
||||
availableFilters: FilterDefinitionByEntity<Entity>[];
|
||||
currentViewId: string | undefined;
|
||||
scopeContext: Context<string | null>;
|
||||
}) => {
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
currentTableViewIdState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const [filters, setFilters] = useRecoilScopedState(
|
||||
filtersScopedState,
|
||||
TableRecoilScopeContext,
|
||||
scopeContext,
|
||||
);
|
||||
const [, setSavedFilters] = useRecoilState(
|
||||
savedFiltersScopedState(currentViewId),
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react';
|
||||
import { Context, useCallback } from 'react';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { savedSortsScopedState } from '@/ui/filter-n-sort/states/savedSortsScopedState';
|
||||
@ -8,10 +8,7 @@ import type {
|
||||
SelectedSortType,
|
||||
SortType,
|
||||
} from '@/ui/filter-n-sort/types/interface';
|
||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { currentTableViewIdState } from '@/ui/table/states/tableViewsState';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import {
|
||||
useCreateViewSortsMutation,
|
||||
useDeleteViewSortsMutation,
|
||||
@ -23,16 +20,16 @@ import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
export const useViewSorts = <SortField>({
|
||||
availableSorts,
|
||||
currentViewId,
|
||||
scopeContext,
|
||||
}: {
|
||||
availableSorts: SortType<SortField>[];
|
||||
currentViewId: string | undefined;
|
||||
scopeContext: Context<string | null>;
|
||||
}) => {
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
currentTableViewIdState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const [sorts, setSorts] = useRecoilScopedState(
|
||||
sortsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
scopeContext,
|
||||
);
|
||||
const [, setSavedSorts] = useRecoilState(
|
||||
savedSortsScopedState(currentViewId),
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
|
||||
import { sortsScopedState } from '@/ui/filter-n-sort/states/sortsScopedState';
|
||||
import type { FilterDefinitionByEntity } from '@/ui/filter-n-sort/types/FilterDefinitionByEntity';
|
||||
import type { SortType } from '@/ui/filter-n-sort/types/interface';
|
||||
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import {
|
||||
type TableView,
|
||||
@ -21,17 +17,12 @@ import {
|
||||
} from '~/generated/graphql';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
import { useViewFilters } from './useViewFilters';
|
||||
import { useViewSorts } from './useViewSorts';
|
||||
|
||||
export const useViews = <Entity, SortField>({
|
||||
availableFilters,
|
||||
availableSorts,
|
||||
export const useViews = ({
|
||||
objectId,
|
||||
onViewCreate,
|
||||
}: {
|
||||
availableFilters: FilterDefinitionByEntity<Entity>[];
|
||||
availableSorts: SortType<SortField>[];
|
||||
objectId: 'company' | 'person';
|
||||
onViewCreate: (viewId: string) => Promise<void>;
|
||||
}) => {
|
||||
const [views, setViews] = useRecoilScopedState(
|
||||
tableViewsState,
|
||||
@ -41,22 +32,11 @@ export const useViews = <Entity, SortField>({
|
||||
tableViewsByIdState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const selectedFilters = useRecoilScopedValue(
|
||||
filtersScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const selectedSorts = useRecoilScopedValue(
|
||||
sortsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [createViewsMutation] = useCreateViewsMutation();
|
||||
const [updateViewMutation] = useUpdateViewMutation();
|
||||
const [deleteViewsMutation] = useDeleteViewsMutation();
|
||||
|
||||
const { createViewFilters } = useViewFilters({ availableFilters });
|
||||
const { createViewSorts } = useViewSorts({ availableSorts });
|
||||
|
||||
const createViews = useCallback(
|
||||
async (views: TableView[]) => {
|
||||
if (!views.length) return;
|
||||
@ -71,21 +51,9 @@ export const useViews = <Entity, SortField>({
|
||||
},
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
views.flatMap((view) => [
|
||||
createViewFilters(selectedFilters, view.id),
|
||||
createViewSorts(selectedSorts, view.id),
|
||||
]),
|
||||
);
|
||||
await Promise.all(views.map((view) => onViewCreate(view.id)));
|
||||
},
|
||||
[
|
||||
createViewFilters,
|
||||
createViewSorts,
|
||||
createViewsMutation,
|
||||
objectId,
|
||||
selectedFilters,
|
||||
selectedSorts,
|
||||
],
|
||||
[createViewsMutation, objectId, onViewCreate],
|
||||
);
|
||||
|
||||
const updateViews = useCallback(
|
||||
|
||||
Reference in New Issue
Block a user