Migrate to a monorepo structure (#2909)

This commit is contained in:
Charles Bochet
2023-12-10 18:10:54 +01:00
committed by GitHub
parent a70a9281eb
commit 5bdca9de6c
2304 changed files with 37152 additions and 25869 deletions

View File

@ -0,0 +1,155 @@
import { useApolloClient } from '@apollo/client';
import { useRecoilCallback } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { ViewField } from '@/views/types/ViewField';
import { getViewScopedStatesFromSnapshot } from '@/views/utils/getViewScopedStatesFromSnapshot';
import { getViewScopedStateValuesFromSnapshot } from '@/views/utils/getViewScopedStateValuesFromSnapshot';
export const useViewFields = (viewScopeId: string) => {
const { updateOneRecordMutation, createOneRecordMutation } =
useObjectMetadataItem({
objectNameSingular: 'viewField',
});
const { modifyRecordFromCache } = useObjectMetadataItem({
objectNameSingular: 'view',
});
const apolloClient = useApolloClient();
const persistViewFields = useRecoilCallback(
({ snapshot, set }) =>
async (viewFieldsToPersist: ViewField[], viewId?: string) => {
const {
viewObjectMetadataId,
currentViewId,
savedViewFieldsByKey,
onViewFieldsChange,
views,
} = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
viewId,
});
const {
isPersistingViewState,
currentViewFieldsState,
savedViewFieldsState,
} = getViewScopedStatesFromSnapshot({
snapshot,
viewScopeId,
viewId,
});
const viewIdToPersist = viewId ?? currentViewId;
if (!currentViewId || !savedViewFieldsByKey || !viewObjectMetadataId) {
return;
}
const _createViewFields = (viewFieldsToCreate: ViewField[]) => {
if (!viewFieldsToCreate.length) {
return;
}
return Promise.all(
viewFieldsToCreate.map((viewField) =>
apolloClient.mutate({
mutation: createOneRecordMutation,
variables: {
input: {
fieldMetadataId: viewField.fieldMetadataId,
viewId: viewIdToPersist,
isVisible: viewField.isVisible,
size: viewField.size,
position: viewField.position,
},
},
}),
),
);
};
const _updateViewFields = (viewFieldsToUpdate: ViewField[]) => {
if (!viewFieldsToUpdate.length) {
return;
}
return Promise.all(
viewFieldsToUpdate.map((viewField) =>
apolloClient.mutate({
mutation: updateOneRecordMutation,
variables: {
idToUpdate: viewField.id,
input: {
isVisible: viewField.isVisible,
size: viewField.size,
position: viewField.position,
},
},
}),
),
);
};
const viewFieldsToCreate = viewFieldsToPersist.filter(
(viewField) => !savedViewFieldsByKey[viewField.fieldMetadataId],
);
const viewFieldsToUpdate = viewFieldsToPersist.filter(
(viewFieldToPersit) =>
savedViewFieldsByKey[viewFieldToPersit.fieldMetadataId] &&
(savedViewFieldsByKey[viewFieldToPersit.fieldMetadataId].size !==
viewFieldToPersit.size ||
savedViewFieldsByKey[viewFieldToPersit.fieldMetadataId]
.position !== viewFieldToPersit.position ||
savedViewFieldsByKey[viewFieldToPersit.fieldMetadataId]
.isVisible !== viewFieldToPersit.isVisible),
);
set(isPersistingViewState, true);
await _createViewFields(viewFieldsToCreate);
await _updateViewFields(viewFieldsToUpdate);
set(isPersistingViewState, false);
set(currentViewFieldsState, viewFieldsToPersist);
set(savedViewFieldsState, viewFieldsToPersist);
const existingView = views.find((view) => view.id === viewIdToPersist);
if (!existingView) {
return;
}
modifyRecordFromCache(viewIdToPersist ?? '', {
viewFields: () => ({
edges: viewFieldsToPersist.map((viewField) => ({
node: viewField,
cursor: '',
})),
pageInfo: {
hasNextPage: false,
hasPreviousPage: false,
startCursor: '',
endCursor: '',
},
}),
});
onViewFieldsChange?.(viewFieldsToPersist);
},
[
viewScopeId,
modifyRecordFromCache,
apolloClient,
createOneRecordMutation,
updateOneRecordMutation,
],
);
return { persistViewFields };
};

View File

@ -0,0 +1,252 @@
import { useApolloClient } from '@apollo/client';
import { produce } from 'immer';
import { useRecoilCallback } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
import { savedViewFiltersScopedFamilyState } from '@/views/states/savedViewFiltersScopedFamilyState';
import { ViewFilter } from '@/views/types/ViewFilter';
import { getViewScopedStateValuesFromSnapshot } from '@/views/utils/getViewScopedStateValuesFromSnapshot';
import { useViewScopedStates } from './useViewScopedStates';
export const useViewFilters = (viewScopeId: string) => {
const {
updateOneRecordMutation,
createOneRecordMutation,
deleteOneRecordMutation,
} = useObjectMetadataItem({
objectNameSingular: 'viewFilter',
});
const { modifyRecordFromCache } = useObjectMetadataItem({
objectNameSingular: 'view',
});
const apolloClient = useApolloClient();
const { currentViewFiltersState } = useViewScopedStates({
viewScopeId: viewScopeId,
});
const persistViewFilters = useRecoilCallback(
({ snapshot, set }) =>
async (viewId?: string) => {
const {
currentViewId,
currentViewFilters,
savedViewFiltersByKey,
views,
} = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
if (!currentViewFilters) {
return;
}
if (!savedViewFiltersByKey) {
return;
}
const createViewFilters = (viewFiltersToCreate: ViewFilter[]) => {
if (!viewFiltersToCreate.length) return;
return Promise.all(
viewFiltersToCreate.map((viewFilter) =>
apolloClient.mutate({
mutation: createOneRecordMutation,
variables: {
input: {
fieldMetadataId: viewFilter.fieldMetadataId,
viewId: viewId ?? currentViewId,
value: viewFilter.value,
displayValue: viewFilter.displayValue,
operand: viewFilter.operand,
},
},
}),
),
);
};
const updateViewFilters = (viewFiltersToUpdate: ViewFilter[]) => {
if (!viewFiltersToUpdate.length) return;
return Promise.all(
viewFiltersToUpdate.map((viewFilter) =>
apolloClient.mutate({
mutation: updateOneRecordMutation,
variables: {
idToUpdate: viewFilter.id,
input: {
value: viewFilter.value,
displayValue: viewFilter.displayValue,
operand: viewFilter.operand,
},
},
}),
),
);
};
const deleteViewFilters = (viewFilterIdsToDelete: string[]) => {
if (!viewFilterIdsToDelete.length) return;
return Promise.all(
viewFilterIdsToDelete.map((viewFilterId) =>
apolloClient.mutate({
mutation: deleteOneRecordMutation,
variables: {
idToDelete: viewFilterId,
},
}),
),
);
};
const filtersToCreate = currentViewFilters.filter(
(filter) => !savedViewFiltersByKey[filter.fieldMetadataId],
);
await createViewFilters(filtersToCreate);
const filtersToUpdate = currentViewFilters.filter(
(filter) =>
savedViewFiltersByKey[filter.fieldMetadataId] &&
(savedViewFiltersByKey[filter.fieldMetadataId].operand !==
filter.operand ||
savedViewFiltersByKey[filter.fieldMetadataId].value !==
filter.value),
);
await updateViewFilters(filtersToUpdate);
const filterKeys = currentViewFilters.map(
(filter) => filter.fieldMetadataId,
);
const filterKeysToDelete = Object.keys(savedViewFiltersByKey).filter(
(previousFilterKey) => !filterKeys.includes(previousFilterKey),
);
const filterIdsToDelete = filterKeysToDelete.map(
(filterKeyToDelete) =>
savedViewFiltersByKey[filterKeyToDelete].id ?? '',
);
await deleteViewFilters(filterIdsToDelete);
set(
savedViewFiltersScopedFamilyState({
scopeId: viewScopeId,
familyKey: viewId ?? currentViewId,
}),
currentViewFilters,
);
const existingViewId = viewId ?? currentViewId;
const existingView = views.find((view) => view.id === existingViewId);
if (!existingView) {
return;
}
modifyRecordFromCache(existingViewId, {
viewFilters: () => ({
edges: currentViewFilters.map((viewFilter) => ({
node: viewFilter,
cursor: '',
})),
pageInfo: {
hasNextPage: false,
hasPreviousPage: false,
startCursor: '',
endCursor: '',
},
}),
});
},
[
apolloClient,
createOneRecordMutation,
deleteOneRecordMutation,
modifyRecordFromCache,
updateOneRecordMutation,
viewScopeId,
],
);
const upsertViewFilter = useRecoilCallback(
({ snapshot, set }) =>
(filterToUpsert: Filter) => {
const { currentViewId, savedViewFiltersByKey, onViewFiltersChange } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
if (!savedViewFiltersByKey) {
return;
}
const existingSavedFilterId =
savedViewFiltersByKey[filterToUpsert.fieldMetadataId]?.id;
set(currentViewFiltersState, (filters) => {
const newViewFilters = produce(filters, (filtersDraft) => {
const existingFilterIndex = filtersDraft.findIndex(
(filter) =>
filter.fieldMetadataId === filterToUpsert.fieldMetadataId,
);
if (existingFilterIndex === -1 && filterToUpsert.value !== '') {
filtersDraft.push({
...filterToUpsert,
id: existingSavedFilterId,
});
return filtersDraft;
}
if (filterToUpsert.value === '') {
filtersDraft.splice(existingFilterIndex, 1);
return filtersDraft;
}
filtersDraft[existingFilterIndex] = {
...filterToUpsert,
id: existingSavedFilterId,
};
});
onViewFiltersChange?.(newViewFilters);
return newViewFilters;
});
},
[currentViewFiltersState, viewScopeId],
);
const removeViewFilter = useRecoilCallback(
({ snapshot, set }) =>
(fieldMetadataId: string) => {
const { currentViewId, currentViewFilters, onViewFiltersChange } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
const newViewFilters = currentViewFilters.filter((filter) => {
return filter.fieldMetadataId !== fieldMetadataId;
});
set(currentViewFiltersState, newViewFilters);
onViewFiltersChange?.(newViewFilters);
},
[currentViewFiltersState, viewScopeId],
);
return { persistViewFilters, removeViewFilter, upsertViewFilter };
};

View File

@ -0,0 +1,86 @@
import { useRecoilState } from 'recoil';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { getScopedState } from '@/ui/utilities/recoil-scope/utils/getScopedState';
import { UNDEFINED_FAMILY_ITEM_ID } from '../../constants';
import { ViewScopeInternalContext } from '../../scopes/scope-internal-context/ViewScopeInternalContext';
import { currentViewIdScopedState } from '../../states/currentViewIdScopedState';
import { getViewScopedStates } from '../../utils/internal/getViewScopedStates';
export const useViewScopedStates = (args?: { viewScopeId?: string }) => {
const { viewScopeId } = args ?? {};
const scopeId = useAvailableScopeIdOrThrow(
ViewScopeInternalContext,
viewScopeId,
);
// View
const [currentViewId] = useRecoilState(
getScopedState(currentViewIdScopedState, scopeId),
);
const viewId = currentViewId ?? UNDEFINED_FAMILY_ITEM_ID;
const {
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
canPersistFiltersSelector,
canPersistSortsSelector,
currentViewFieldsState,
currentViewFiltersState,
currentViewIdState,
currentViewSelector,
currentViewSortsState,
entityCountInCurrentViewState,
isViewBarExpandedState,
isPersistingViewState,
onViewFieldsChangeState,
onViewFiltersChangeState,
onViewSortsChangeState,
savedViewFieldsByKeySelector,
savedViewFieldsState,
savedViewFiltersByKeySelector,
savedViewFiltersState,
savedViewSortsByKeySelector,
savedViewSortsState,
viewEditModeState,
viewObjectMetadataIdState,
viewTypeState,
viewsState,
} = getViewScopedStates({
viewScopeId: scopeId,
viewId,
});
return {
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
canPersistFiltersSelector,
canPersistSortsSelector,
currentViewFieldsState,
currentViewFiltersState,
currentViewIdState,
currentViewSelector,
currentViewSortsState,
entityCountInCurrentViewState,
isViewBarExpandedState,
isPersistingViewState,
onViewFieldsChangeState,
onViewFiltersChangeState,
onViewSortsChangeState,
savedViewFieldsByKeySelector,
savedViewFieldsState,
savedViewFiltersByKeySelector,
savedViewFiltersState,
savedViewSortsByKeySelector,
savedViewSortsState,
viewEditModeState,
viewObjectMetadataIdState,
viewTypeState,
viewsState,
};
};

View File

@ -0,0 +1,230 @@
import { useApolloClient } from '@apollo/client';
import { produce } from 'immer';
import { useRecoilCallback } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
import { savedViewSortsScopedFamilyState } from '@/views/states/savedViewSortsScopedFamilyState';
import { ViewSort } from '@/views/types/ViewSort';
import { getViewScopedStateValuesFromSnapshot } from '@/views/utils/getViewScopedStateValuesFromSnapshot';
import { useViewScopedStates } from './useViewScopedStates';
export const useViewSorts = (viewScopeId: string) => {
const {
updateOneRecordMutation,
createOneRecordMutation,
deleteOneRecordMutation,
} = useObjectMetadataItem({
objectNameSingular: 'viewSort',
});
const { modifyRecordFromCache } = useObjectMetadataItem({
objectNameSingular: 'view',
});
const apolloClient = useApolloClient();
const { currentViewSortsState } = useViewScopedStates({
viewScopeId: viewScopeId,
});
const persistViewSorts = useRecoilCallback(
({ snapshot, set }) =>
async (viewId?: string) => {
const { currentViewId, currentViewSorts, savedViewSortsByKey, views } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
if (!currentViewSorts) {
return;
}
if (!savedViewSortsByKey) {
return;
}
const createViewSorts = (viewSortsToCreate: ViewSort[]) => {
if (!viewSortsToCreate.length) return;
return Promise.all(
viewSortsToCreate.map((viewSort) =>
apolloClient.mutate({
mutation: createOneRecordMutation,
variables: {
input: {
fieldMetadataId: viewSort.fieldMetadataId,
viewId: viewId ?? currentViewId,
direction: viewSort.direction,
},
},
}),
),
);
};
const updateViewSorts = (viewSortsToUpdate: ViewSort[]) => {
if (!viewSortsToUpdate.length) return;
return Promise.all(
viewSortsToUpdate.map((viewSort) =>
apolloClient.mutate({
mutation: updateOneRecordMutation,
variables: {
idToUpdate: viewSort.id,
input: {
direction: viewSort.direction,
},
},
}),
),
);
};
const deleteViewSorts = (viewSortIdsToDelete: string[]) => {
if (!viewSortIdsToDelete.length) return;
return Promise.all(
viewSortIdsToDelete.map((viewSortId) =>
apolloClient.mutate({
mutation: deleteOneRecordMutation,
variables: {
idToDelete: viewSortId,
},
}),
),
);
};
const sortsToCreate = currentViewSorts.filter(
(sort) => !savedViewSortsByKey[sort.fieldMetadataId],
);
await createViewSorts(sortsToCreate);
const sortsToUpdate = currentViewSorts.filter(
(sort) =>
savedViewSortsByKey[sort.fieldMetadataId] &&
savedViewSortsByKey[sort.fieldMetadataId].direction !==
sort.direction,
);
await updateViewSorts(sortsToUpdate);
const sortKeys = currentViewSorts.map((sort) => sort.fieldMetadataId);
const sortKeysToDelete = Object.keys(savedViewSortsByKey).filter(
(previousSortKey) => !sortKeys.includes(previousSortKey),
);
const sortIdsToDelete = sortKeysToDelete.map(
(sortKeyToDelete) => savedViewSortsByKey[sortKeyToDelete].id ?? '',
);
await deleteViewSorts(sortIdsToDelete);
set(
savedViewSortsScopedFamilyState({
scopeId: viewScopeId,
familyKey: viewId ?? currentViewId,
}),
currentViewSorts,
);
const existingViewId = viewId ?? currentViewId;
const existingView = views.find((view) => view.id === existingViewId);
if (!existingView) {
return;
}
modifyRecordFromCache(existingViewId, {
viewSorts: () => ({
edges: currentViewSorts.map((viewSort) => ({
node: viewSort,
cursor: '',
})),
pageInfo: {
hasNextPage: false,
hasPreviousPage: false,
startCursor: '',
endCursor: '',
},
}),
});
},
[
apolloClient,
createOneRecordMutation,
deleteOneRecordMutation,
modifyRecordFromCache,
updateOneRecordMutation,
viewScopeId,
],
);
const upsertViewSort = useRecoilCallback(
({ snapshot, set }) =>
(sortToUpsert: Sort) => {
const { currentViewId, onViewSortsChange, savedViewSortsByKey } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
if (!savedViewSortsByKey) {
return;
}
const existingSavedSortId =
savedViewSortsByKey[sortToUpsert.fieldMetadataId]?.id;
set(currentViewSortsState, (sorts) => {
const newViewSorts = produce(sorts, (sortsDraft) => {
const existingSortIndex = sortsDraft.findIndex(
(sort) => sort.fieldMetadataId === sortToUpsert.fieldMetadataId,
);
if (existingSortIndex === -1) {
sortsDraft.push({ ...sortToUpsert, id: existingSavedSortId });
return sortsDraft;
}
sortsDraft[existingSortIndex] = {
...sortToUpsert,
id: existingSavedSortId,
};
});
onViewSortsChange?.(newViewSorts);
return newViewSorts;
});
},
[currentViewSortsState, viewScopeId],
);
const removeViewSort = useRecoilCallback(
({ snapshot, set }) =>
(fieldMetadataId: string) => {
const { currentViewId, onViewSortsChange, currentViewSorts } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId,
});
if (!currentViewId) {
return;
}
const newViewSorts = currentViewSorts.filter((filter) => {
return filter.fieldMetadataId !== fieldMetadataId;
});
set(currentViewSortsState, newViewSorts);
onViewSortsChange?.(newViewSorts);
},
[currentViewSortsState, viewScopeId],
);
return { persistViewSorts, upsertViewSort, removeViewSort };
};

View File

@ -0,0 +1,78 @@
import { useApolloClient } from '@apollo/client';
import { useRecoilCallback } from 'recoil';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { GraphQLView } from '@/views/types/GraphQLView';
import { getViewScopedStateValuesFromSnapshot } from '@/views/utils/getViewScopedStateValuesFromSnapshot';
export const useViews = (scopeId: string) => {
const {
updateOneRecordMutation: updateOneMutation,
createOneRecordMutation: createOneMutation,
deleteOneRecordMutation: deleteOneMutation,
findManyRecordsQuery: findManyQuery,
} = useObjectMetadataItem({
objectNameSingular: 'view',
});
const apolloClient = useApolloClient();
const createView = useRecoilCallback(
({ snapshot }) =>
async (view: Pick<GraphQLView, 'id' | 'name'>) => {
const { viewObjectMetadataId, viewType } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
});
if (!viewObjectMetadataId || !viewType) {
return;
}
await apolloClient.mutate({
mutation: createOneMutation,
variables: {
input: {
id: view.id,
name: view.name,
objectMetadataId: viewObjectMetadataId,
type: viewType,
},
},
refetchQueries: [findManyQuery],
});
},
[scopeId, apolloClient, createOneMutation, findManyQuery],
);
const updateView = async (view: GraphQLView) => {
await apolloClient.mutate({
mutation: updateOneMutation,
variables: {
idToUpdate: view.id,
input: {
id: view.id,
name: view.name,
},
},
refetchQueries: [findManyQuery],
});
};
const deleteView = async (viewId: string) => {
await apolloClient.mutate({
mutation: deleteOneMutation,
variables: {
idToDelete: viewId,
},
refetchQueries: [findManyQuery],
});
};
return {
createView,
deleteView,
isFetchingViews: false,
updateView,
};
};

View File

@ -0,0 +1,39 @@
export const useMoveViewColumns = () => {
const handleColumnMove = <T extends { position: number }>(
direction: 'left' | 'right',
currentArrayindex: number,
targetArray: T[],
) => {
const targetArrayIndex =
direction === 'left' ? currentArrayindex - 1 : currentArrayindex + 1;
const targetArraySize = targetArray.length - 1;
if (
currentArrayindex >= 0 &&
targetArrayIndex >= 0 &&
currentArrayindex <= targetArraySize &&
targetArrayIndex <= targetArraySize
) {
const currentEntity = targetArray[currentArrayindex];
const targetEntity = targetArray[targetArrayIndex];
const newArray = [...targetArray];
newArray[currentArrayindex] = {
...targetEntity,
index: currentEntity.position,
};
newArray[targetArrayIndex] = {
...currentEntity,
index: targetEntity.position,
};
return newArray.map((column, index) => ({
...column,
position: index,
}));
}
return targetArray;
};
return { handleColumnMove };
};

View File

@ -0,0 +1,447 @@
import { useCallback } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useRecoilCallback, useRecoilState, useSetRecoilState } from 'recoil';
import { v4 } from 'uuid';
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { ViewField } from '@/views/types/ViewField';
import { ViewFilter } from '@/views/types/ViewFilter';
import { ViewSort } from '@/views/types/ViewSort';
import { assertNotNull } from '~/utils/assert';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext';
import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState';
import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState';
import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState';
import { getViewScopedStatesFromSnapshot } from '../utils/getViewScopedStatesFromSnapshot';
import { getViewScopedStateValuesFromSnapshot } from '../utils/getViewScopedStateValuesFromSnapshot';
import { useViewFields } from './internal/useViewFields';
import { useViewFilters } from './internal/useViewFilters';
import { useViews } from './internal/useViews';
import { useViewScopedStates } from './internal/useViewScopedStates';
import { useViewSorts } from './internal/useViewSorts';
type UseViewProps = {
viewBarId?: string;
};
export const useViewBar = (props?: UseViewProps) => {
const scopeId = useAvailableScopeIdOrThrow(
ViewScopeInternalContext,
props?.viewBarId,
);
const {
currentViewFiltersState,
currentViewIdState,
currentViewSortsState,
viewEditModeState,
availableFieldDefinitionsState,
availableFilterDefinitionsState,
availableSortDefinitionsState,
entityCountInCurrentViewState,
viewObjectMetadataIdState,
viewTypeState,
} = useViewScopedStates({
viewScopeId: scopeId,
});
const { persistViewSorts, upsertViewSort, removeViewSort } =
useViewSorts(scopeId);
const { persistViewFilters, upsertViewFilter, removeViewFilter } =
useViewFilters(scopeId);
const { persistViewFields } = useViewFields(scopeId);
const {
createView: internalCreateView,
updateView: internalUpdateView,
deleteView: internalDeleteView,
} = useViews(scopeId);
const [currentViewId, setCurrentViewId] = useRecoilState(currentViewIdState);
const setAvailableFieldDefinitions = useSetRecoilState(
availableFieldDefinitionsState,
);
const setAvailableSortDefinitions = useSetRecoilState(
availableSortDefinitionsState,
);
const setAvailableFilterDefinitions = useSetRecoilState(
availableFilterDefinitionsState,
);
const setEntityCountInCurrentView = useSetRecoilState(
entityCountInCurrentViewState,
);
const setViewEditMode = useSetRecoilState(viewEditModeState);
const setViewObjectMetadataId = useSetRecoilState(viewObjectMetadataIdState);
const setViewType = useSetRecoilState(viewTypeState);
const [_, setSearchParams] = useSearchParams();
const changeViewInUrl = useCallback(
(viewId: string) => {
setSearchParams({ view: viewId });
},
[setSearchParams],
);
const loadViewFields = useRecoilCallback(
({ snapshot, set }) =>
async (
data: PaginatedRecordTypeResults<ViewField>,
currentViewId: string,
) => {
const {
availableFieldDefinitions,
onViewFieldsChange,
savedViewFields,
isPersistingView,
} = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
viewId: currentViewId,
});
const { savedViewFieldsState, currentViewFieldsState } =
getViewScopedStatesFromSnapshot({
snapshot,
viewScopeId: scopeId,
viewId: currentViewId,
});
if (!availableFieldDefinitions) {
return;
}
const queriedViewFields = data.edges
.map((viewField) => viewField.node)
.filter(assertNotNull);
if (isPersistingView) {
return;
}
if (!isDeeplyEqual(savedViewFields, queriedViewFields)) {
set(currentViewFieldsState, queriedViewFields);
set(savedViewFieldsState, queriedViewFields);
onViewFieldsChange?.(queriedViewFields);
}
},
[scopeId],
);
const loadViewFilters = useRecoilCallback(
({ snapshot, set }) =>
async (
data: PaginatedRecordTypeResults<Required<ViewFilter>>,
currentViewId: string,
) => {
const {
availableFilterDefinitions,
savedViewFilters,
onViewFiltersChange,
} = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
viewId: currentViewId,
});
const { savedViewFiltersState, currentViewFiltersState } =
getViewScopedStatesFromSnapshot({
snapshot,
viewScopeId: scopeId,
viewId: currentViewId,
});
if (!availableFilterDefinitions) {
return;
}
const queriedViewFilters = data.edges
.map(({ node }) => {
const availableFilterDefinition = availableFilterDefinitions.find(
(filterDefinition) =>
filterDefinition.fieldMetadataId === node.fieldMetadataId,
);
if (!availableFilterDefinition) return null;
return {
...node,
displayValue: node.displayValue ?? node.value,
definition: availableFilterDefinition,
};
})
.filter(assertNotNull);
if (!isDeeplyEqual(savedViewFilters, queriedViewFilters)) {
set(savedViewFiltersState, queriedViewFilters);
set(currentViewFiltersState, queriedViewFilters);
}
onViewFiltersChange?.(queriedViewFilters);
},
[scopeId],
);
const loadViewSorts = useRecoilCallback(
({ snapshot, set }) =>
async (
data: PaginatedRecordTypeResults<Required<ViewSort>>,
currentViewId: string,
) => {
const { availableSortDefinitions, savedViewSorts, onViewSortsChange } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
viewId: currentViewId,
});
const { savedViewSortsState, currentViewSortsState } =
getViewScopedStatesFromSnapshot({
snapshot,
viewScopeId: scopeId,
viewId: currentViewId,
});
if (!availableSortDefinitions || !currentViewId) {
return;
}
const queriedViewSorts = data.edges
.map(({ node }) => {
const availableSortDefinition = availableSortDefinitions.find(
(sort) => sort.fieldMetadataId === node.fieldMetadataId,
);
if (!availableSortDefinition) return null;
return {
id: node.id,
fieldMetadataId: node.fieldMetadataId,
direction: node.direction,
definition: availableSortDefinition,
};
})
.filter(assertNotNull);
if (!isDeeplyEqual(savedViewSorts, queriedViewSorts)) {
set(savedViewSortsState, queriedViewSorts);
set(currentViewSortsState, queriedViewSorts);
}
onViewSortsChange?.(queriedViewSorts);
},
[scopeId],
);
const loadView = useRecoilCallback(
({ snapshot }) =>
(viewId: string) => {
setCurrentViewId?.(viewId);
const { currentView } = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
viewId,
});
if (!currentView) {
return;
}
loadViewFields(currentView.viewFields, viewId);
loadViewFilters(currentView.viewFilters, viewId);
loadViewSorts(currentView.viewSorts, viewId);
},
[setCurrentViewId, scopeId, loadViewFields, loadViewFilters, loadViewSorts],
);
const resetViewBar = useRecoilCallback(
({ snapshot, set }) =>
() => {
const {
savedViewFilters,
savedViewSorts,
onViewFiltersChange,
onViewSortsChange,
} = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
});
if (savedViewFilters) {
set(currentViewFiltersState, savedViewFilters);
onViewFiltersChange?.(savedViewFilters);
}
if (savedViewSorts) {
set(currentViewSortsState, savedViewSorts);
onViewSortsChange?.(savedViewSorts);
}
set(viewEditModeState, 'none');
},
[
currentViewFiltersState,
currentViewSortsState,
scopeId,
viewEditModeState,
],
);
const createView = useRecoilCallback(
({ snapshot, set }) =>
async (name: string) => {
const newViewId = v4();
await internalCreateView({ id: newViewId, name });
const { currentViewFields, currentViewFilters, currentViewSorts } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
});
set(
currentViewFieldsScopedFamilyState({ scopeId, familyKey: newViewId }),
currentViewFields,
);
set(
currentViewFiltersScopedFamilyState({
scopeId,
familyKey: newViewId,
}),
currentViewFilters,
);
set(
currentViewSortsScopedFamilyState({
scopeId,
familyKey: newViewId,
}),
currentViewSorts,
);
await persistViewFields(currentViewFields, newViewId);
await persistViewFilters(newViewId);
await persistViewSorts(newViewId);
changeViewInUrl(newViewId);
},
[
changeViewInUrl,
internalCreateView,
persistViewFields,
persistViewFilters,
persistViewSorts,
scopeId,
],
);
const updateCurrentView = async () => {
await persistViewFilters();
await persistViewSorts();
};
const removeView = useRecoilCallback(
({ set, snapshot }) =>
async (viewIdToDelete: string) => {
const { currentViewId } = getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
});
const { currentViewIdState, viewsState } =
getViewScopedStatesFromSnapshot({
snapshot,
viewScopeId: scopeId,
});
if (currentViewId === viewIdToDelete) {
set(currentViewIdState, undefined);
}
set(viewsState, (previousViews) =>
previousViews.filter((view) => view.id !== viewIdToDelete),
);
internalDeleteView(viewIdToDelete);
if (currentViewId === viewIdToDelete) {
setSearchParams();
}
},
[internalDeleteView, scopeId, setSearchParams],
);
const handleViewNameSubmit = useRecoilCallback(
({ snapshot }) =>
async (name?: string) => {
if (!name) {
return;
}
const { viewEditMode, currentView } =
getViewScopedStateValuesFromSnapshot({
snapshot,
viewScopeId: scopeId,
});
if (!currentView) {
return;
}
if (viewEditMode === 'create' && name) {
await createView(name);
// Temporary to force refetch
await internalUpdateView({
...currentView,
});
} else {
await internalUpdateView({
...currentView,
name,
});
}
},
[createView, internalUpdateView, scopeId],
);
return {
scopeId,
currentViewId,
setCurrentViewId,
updateCurrentView,
createView,
removeView,
resetViewBar,
handleViewNameSubmit,
setViewEditMode,
setViewObjectMetadataId,
setViewType,
setEntityCountInCurrentView,
setAvailableFieldDefinitions,
setAvailableSortDefinitions,
upsertViewSort,
removeViewSort,
setAvailableFilterDefinitions,
upsertViewFilter,
removeViewFilter,
persistViewFields,
changeViewInUrl,
loadView,
loadViewFields,
loadViewFilters,
loadViewSorts,
};
};