Refactor Views by cleaning the code, relying on apolloCache and improving performances (#4516)
* Wip refactoring view * Post merge conflicts * Fix review * Add create view capability * Fix create object missing view * Fix tests
This commit is contained in:
@ -1,10 +1,10 @@
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { getOperandLabelShort } from '@/object-record/object-filter-dropdown/utils/getOperandLabel';
|
||||
import { useIcons } from '@/ui/display/icon/hooks/useIcons';
|
||||
import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
|
||||
type EditableFilterChipProps = {
|
||||
viewFilter: ViewFilter;
|
||||
viewFilter: Filter;
|
||||
onRemove: () => void;
|
||||
};
|
||||
|
||||
|
||||
@ -1,18 +1,19 @@
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { MultipleFiltersDropdownContent } from '@/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent';
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { EditableFilterChip } from '@/views/components/EditableFilterChip';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { useCombinedViewFilters } from '@/views/hooks/useCombinedViewFilters';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
type EditableFilterDropdownButtonProps = {
|
||||
viewFilterDropdownId: string;
|
||||
viewFilter: ViewFilter;
|
||||
viewFilter: Filter;
|
||||
hotkeyScope: HotkeyScope;
|
||||
};
|
||||
|
||||
@ -22,7 +23,7 @@ export const EditableFilterDropdownButton = ({
|
||||
hotkeyScope,
|
||||
}: EditableFilterDropdownButtonProps) => {
|
||||
const {
|
||||
availableFilterDefinitions,
|
||||
availableFilterDefinitionsState,
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
setSelectedFilter,
|
||||
@ -30,9 +31,13 @@ export const EditableFilterDropdownButton = ({
|
||||
filterDropdownId: viewFilterDropdownId,
|
||||
});
|
||||
|
||||
const availableFilterDefinitions = useRecoilValue(
|
||||
availableFilterDefinitionsState,
|
||||
);
|
||||
|
||||
const { closeDropdown } = useDropdown(viewFilterDropdownId);
|
||||
|
||||
const { removeViewFilter } = useViewBar();
|
||||
const { removeCombinedViewFilter } = useCombinedViewFilters();
|
||||
|
||||
useEffect(() => {
|
||||
const filterDefinition = availableFilterDefinitions.find(
|
||||
@ -57,15 +62,15 @@ export const EditableFilterDropdownButton = ({
|
||||
const handleRemove = () => {
|
||||
closeDropdown();
|
||||
|
||||
removeViewFilter(viewFilter.fieldMetadataId);
|
||||
removeCombinedViewFilter(viewFilter.fieldMetadataId);
|
||||
};
|
||||
|
||||
const handleDropdownClickOutside = useCallback(() => {
|
||||
const { value, fieldMetadataId } = viewFilter;
|
||||
if (!value) {
|
||||
removeViewFilter(fieldMetadataId);
|
||||
removeCombinedViewFilter(fieldMetadataId);
|
||||
}
|
||||
}, [viewFilter, removeViewFilter]);
|
||||
}, [viewFilter, removeCombinedViewFilter]);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
|
||||
@ -1,21 +1,22 @@
|
||||
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
|
||||
import { IconArrowDown, IconArrowUp } from '@/ui/display/icon/index';
|
||||
import { SortOrFilterChip } from '@/views/components/SortOrFilterChip';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { useCombinedViewSorts } from '@/views/hooks/useCombinedViewSorts';
|
||||
|
||||
type EditableSortChipProps = {
|
||||
viewSort: ViewSort;
|
||||
viewSort: Sort;
|
||||
};
|
||||
|
||||
export const EditableSortChip = ({ viewSort }: EditableSortChipProps) => {
|
||||
const { removeViewSort, upsertViewSort } = useViewBar();
|
||||
const { removeCombinedViewSort, upsertCombinedViewSort } =
|
||||
useCombinedViewSorts();
|
||||
|
||||
const handleRemoveClick = () => {
|
||||
removeViewSort(viewSort.fieldMetadataId);
|
||||
removeCombinedViewSort(viewSort.fieldMetadataId);
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
upsertViewSort({
|
||||
upsertCombinedViewSort({
|
||||
...viewSort,
|
||||
direction: viewSort.direction === 'asc' ? 'desc' : 'asc',
|
||||
});
|
||||
|
||||
@ -1,38 +1,47 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { isUndefined } from '@sniptt/guards';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useFiltersFromQueryParams } from '@/views/hooks/internal/useFiltersFromQueryParams';
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { useResetCurrentView } from '@/views/hooks/useResetCurrentView';
|
||||
|
||||
export const FilterQueryParamsEffect = () => {
|
||||
const { hasFiltersQueryParams, getFiltersFromQueryParams } =
|
||||
useFiltersFromQueryParams();
|
||||
const { currentViewFiltersState, onViewFiltersChangeState } =
|
||||
useViewScopedStates();
|
||||
const setCurrentViewFilters = useSetRecoilState(currentViewFiltersState);
|
||||
const onViewFiltersChange = useRecoilValue(onViewFiltersChangeState);
|
||||
const { resetViewBar } = useViewBar();
|
||||
const { hasFiltersQueryParams, getFiltersFromQueryParams, viewIdQueryParam } =
|
||||
useViewFromQueryParams();
|
||||
const { unsavedToUpsertViewFiltersState, currentViewIdState } =
|
||||
useViewStates();
|
||||
const setUnsavedViewFilter = useSetRecoilState(
|
||||
unsavedToUpsertViewFiltersState,
|
||||
);
|
||||
const setCurrentViewId = useSetRecoilState(currentViewIdState);
|
||||
const { resetCurrentView } = useResetCurrentView();
|
||||
|
||||
useEffect(() => {
|
||||
if (isUndefined(viewIdQueryParam) || !viewIdQueryParam) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCurrentViewId(viewIdQueryParam);
|
||||
}, [getFiltersFromQueryParams, setCurrentViewId, viewIdQueryParam]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasFiltersQueryParams) return;
|
||||
|
||||
getFiltersFromQueryParams().then((filtersFromParams) => {
|
||||
if (Array.isArray(filtersFromParams)) {
|
||||
setCurrentViewFilters(filtersFromParams);
|
||||
onViewFiltersChange?.(filtersFromParams);
|
||||
setUnsavedViewFilter(filtersFromParams);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
resetViewBar();
|
||||
resetCurrentView();
|
||||
};
|
||||
}, [
|
||||
getFiltersFromQueryParams,
|
||||
hasFiltersQueryParams,
|
||||
onViewFiltersChange,
|
||||
resetViewBar,
|
||||
setCurrentViewFilters,
|
||||
resetCurrentView,
|
||||
setUnsavedViewFilter,
|
||||
]);
|
||||
|
||||
return null;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useCallback } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { IconChevronDown, IconPlus } from '@/ui/display/icon';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
@ -10,9 +10,8 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { UPDATE_VIEW_DROPDOWN_ID } from '@/views/constants/UpdateViewDropdownId';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
|
||||
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { useSaveCurrentViewFiltersAndSorts } from '@/views/hooks/useSaveCurrentViewFiltersAndSorts';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
background: ${({ theme }) => theme.color.blue};
|
||||
@ -31,14 +30,11 @@ export const UpdateViewButtonGroup = ({
|
||||
hotkeyScope,
|
||||
onViewEditModeChange,
|
||||
}: UpdateViewButtonGroupProps) => {
|
||||
const { updateCurrentView, setViewEditMode } = useViewBar();
|
||||
const { canPersistFiltersSelector, canPersistSortsSelector } =
|
||||
useViewScopedStates();
|
||||
const { canPersistViewSelector, viewEditModeState } = useViewStates();
|
||||
const { saveCurrentViewFilterAndSorts } = useSaveCurrentViewFiltersAndSorts();
|
||||
|
||||
const canPersistFilters = useRecoilValue(canPersistFiltersSelector);
|
||||
const canPersistSorts = useRecoilValue(canPersistSortsSelector);
|
||||
|
||||
const canPersistView = canPersistFilters || canPersistSorts;
|
||||
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
||||
const canPersistView = useRecoilValue(canPersistViewSelector());
|
||||
|
||||
const handleCreateViewButtonClick = useCallback(() => {
|
||||
setViewEditMode('create');
|
||||
@ -46,7 +42,7 @@ export const UpdateViewButtonGroup = ({
|
||||
}, [setViewEditMode, onViewEditModeChange]);
|
||||
|
||||
const handleViewSubmit = async () => {
|
||||
await updateCurrentView?.();
|
||||
await saveCurrentViewFilterAndSorts();
|
||||
};
|
||||
|
||||
if (!canPersistView) {
|
||||
|
||||
@ -10,12 +10,8 @@ import { FilterQueryParamsEffect } from '@/views/components/FilterQueryParamsEff
|
||||
import { ViewBarEffect } from '@/views/components/ViewBarEffect';
|
||||
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
|
||||
import { ViewBarSortEffect } from '@/views/components/ViewBarSortEffect';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
|
||||
import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
|
||||
|
||||
@ -28,13 +24,7 @@ export type ViewBarProps = {
|
||||
className?: string;
|
||||
optionsDropdownButton: ReactNode;
|
||||
optionsDropdownScopeId: string;
|
||||
onViewSortsChange?: (sorts: ViewSort[]) => void | Promise<void>;
|
||||
onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise<void>;
|
||||
onViewFieldsChange?: (fields: ViewField[]) => void | Promise<void>;
|
||||
onViewTypeChange?: (viewType: ViewType) => void | Promise<void>;
|
||||
onViewCompactModeChange?: (
|
||||
isCompactModeActive: boolean,
|
||||
) => void | Promise<void>;
|
||||
onCurrentViewChange: (view: GraphQLView | undefined) => void | Promise<void>;
|
||||
};
|
||||
|
||||
export const ViewBar = ({
|
||||
@ -42,18 +32,12 @@ export const ViewBar = ({
|
||||
className,
|
||||
optionsDropdownButton,
|
||||
optionsDropdownScopeId,
|
||||
onViewFieldsChange,
|
||||
onViewFiltersChange,
|
||||
onViewSortsChange,
|
||||
onViewTypeChange,
|
||||
onViewCompactModeChange,
|
||||
onCurrentViewChange,
|
||||
}: ViewBarProps) => {
|
||||
const { openDropdown: openOptionsDropdownButton } = useDropdown(
|
||||
optionsDropdownScopeId,
|
||||
);
|
||||
const { upsertViewSort, upsertViewFilter } = useViewBar({
|
||||
viewBarId,
|
||||
});
|
||||
|
||||
const { objectNamePlural } = useParams();
|
||||
|
||||
const filterDropdownId = 'view-filter';
|
||||
@ -62,21 +46,11 @@ export const ViewBar = ({
|
||||
return (
|
||||
<ViewScope
|
||||
viewScopeId={viewBarId}
|
||||
onViewFieldsChange={onViewFieldsChange}
|
||||
onViewFiltersChange={onViewFiltersChange}
|
||||
onViewSortsChange={onViewSortsChange}
|
||||
onViewTypeChange={onViewTypeChange}
|
||||
onViewCompactModeChange={onViewCompactModeChange}
|
||||
onCurrentViewChange={onCurrentViewChange}
|
||||
>
|
||||
<ViewBarEffect />
|
||||
<ViewBarFilterEffect
|
||||
filterDropdownId={filterDropdownId}
|
||||
onFilterSelect={upsertViewFilter}
|
||||
/>
|
||||
<ViewBarSortEffect
|
||||
sortDropdownId={sortDropdownId}
|
||||
onSortSelect={upsertViewSort}
|
||||
/>
|
||||
<ViewBarEffect viewBarId={viewBarId} />
|
||||
<ViewBarFilterEffect filterDropdownId={filterDropdownId} />
|
||||
<ViewBarSortEffect sortDropdownId={sortDropdownId} />
|
||||
{!!objectNamePlural && <FilterQueryParamsEffect />}
|
||||
|
||||
<TopBar
|
||||
|
||||
@ -9,9 +9,11 @@ import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { EditableFilterDropdownButton } from '@/views/components/EditableFilterDropdownButton';
|
||||
import { EditableSortChip } from '@/views/components/EditableSortChip';
|
||||
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
|
||||
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||
import { useResetCurrentView } from '@/views/hooks/useResetCurrentView';
|
||||
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
|
||||
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
|
||||
|
||||
export type ViewBarDetailsProps = {
|
||||
hasFilterButton?: boolean;
|
||||
@ -25,9 +27,11 @@ const StyledBar = styled.div`
|
||||
border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 40px;
|
||||
min-height: 32px;
|
||||
justify-content: space-between;
|
||||
z-index: 4;
|
||||
padding-top: ${({ theme }) => theme.spacing(1)};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
const StyledChipcontainer = styled.div`
|
||||
@ -35,10 +39,9 @@ const StyledChipcontainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
height: 40px;
|
||||
justify-content: space-between;
|
||||
min-height: 32px;
|
||||
margin-left: ${({ theme }) => theme.spacing(2)};
|
||||
overflow-x: auto;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const StyledCancelButton = styled.button`
|
||||
@ -92,37 +95,35 @@ export const ViewBarDetails = ({
|
||||
hasFilterButton = false,
|
||||
rightComponent,
|
||||
filterDropdownId,
|
||||
viewBarId,
|
||||
}: ViewBarDetailsProps) => {
|
||||
const {
|
||||
currentViewSortsState,
|
||||
currentViewFiltersState,
|
||||
canPersistFiltersSelector,
|
||||
canPersistSortsSelector,
|
||||
canPersistViewSelector,
|
||||
isViewBarExpandedState,
|
||||
} = useViewScopedStates();
|
||||
availableFilterDefinitionsState,
|
||||
availableSortDefinitionsState,
|
||||
} = useViewStates();
|
||||
|
||||
const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView();
|
||||
|
||||
const currentViewSorts = useRecoilValue(currentViewSortsState);
|
||||
const currentViewFilters = useRecoilValue(currentViewFiltersState);
|
||||
const canPersistFilters = useRecoilValue(canPersistFiltersSelector);
|
||||
const canPersistSorts = useRecoilValue(canPersistSortsSelector);
|
||||
const isViewBarExpanded = useRecoilValue(isViewBarExpandedState);
|
||||
const canPersistView = useRecoilValue(canPersistViewSelector());
|
||||
const availableFilterDefinitions = useRecoilValue(
|
||||
availableFilterDefinitionsState,
|
||||
);
|
||||
const availableSortDefinitions = useRecoilValue(
|
||||
availableSortDefinitionsState,
|
||||
);
|
||||
|
||||
const { resetViewBar } = useViewBar();
|
||||
|
||||
const canPersistView = canPersistFilters || canPersistSorts;
|
||||
const { resetCurrentView } = useResetCurrentView();
|
||||
|
||||
const handleCancelClick = () => {
|
||||
resetViewBar();
|
||||
resetCurrentView();
|
||||
};
|
||||
|
||||
const { upsertViewFilter } = useViewBar({
|
||||
viewBarId: viewBarId,
|
||||
});
|
||||
|
||||
const shouldExpandViewBar =
|
||||
canPersistView ||
|
||||
((currentViewSorts?.length || currentViewFilters?.length) &&
|
||||
((currentViewWithCombinedFiltersAndSorts?.viewSorts?.length ||
|
||||
currentViewWithCombinedFiltersAndSorts?.viewFilters?.length) &&
|
||||
isViewBarExpanded);
|
||||
|
||||
if (!shouldExpandViewBar) {
|
||||
@ -133,23 +134,29 @@ export const ViewBarDetails = ({
|
||||
<StyledBar>
|
||||
<StyledFilterContainer>
|
||||
<StyledChipcontainer>
|
||||
{currentViewSorts?.map((sort) => (
|
||||
<EditableSortChip key={sort.id} viewSort={sort} />
|
||||
{mapViewSortsToSorts(
|
||||
currentViewWithCombinedFiltersAndSorts?.viewSorts ?? [],
|
||||
availableSortDefinitions,
|
||||
).map((sort) => (
|
||||
<EditableSortChip key={sort.fieldMetadataId} viewSort={sort} />
|
||||
))}
|
||||
{!!currentViewSorts?.length && !!currentViewFilters?.length && (
|
||||
<StyledSeperatorContainer>
|
||||
<StyledSeperator />
|
||||
</StyledSeperatorContainer>
|
||||
)}
|
||||
{currentViewFilters?.map((viewFilter) => (
|
||||
{!!currentViewWithCombinedFiltersAndSorts?.viewSorts?.length &&
|
||||
!!currentViewWithCombinedFiltersAndSorts?.viewFilters?.length && (
|
||||
<StyledSeperatorContainer>
|
||||
<StyledSeperator />
|
||||
</StyledSeperatorContainer>
|
||||
)}
|
||||
{mapViewFiltersToFilters(
|
||||
currentViewWithCombinedFiltersAndSorts?.viewFilters ?? [],
|
||||
availableFilterDefinitions,
|
||||
).map((viewFilter) => (
|
||||
<ObjectFilterDropdownScope
|
||||
key={viewFilter.id}
|
||||
key={viewFilter.fieldMetadataId}
|
||||
filterScopeId={viewFilter.fieldMetadataId}
|
||||
>
|
||||
<DropdownScope dropdownScopeId={viewFilter.fieldMetadataId}>
|
||||
<ViewBarFilterEffect
|
||||
filterDropdownId={viewFilter.fieldMetadataId}
|
||||
onFilterSelect={upsertViewFilter}
|
||||
/>
|
||||
<EditableFilterDropdownButton
|
||||
viewFilter={viewFilter}
|
||||
|
||||
@ -1,86 +1,71 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { isUndefined } from '@sniptt/guards';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
|
||||
type ViewBarEffectProps = {
|
||||
viewBarId: string;
|
||||
};
|
||||
|
||||
export const ViewBarEffect = () => {
|
||||
export const ViewBarEffect = ({ viewBarId }: ViewBarEffectProps) => {
|
||||
const { currentViewWithCombinedFiltersAndSorts } =
|
||||
useGetCurrentView(viewBarId);
|
||||
const {
|
||||
loadView,
|
||||
changeViewInUrl,
|
||||
loadViewFields,
|
||||
loadViewFilters,
|
||||
loadViewSorts,
|
||||
} = useViewBar();
|
||||
onCurrentViewChangeState,
|
||||
currentViewIdState,
|
||||
availableFilterDefinitionsState,
|
||||
isPersistingViewFieldsState,
|
||||
} = useViewStates(viewBarId);
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const currentViewIdFromUrl = searchParams.get('view');
|
||||
|
||||
const { viewObjectMetadataIdState, viewsState, currentViewIdState } =
|
||||
useViewScopedStates();
|
||||
|
||||
const [views, setViews] = useRecoilState(viewsState);
|
||||
const viewObjectMetadataId = useRecoilValue(viewObjectMetadataIdState);
|
||||
const setCurrentViewId = useSetRecoilState(currentViewIdState);
|
||||
|
||||
const { records: newViews } = usePrefetchedData<GraphQLView>(
|
||||
PrefetchKey.AllViews,
|
||||
);
|
||||
|
||||
const newViewsOnCurrentObject = newViews.filter(
|
||||
(view) => view.objectMetadataId === viewObjectMetadataId,
|
||||
const [currentViewSnapshot, setCurrentViewSnapshot] = useState<
|
||||
GraphQLView | undefined
|
||||
>(undefined);
|
||||
const onCurrentViewChange = useRecoilValue(onCurrentViewChangeState);
|
||||
const availableFilterDefinitions = useRecoilValue(
|
||||
availableFilterDefinitionsState,
|
||||
);
|
||||
const isPersistingViewFields = useRecoilValue(isPersistingViewFieldsState);
|
||||
const [currentViewId, setCurrentViewId] = useRecoilState(currentViewIdState);
|
||||
|
||||
useEffect(() => {
|
||||
if (!newViewsOnCurrentObject.length) return;
|
||||
if (
|
||||
!isDeeplyEqual(
|
||||
currentViewWithCombinedFiltersAndSorts,
|
||||
currentViewSnapshot,
|
||||
)
|
||||
) {
|
||||
setCurrentViewSnapshot(currentViewWithCombinedFiltersAndSorts);
|
||||
|
||||
if (!isDeeplyEqual(views, newViewsOnCurrentObject)) {
|
||||
setViews(newViewsOnCurrentObject);
|
||||
if (isUndefined(currentViewWithCombinedFiltersAndSorts)) {
|
||||
onCurrentViewChange?.(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isPersistingViewFields) {
|
||||
onCurrentViewChange?.(currentViewWithCombinedFiltersAndSorts);
|
||||
}
|
||||
}
|
||||
|
||||
const currentView =
|
||||
newViewsOnCurrentObject.find(
|
||||
(view) => view.id === currentViewIdFromUrl,
|
||||
) ??
|
||||
newViewsOnCurrentObject[0] ??
|
||||
null;
|
||||
|
||||
if (isUndefinedOrNull(currentView)) return;
|
||||
|
||||
setCurrentViewId(currentView.id);
|
||||
|
||||
if (isDefined(currentView?.viewFields)) {
|
||||
loadViewFields(currentView.viewFields, currentView.id);
|
||||
loadViewFilters(currentView.viewFilters, currentView.id);
|
||||
loadViewSorts(currentView.viewSorts, currentView.id);
|
||||
}
|
||||
|
||||
if (!currentViewIdFromUrl) return changeViewInUrl(currentView.id);
|
||||
}, [
|
||||
changeViewInUrl,
|
||||
currentViewIdFromUrl,
|
||||
loadViewFields,
|
||||
loadViewFilters,
|
||||
loadViewSorts,
|
||||
newViewsOnCurrentObject,
|
||||
setCurrentViewId,
|
||||
setViews,
|
||||
views,
|
||||
availableFilterDefinitions,
|
||||
currentViewSnapshot,
|
||||
currentViewWithCombinedFiltersAndSorts,
|
||||
isPersistingViewFields,
|
||||
onCurrentViewChange,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentViewIdFromUrl || !newViewsOnCurrentObject.length) return;
|
||||
|
||||
loadView(currentViewIdFromUrl);
|
||||
}, [currentViewIdFromUrl, loadView, newViewsOnCurrentObject]);
|
||||
if (
|
||||
isDefined(currentViewWithCombinedFiltersAndSorts) &&
|
||||
!isDefined(currentViewId)
|
||||
) {
|
||||
setCurrentViewId(currentViewWithCombinedFiltersAndSorts.id);
|
||||
}
|
||||
}, [currentViewWithCombinedFiltersAndSorts, currentViewId, setCurrentViewId]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
@ -4,20 +4,21 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { useCombinedViewFilters } from '@/views/hooks/useCombinedViewFilters';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
type ViewBarFilterEffectProps = {
|
||||
filterDropdownId: string;
|
||||
onFilterSelect?: ((filter: Filter) => void) | undefined;
|
||||
};
|
||||
|
||||
export const ViewBarFilterEffect = ({
|
||||
filterDropdownId,
|
||||
onFilterSelect,
|
||||
}: ViewBarFilterEffectProps) => {
|
||||
const { availableFilterDefinitionsState, currentViewFiltersState } =
|
||||
useViewScopedStates();
|
||||
const { availableFilterDefinitionsState, unsavedToUpsertViewFiltersState } =
|
||||
useViewStates();
|
||||
|
||||
const { upsertCombinedViewFilter } = useCombinedViewFilters();
|
||||
|
||||
const availableFilterDefinitions = useRecoilValue(
|
||||
availableFilterDefinitionsState,
|
||||
@ -25,32 +26,38 @@ export const ViewBarFilterEffect = ({
|
||||
const {
|
||||
setAvailableFilterDefinitions,
|
||||
setOnFilterSelect,
|
||||
filterDefinitionUsedInDropdown,
|
||||
filterDefinitionUsedInDropdownState,
|
||||
setObjectFilterDropdownSelectedRecordIds,
|
||||
setObjectFilterDropdownSelectedOptionValues,
|
||||
isObjectFilterDropdownUnfolded,
|
||||
} = useFilterDropdown({ filterDropdownId });
|
||||
|
||||
const filterDefinitionUsedInDropdown = useRecoilValue(
|
||||
filterDefinitionUsedInDropdownState,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDefined(availableFilterDefinitions)) {
|
||||
setAvailableFilterDefinitions(availableFilterDefinitions);
|
||||
}
|
||||
|
||||
if (isDefined(onFilterSelect)) {
|
||||
setOnFilterSelect(() => onFilterSelect);
|
||||
}
|
||||
setOnFilterSelect(() => (filter: Filter | null) => {
|
||||
if (isDefined(filter)) {
|
||||
upsertCombinedViewFilter(filter);
|
||||
}
|
||||
});
|
||||
}, [
|
||||
availableFilterDefinitions,
|
||||
onFilterSelect,
|
||||
setAvailableFilterDefinitions,
|
||||
setOnFilterSelect,
|
||||
upsertCombinedViewFilter,
|
||||
]);
|
||||
|
||||
const currentViewFilters = useRecoilValue(currentViewFiltersState);
|
||||
const unsavedToUpsertViewFilters = useRecoilValue(
|
||||
unsavedToUpsertViewFiltersState,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (filterDefinitionUsedInDropdown?.type === 'RELATION') {
|
||||
const viewFilterUsedInDropdown = currentViewFilters.find(
|
||||
const viewFilterUsedInDropdown = unsavedToUpsertViewFilters.find(
|
||||
(filter) =>
|
||||
filter.fieldMetadataId ===
|
||||
filterDefinitionUsedInDropdown.fieldMetadataId,
|
||||
@ -64,7 +71,7 @@ export const ViewBarFilterEffect = ({
|
||||
|
||||
setObjectFilterDropdownSelectedRecordIds(viewFilterSelectedRecordIds);
|
||||
} else if (filterDefinitionUsedInDropdown?.type === 'SELECT') {
|
||||
const viewFilterUsedInDropdown = currentViewFilters.find(
|
||||
const viewFilterUsedInDropdown = unsavedToUpsertViewFilters.find(
|
||||
(filter) =>
|
||||
filter.fieldMetadataId ===
|
||||
filterDefinitionUsedInDropdown.fieldMetadataId,
|
||||
@ -82,10 +89,9 @@ export const ViewBarFilterEffect = ({
|
||||
}
|
||||
}, [
|
||||
filterDefinitionUsedInDropdown,
|
||||
currentViewFilters,
|
||||
setObjectFilterDropdownSelectedRecordIds,
|
||||
isObjectFilterDropdownUnfolded,
|
||||
setObjectFilterDropdownSelectedOptionValues,
|
||||
unsavedToUpsertViewFilters,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
|
||||
@ -1,42 +1,52 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useSortDropdown';
|
||||
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { useCombinedViewSorts } from '@/views/hooks/useCombinedViewSorts';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
type ViewBarSortEffectProps = {
|
||||
sortDropdownId: string;
|
||||
onSortSelect?: ((sort: Sort) => void) | undefined;
|
||||
};
|
||||
|
||||
export const ViewBarSortEffect = ({
|
||||
sortDropdownId,
|
||||
onSortSelect,
|
||||
}: ViewBarSortEffectProps) => {
|
||||
const { availableSortDefinitionsState } = useViewScopedStates();
|
||||
const { availableSortDefinitionsState } = useViewStates();
|
||||
const { upsertCombinedViewSort } = useCombinedViewSorts();
|
||||
|
||||
const availableSortDefinitions = useRecoilValue(
|
||||
availableSortDefinitionsState,
|
||||
);
|
||||
|
||||
const { setAvailableSortDefinitions, setOnSortSelect } = useSortDropdown({
|
||||
const {
|
||||
availableSortDefinitionsState: availableSortDefinitionsInSortDropdownState,
|
||||
onSortSelectState,
|
||||
} = useSortDropdown({
|
||||
sortDropdownId,
|
||||
});
|
||||
|
||||
const setAvailableSortDefinitionsInSortDropdown = useSetRecoilState(
|
||||
availableSortDefinitionsInSortDropdownState,
|
||||
);
|
||||
const setOnSortSelect = useSetRecoilState(onSortSelectState);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDefined(availableSortDefinitions)) {
|
||||
setAvailableSortDefinitions(availableSortDefinitions);
|
||||
}
|
||||
if (isDefined(onSortSelect)) {
|
||||
setOnSortSelect(() => onSortSelect);
|
||||
setAvailableSortDefinitionsInSortDropdown(availableSortDefinitions);
|
||||
}
|
||||
setOnSortSelect(() => (sort: Sort | null) => {
|
||||
if (isDefined(sort)) {
|
||||
upsertCombinedViewSort(sort);
|
||||
}
|
||||
});
|
||||
}, [
|
||||
availableSortDefinitions,
|
||||
onSortSelect,
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableSortDefinitionsInSortDropdown,
|
||||
setOnSortSelect,
|
||||
upsertCombinedViewSort,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { MouseEvent } from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import {
|
||||
IconChevronDown,
|
||||
@ -20,10 +20,11 @@ import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { MOBILE_VIEWPORT } from '@/ui/theme/constants/MobileViewport';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { VIEWS_DROPDOWN_ID } from '@/views/constants/ViewsDropdownId';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||
import { useHandleViews } from '@/views/hooks/useHandleViews';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
import { useViewScopedStates } from '../hooks/internal/useViewScopedStates';
|
||||
import { useViewStates } from '../hooks/internal/useViewStates';
|
||||
|
||||
const StyledBoldDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
@ -65,18 +66,18 @@ export const ViewsDropdownButton = ({
|
||||
optionsDropdownScopeId,
|
||||
}: ViewsDropdownButtonProps) => {
|
||||
const theme = useTheme();
|
||||
const { removeView, changeViewInUrl } = useViewBar();
|
||||
|
||||
const { viewsState, currentViewSelector, entityCountInCurrentViewState } =
|
||||
useViewScopedStates();
|
||||
const { removeView, selectView } = useHandleViews();
|
||||
const { entityCountInCurrentViewState, viewEditModeState } = useViewStates();
|
||||
|
||||
const { currentViewWithCombinedFiltersAndSorts, viewsOnCurrentObject } =
|
||||
useGetCurrentView();
|
||||
|
||||
const views = useRecoilValue(viewsState);
|
||||
const currentView = useRecoilValue(currentViewSelector);
|
||||
const entityCountInCurrentView = useRecoilValue(
|
||||
entityCountInCurrentViewState,
|
||||
);
|
||||
|
||||
const { setViewEditMode, setCurrentViewId, loadView } = useViewBar();
|
||||
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
||||
|
||||
const {
|
||||
isDropdownOpen: isViewsDropdownOpen,
|
||||
@ -87,14 +88,10 @@ export const ViewsDropdownButton = ({
|
||||
optionsDropdownScopeId,
|
||||
);
|
||||
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
() => async (viewId: string) => {
|
||||
changeViewInUrl(viewId);
|
||||
loadView(viewId);
|
||||
closeViewsDropdown();
|
||||
},
|
||||
[changeViewInUrl, closeViewsDropdown, loadView],
|
||||
);
|
||||
const handleViewSelect = (viewId: string) => {
|
||||
selectView(viewId);
|
||||
closeViewsDropdown();
|
||||
};
|
||||
|
||||
const handleAddViewButtonClick = () => {
|
||||
setViewEditMode('create');
|
||||
@ -108,8 +105,7 @@ export const ViewsDropdownButton = ({
|
||||
viewId: string,
|
||||
) => {
|
||||
event.stopPropagation();
|
||||
changeViewInUrl(viewId);
|
||||
setCurrentViewId(viewId);
|
||||
selectView(viewId);
|
||||
setViewEditMode('edit');
|
||||
onViewEditModeChange?.();
|
||||
closeViewsDropdown();
|
||||
@ -123,11 +119,12 @@ export const ViewsDropdownButton = ({
|
||||
event.stopPropagation();
|
||||
|
||||
await removeView(viewId);
|
||||
selectView(viewsOnCurrentObject.filter((view) => view.id !== viewId)[0].id);
|
||||
closeViewsDropdown();
|
||||
};
|
||||
|
||||
const { getIcon } = useIcons();
|
||||
const CurrentViewIcon = getIcon(currentView?.icon);
|
||||
const CurrentViewIcon = getIcon(currentViewWithCombinedFiltersAndSorts?.icon);
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
@ -135,12 +132,14 @@ export const ViewsDropdownButton = ({
|
||||
dropdownHotkeyScope={hotkeyScope}
|
||||
clickableComponent={
|
||||
<StyledDropdownButtonContainer isUnfolded={isViewsDropdownOpen}>
|
||||
{currentView && CurrentViewIcon ? (
|
||||
{currentViewWithCombinedFiltersAndSorts && CurrentViewIcon ? (
|
||||
<CurrentViewIcon size={theme.icon.size.md} />
|
||||
) : (
|
||||
<IconList size={theme.icon.size.md} />
|
||||
)}
|
||||
<StyledViewName>{currentView?.name ?? 'All'}</StyledViewName>
|
||||
<StyledViewName>
|
||||
{currentViewWithCombinedFiltersAndSorts?.name ?? 'All'}
|
||||
</StyledViewName>
|
||||
<StyledDropdownLabelAdornments>
|
||||
· {entityCountInCurrentView}{' '}
|
||||
<IconChevronDown size={theme.icon.size.sm} />
|
||||
@ -150,16 +149,18 @@ export const ViewsDropdownButton = ({
|
||||
dropdownComponents={
|
||||
<>
|
||||
<DropdownMenuItemsContainer>
|
||||
{views.map((view) => (
|
||||
{viewsOnCurrentObject.map((view) => (
|
||||
<MenuItem
|
||||
key={view.id}
|
||||
iconButtons={[
|
||||
{
|
||||
Icon: IconPencil,
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||
handleEditViewButtonClick(event, view.id),
|
||||
},
|
||||
views.length > 1
|
||||
currentViewWithCombinedFiltersAndSorts?.id === view.id
|
||||
? {
|
||||
Icon: IconPencil,
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||
handleEditViewButtonClick(event, view.id),
|
||||
}
|
||||
: null,
|
||||
viewsOnCurrentObject.length > 1
|
||||
? {
|
||||
Icon: IconTrash,
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) =>
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
// TODO: find a better pattern than using '' as a fallback
|
||||
export const UNDEFINED_FAMILY_ITEM_ID = '';
|
||||
@ -1,27 +1,11 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { MemoryRouter, useSearchParams } from 'react-router-dom';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { generateDeleteOneRecordMutation } from '@/object-record/utils/generateDeleteOneRecordMutation';
|
||||
import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated';
|
||||
import {
|
||||
filterDefinition,
|
||||
viewFilter,
|
||||
} from '@/views/hooks/__tests__/useViewBar_ViewFilters.test';
|
||||
import {
|
||||
sortDefinition,
|
||||
viewSort,
|
||||
} from '@/views/hooks/__tests__/useViewBar_ViewSorts.test';
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||
import { entityCountInCurrentViewScopedState } from '@/views/states/entityCountInCurrentViewScopedState';
|
||||
import { viewEditModeScopedState } from '@/views/states/viewEditModeScopedState';
|
||||
import { viewObjectMetadataIdScopeState } from '@/views/states/viewObjectMetadataIdScopeState';
|
||||
|
||||
const mockedUuid = 'mocked-uuid';
|
||||
jest.mock('uuid');
|
||||
@ -49,225 +33,19 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
>
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<RecoilRoot>
|
||||
<ViewScope viewScopeId="viewScopeId">{children}</ViewScope>
|
||||
<ViewScope viewScopeId="viewScopeId" onCurrentViewChange={() => {}}>
|
||||
{children}
|
||||
</ViewScope>
|
||||
</RecoilRoot>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const renderHookConfig = {
|
||||
const _renderHookConfig = {
|
||||
wrapper: Wrapper,
|
||||
};
|
||||
|
||||
const viewBarId = 'viewBarTestId';
|
||||
const _viewBarId = 'viewBarTestId';
|
||||
|
||||
describe('useViewBar', () => {
|
||||
it('should set and get current view Id', () => {
|
||||
const { result } = renderHook(
|
||||
() => useViewBar({ viewBarId }),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
expect(result.current.scopeId).toBe(viewBarId);
|
||||
expect(result.current.currentViewId).toBeUndefined();
|
||||
|
||||
act(() => {
|
||||
result.current.setCurrentViewId('testId');
|
||||
});
|
||||
|
||||
expect(result.current.currentViewId).toBe('testId');
|
||||
});
|
||||
|
||||
it('should create view and update url params', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const viewBar = useViewBar({ viewBarId });
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
return {
|
||||
viewBar,
|
||||
searchParams,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
await act(async () => {
|
||||
await result.current.viewBar.createView('Test View');
|
||||
});
|
||||
expect(result.current.searchParams[0].get('view')).toBe(mockedUuid);
|
||||
});
|
||||
|
||||
it('should delete current view and remove id from params', async () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
viewBar: useViewBar({ viewBarId }),
|
||||
searchParams: useSearchParams(),
|
||||
}),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.viewBar.createView('Test View');
|
||||
result.current.viewBar.setCurrentViewId(mockedUuid);
|
||||
});
|
||||
expect(result.current.searchParams[0].get('view')).toBe(mockedUuid);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.viewBar.removeView(mockedUuid);
|
||||
});
|
||||
expect(result.current.searchParams[0].get('view')).toBeNull();
|
||||
|
||||
const addBookMutationMock = mocks[0].result;
|
||||
await waitFor(() => expect(addBookMutationMock).toHaveBeenCalled());
|
||||
});
|
||||
|
||||
it('should resetViewBar', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const viewBar = useViewBar({ viewBarId });
|
||||
const {
|
||||
currentViewFiltersState,
|
||||
currentViewSortsState,
|
||||
viewEditModeState,
|
||||
} = useViewScopedStates({
|
||||
viewScopeId: viewBarId,
|
||||
});
|
||||
const currentViewFilters = useRecoilValue(currentViewFiltersState);
|
||||
const currentViewSorts = useRecoilValue(currentViewSortsState);
|
||||
const viewEditMode = useRecoilValue(viewEditModeState);
|
||||
|
||||
return {
|
||||
viewBar,
|
||||
currentViewFilters,
|
||||
currentViewSorts,
|
||||
viewEditMode,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
act(() => {
|
||||
result.current.viewBar.resetViewBar();
|
||||
});
|
||||
|
||||
expect(result.current.currentViewFilters).toStrictEqual([]);
|
||||
expect(result.current.currentViewSorts).toStrictEqual([]);
|
||||
expect(result.current.viewEditMode).toBe('none');
|
||||
});
|
||||
|
||||
it('should handleViewNameSubmit', async () => {
|
||||
const { result } = renderHook(
|
||||
() => useViewBar({ viewBarId }),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.handleViewNameSubmit('New View Name');
|
||||
});
|
||||
});
|
||||
|
||||
it('should update edit mode', async () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
viewBar: useViewBar({ viewBarId }),
|
||||
editMode: useRecoilState(
|
||||
getScopedStateDeprecated(viewEditModeScopedState, viewBarId),
|
||||
)[0],
|
||||
}),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
expect(result.current.editMode).toBe('none');
|
||||
await act(async () => {
|
||||
result.current.viewBar.setViewEditMode('create');
|
||||
});
|
||||
|
||||
expect(result.current.editMode).toBe('create');
|
||||
await act(async () => {
|
||||
result.current.viewBar.setViewEditMode('edit');
|
||||
});
|
||||
|
||||
expect(result.current.editMode).toBe('edit');
|
||||
});
|
||||
|
||||
it('should update url param', async () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
viewBar: useViewBar({ viewBarId }),
|
||||
searchParams: useSearchParams(),
|
||||
}),
|
||||
renderHookConfig,
|
||||
);
|
||||
expect(result.current.searchParams[0].get('view')).toBeNull();
|
||||
await act(async () => {
|
||||
result.current.viewBar.changeViewInUrl('view1');
|
||||
});
|
||||
expect(result.current.searchParams[0].get('view')).toBe('view1');
|
||||
});
|
||||
|
||||
it('should update object metadata id', async () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
viewBar: useViewBar({ viewBarId }),
|
||||
metadataId: useRecoilState(
|
||||
getScopedStateDeprecated(viewObjectMetadataIdScopeState, viewBarId),
|
||||
)[0],
|
||||
}),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
expect(result.current.metadataId).toBeUndefined();
|
||||
await act(async () => {
|
||||
result.current.viewBar.setViewObjectMetadataId('newId');
|
||||
});
|
||||
|
||||
expect(result.current.metadataId).toBe('newId');
|
||||
});
|
||||
|
||||
it('should update count in current view', async () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
viewBar: useViewBar({ viewBarId }),
|
||||
count: useRecoilState(
|
||||
getScopedStateDeprecated(
|
||||
entityCountInCurrentViewScopedState,
|
||||
viewBarId,
|
||||
),
|
||||
)[0],
|
||||
}),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
expect(result.current.count).toBe(0);
|
||||
await act(async () => {
|
||||
result.current.viewBar.setEntityCountInCurrentView(1);
|
||||
});
|
||||
|
||||
expect(result.current.count).toBe(1);
|
||||
});
|
||||
|
||||
it('should loadView', async () => {
|
||||
const { result } = renderHook(
|
||||
() => useViewBar({ viewBarId }),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.loadView(mockedUuid);
|
||||
});
|
||||
});
|
||||
|
||||
it('should updateCurrentView', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const viewBar = useViewBar({ viewBarId });
|
||||
viewBar.setCurrentViewId(mockedUuid);
|
||||
|
||||
viewBar.setAvailableSortDefinitions([sortDefinition]);
|
||||
|
||||
viewBar.loadViewSorts([viewSort], mockedUuid);
|
||||
|
||||
viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
||||
|
||||
viewBar.loadViewFilters([viewFilter], mockedUuid);
|
||||
|
||||
return { viewBar };
|
||||
}, renderHookConfig);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.viewBar.updateCurrentView();
|
||||
});
|
||||
});
|
||||
it('should set and get current view Id', () => {});
|
||||
});
|
||||
|
||||
@ -1,170 +0,0 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { gql } from '@apollo/client';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { getScopedFamilyStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedFamilyStateDeprecated';
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||
import { currentViewFieldsScopedFamilyState } from '@/views/states/currentViewFieldsScopedFamilyState';
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
|
||||
const fieldMetadataId = '12ecdf87-506f-44a7-98c6-393e5f05b225';
|
||||
|
||||
const fieldDefinition: ColumnDefinition<FieldMetadata> = {
|
||||
size: 1,
|
||||
position: 1,
|
||||
fieldMetadataId,
|
||||
label: 'label',
|
||||
iconName: 'icon',
|
||||
type: 'TEXT',
|
||||
metadata: {
|
||||
placeHolder: 'placeHolder',
|
||||
fieldName: 'fieldName',
|
||||
},
|
||||
};
|
||||
const viewField: ViewField = {
|
||||
id: '88930a16-685f-493b-a96b-91ca55666bba',
|
||||
fieldMetadataId,
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
size: 1,
|
||||
definition: fieldDefinition,
|
||||
};
|
||||
|
||||
const viewBarId = 'viewBarTestId';
|
||||
|
||||
const currentViewId = '23f5dceb-3482-4e3a-9bb4-2f52f2556be9';
|
||||
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: gql`
|
||||
mutation CreateOneViewField($input: ViewFieldCreateInput!) {
|
||||
createViewField(data: $input) {
|
||||
__typename
|
||||
position
|
||||
isVisible
|
||||
fieldMetadataId
|
||||
viewId
|
||||
id
|
||||
size
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
input: {
|
||||
fieldMetadataId,
|
||||
viewId: currentViewId,
|
||||
isVisible: true,
|
||||
size: 1,
|
||||
position: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
result: jest.fn(() => ({
|
||||
data: { createViewField: { id: '' } },
|
||||
})),
|
||||
},
|
||||
];
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MemoryRouter
|
||||
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
||||
initialIndex={1}
|
||||
>
|
||||
<MockedProvider mocks={mocks} addTypename={false}>
|
||||
<RecoilRoot>
|
||||
<ViewScope viewScopeId="viewScopeId">{children}</ViewScope>
|
||||
</RecoilRoot>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const renderHookConfig = {
|
||||
wrapper: Wrapper,
|
||||
};
|
||||
|
||||
describe('useViewBar > viewFields', () => {
|
||||
it('should update current fields', async () => {
|
||||
const { result } = renderHook(
|
||||
() => ({
|
||||
viewBar: useViewBar({ viewBarId }),
|
||||
currentFields: useRecoilState(
|
||||
getScopedFamilyStateDeprecated(
|
||||
currentViewFieldsScopedFamilyState,
|
||||
viewBarId,
|
||||
currentViewId,
|
||||
),
|
||||
)[0],
|
||||
}),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
expect(result.current.currentFields).toStrictEqual([]);
|
||||
await act(async () => {
|
||||
result.current.viewBar.setCurrentViewId(currentViewId);
|
||||
result.current.viewBar.setViewObjectMetadataId('newId');
|
||||
result.current.viewBar.persistViewFields([viewField]);
|
||||
});
|
||||
|
||||
await waitFor(() =>
|
||||
expect(result.current.currentFields).toEqual([viewField]),
|
||||
);
|
||||
});
|
||||
|
||||
it('should persist view fields', async () => {
|
||||
const { result } = renderHook(
|
||||
() => useViewBar({ viewBarId }),
|
||||
renderHookConfig,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
result.current.setCurrentViewId(currentViewId);
|
||||
result.current.setViewObjectMetadataId('newId');
|
||||
await result.current.persistViewFields([viewField]);
|
||||
});
|
||||
|
||||
const persistViewFieldsMutation = mocks[0];
|
||||
|
||||
await waitFor(() =>
|
||||
expect(persistViewFieldsMutation.result).toHaveBeenCalled(),
|
||||
);
|
||||
});
|
||||
|
||||
it('should load view fields', async () => {
|
||||
const currentViewId = 'ac8807fd-0065-436d-bdf6-94333d75af6e';
|
||||
|
||||
const { result } = renderHook(() => {
|
||||
const viewBar = useViewBar({ viewBarId });
|
||||
|
||||
const { currentViewFieldsState } = useViewScopedStates({
|
||||
viewScopeId: viewBarId,
|
||||
});
|
||||
const currentViewFields = useRecoilValue(currentViewFieldsState);
|
||||
|
||||
return {
|
||||
viewBar,
|
||||
currentViewFields,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
expect(result.current.currentViewFields).toStrictEqual([]);
|
||||
|
||||
await act(async () => {
|
||||
result.current.viewBar.setAvailableFieldDefinitions([fieldDefinition]);
|
||||
|
||||
await result.current.viewBar.loadViewFields([viewField], currentViewId);
|
||||
result.current.viewBar.setCurrentViewId(currentViewId);
|
||||
});
|
||||
|
||||
expect(result.current.currentViewFields).toStrictEqual([viewField]);
|
||||
});
|
||||
});
|
||||
@ -1,178 +0,0 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MemoryRouter
|
||||
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
||||
initialIndex={1}
|
||||
>
|
||||
<MockedProvider addTypename={false}>
|
||||
<RecoilRoot>
|
||||
<ViewScope viewScopeId="viewScopeId">{children}</ViewScope>
|
||||
</RecoilRoot>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const renderHookConfig = {
|
||||
wrapper: Wrapper,
|
||||
};
|
||||
|
||||
const viewBarId = 'viewBarTestId';
|
||||
|
||||
export const filterDefinition: FilterDefinition = {
|
||||
fieldMetadataId: '113ea8f8-1908-4c9c-9984-3f23c96b92f5',
|
||||
label: 'label',
|
||||
iconName: 'iconName',
|
||||
type: 'TEXT',
|
||||
};
|
||||
|
||||
export const viewFilter: ViewFilter = {
|
||||
id: 'id',
|
||||
fieldMetadataId: '113ea8f8-1908-4c9c-9984-3f23c96b92f5',
|
||||
operand: ViewFilterOperand.Is,
|
||||
value: 'value',
|
||||
displayValue: 'displayValue',
|
||||
definition: filterDefinition,
|
||||
};
|
||||
|
||||
const currentViewId = '23f5dceb-3482-4e3a-9bb4-2f52f2556be9';
|
||||
|
||||
describe('useViewBar > viewFilters', () => {
|
||||
it('should load view filters', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const viewBar = useViewBar({ viewBarId });
|
||||
|
||||
const { currentViewFiltersState } = useViewScopedStates({
|
||||
viewScopeId: viewBarId,
|
||||
});
|
||||
const currentViewFilters = useRecoilValue(currentViewFiltersState);
|
||||
|
||||
return {
|
||||
viewBar,
|
||||
currentViewFilters,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
expect(result.current.currentViewFilters).toStrictEqual([]);
|
||||
|
||||
await act(async () => {
|
||||
result.current.viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
||||
|
||||
await result.current.viewBar.loadViewFilters([viewFilter], currentViewId);
|
||||
result.current.viewBar.setCurrentViewId(currentViewId);
|
||||
});
|
||||
|
||||
expect(result.current.currentViewFilters).toStrictEqual([viewFilter]);
|
||||
});
|
||||
|
||||
it('should upsertViewFilter', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const viewBar = useViewBar({ viewBarId });
|
||||
|
||||
viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
||||
|
||||
viewBar.loadViewFilters([viewFilter], currentViewId);
|
||||
viewBar.setCurrentViewId(currentViewId);
|
||||
|
||||
const { currentViewFiltersState } = useViewScopedStates({
|
||||
viewScopeId: viewBarId,
|
||||
});
|
||||
const currentViewFilters = useRecoilValue(currentViewFiltersState);
|
||||
|
||||
return {
|
||||
viewBar,
|
||||
currentViewFilters,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
expect(result.current.currentViewFilters).toStrictEqual([viewFilter]);
|
||||
|
||||
const newFilters: Filter[] = [
|
||||
{
|
||||
fieldMetadataId: '113ea8f8-1908-4c9c-9984-3f23c96b92f5',
|
||||
value: 'value',
|
||||
displayValue: 'displayValue',
|
||||
operand: ViewFilterOperand.IsNot,
|
||||
definition: {
|
||||
fieldMetadataId: 'id',
|
||||
label: 'label',
|
||||
iconName: 'icon',
|
||||
type: 'TEXT',
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldMetadataId: 'd9487757-183e-4fa0-a554-a980850cb23d',
|
||||
value: 'value',
|
||||
displayValue: 'displayValue',
|
||||
operand: ViewFilterOperand.Contains,
|
||||
definition: {
|
||||
fieldMetadataId: 'id',
|
||||
label: 'label',
|
||||
iconName: 'icon',
|
||||
type: 'TEXT',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// upsert an existing filter
|
||||
act(() => {
|
||||
result.current.viewBar.upsertViewFilter(newFilters[0]);
|
||||
});
|
||||
|
||||
expect(result.current.currentViewFilters).toStrictEqual([
|
||||
{ ...newFilters[0], id: viewFilter.id },
|
||||
]);
|
||||
|
||||
// upsert a new filter
|
||||
act(() => {
|
||||
result.current.viewBar.upsertViewFilter(newFilters[1]);
|
||||
});
|
||||
|
||||
// expect currentViewFilters to contain both filters
|
||||
expect(result.current.currentViewFilters).toStrictEqual([
|
||||
{ ...newFilters[0], id: viewFilter.id },
|
||||
{ ...newFilters[1], id: undefined },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should remove view filter', () => {
|
||||
const { result } = renderHook(() => {
|
||||
const viewBar = useViewBar({ viewBarId });
|
||||
|
||||
viewBar.setAvailableFilterDefinitions([filterDefinition]);
|
||||
|
||||
viewBar.loadViewFilters([viewFilter], currentViewId);
|
||||
viewBar.setCurrentViewId(currentViewId);
|
||||
|
||||
const { currentViewFiltersState } = useViewScopedStates({
|
||||
viewScopeId: viewBarId,
|
||||
});
|
||||
const currentViewFilters = useRecoilValue(currentViewFiltersState);
|
||||
|
||||
return {
|
||||
viewBar,
|
||||
currentViewFilters,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
expect(result.current.currentViewFilters).toStrictEqual([viewFilter]);
|
||||
|
||||
// remove an existing filter
|
||||
act(() => {
|
||||
result.current.viewBar.removeViewFilter(filterDefinition.fieldMetadataId);
|
||||
});
|
||||
|
||||
expect(result.current.currentViewFilters).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
@ -1,165 +0,0 @@
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { RecoilRoot, useRecoilValue } from 'recoil';
|
||||
|
||||
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
|
||||
import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition';
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
|
||||
const Wrapper = ({ children }: { children: React.ReactNode }) => (
|
||||
<MemoryRouter
|
||||
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
||||
initialIndex={1}
|
||||
>
|
||||
<MockedProvider addTypename={false}>
|
||||
<RecoilRoot>
|
||||
<ViewScope viewScopeId="viewScopeId">{children}</ViewScope>
|
||||
</RecoilRoot>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
const renderHookConfig = {
|
||||
wrapper: Wrapper,
|
||||
};
|
||||
|
||||
const viewBarId = 'viewBarTestId';
|
||||
|
||||
export const sortDefinition: SortDefinition = {
|
||||
fieldMetadataId: '12ecdf87-506f-44a7-98c6-393e5f05b225',
|
||||
label: 'label',
|
||||
iconName: 'icon',
|
||||
};
|
||||
|
||||
export const viewSort: ViewSort = {
|
||||
id: '88930a16-685f-493b-a96b-91ca55666bba',
|
||||
fieldMetadataId: '12ecdf87-506f-44a7-98c6-393e5f05b225',
|
||||
direction: 'asc',
|
||||
definition: sortDefinition,
|
||||
};
|
||||
|
||||
describe('View Sorts', () => {
|
||||
const currentViewId = 'ac8807fd-0065-436d-bdf6-94333d75af6e';
|
||||
|
||||
it('should load view sorts', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const viewBar = useViewBar({ viewBarId });
|
||||
|
||||
const { currentViewSortsState } = useViewScopedStates({
|
||||
viewScopeId: viewBarId,
|
||||
});
|
||||
const currentViewSorts = useRecoilValue(currentViewSortsState);
|
||||
|
||||
return {
|
||||
viewBar,
|
||||
currentViewSorts,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
expect(result.current.currentViewSorts).toStrictEqual([]);
|
||||
|
||||
await act(async () => {
|
||||
result.current.viewBar.setAvailableSortDefinitions([sortDefinition]);
|
||||
|
||||
await result.current.viewBar.loadViewSorts([viewSort], currentViewId);
|
||||
result.current.viewBar.setCurrentViewId(currentViewId);
|
||||
});
|
||||
|
||||
expect(result.current.currentViewSorts).toStrictEqual([viewSort]);
|
||||
});
|
||||
|
||||
it('should upsertViewSort', async () => {
|
||||
const { result } = renderHook(() => {
|
||||
const viewBar = useViewBar({ viewBarId });
|
||||
|
||||
viewBar.setAvailableSortDefinitions([sortDefinition]);
|
||||
|
||||
viewBar.loadViewSorts([viewSort], currentViewId);
|
||||
viewBar.setCurrentViewId(currentViewId);
|
||||
|
||||
const { currentViewSortsState } = useViewScopedStates({
|
||||
viewScopeId: viewBarId,
|
||||
});
|
||||
const currentViewSorts = useRecoilValue(currentViewSortsState);
|
||||
|
||||
return {
|
||||
viewBar,
|
||||
currentViewSorts,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
expect(result.current.currentViewSorts).toStrictEqual([viewSort]);
|
||||
|
||||
const newSortFieldMetadataId = 'd9487757-183e-4fa0-a554-a980850cb23d';
|
||||
|
||||
const newSorts: Sort[] = [
|
||||
{
|
||||
fieldMetadataId: viewSort.fieldMetadataId,
|
||||
direction: 'desc',
|
||||
definition: sortDefinition,
|
||||
},
|
||||
{
|
||||
fieldMetadataId: newSortFieldMetadataId,
|
||||
direction: 'asc',
|
||||
definition: {
|
||||
...sortDefinition,
|
||||
fieldMetadataId: newSortFieldMetadataId,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// upsert an existing sort
|
||||
act(() => {
|
||||
result.current.viewBar.upsertViewSort(newSorts[0]);
|
||||
});
|
||||
|
||||
expect(result.current.currentViewSorts).toStrictEqual([
|
||||
{ ...newSorts[0], id: viewSort.id },
|
||||
]);
|
||||
|
||||
// upsert a new sort
|
||||
act(() => {
|
||||
result.current.viewBar.upsertViewSort(newSorts[1]);
|
||||
});
|
||||
|
||||
// expect currentViewSorts to contain both sorts
|
||||
expect(result.current.currentViewSorts).toStrictEqual([
|
||||
{ ...newSorts[0], id: viewSort.id },
|
||||
{ ...newSorts[1], id: undefined },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should remove view sort', () => {
|
||||
const { result } = renderHook(() => {
|
||||
const viewBar = useViewBar({ viewBarId });
|
||||
|
||||
viewBar.setAvailableSortDefinitions([sortDefinition]);
|
||||
|
||||
viewBar.loadViewSorts([viewSort], currentViewId);
|
||||
viewBar.setCurrentViewId(currentViewId);
|
||||
|
||||
const { currentViewSortsState } = useViewScopedStates({
|
||||
viewScopeId: viewBarId,
|
||||
});
|
||||
const currentViewSorts = useRecoilValue(currentViewSortsState);
|
||||
|
||||
return {
|
||||
viewBar,
|
||||
currentViewSorts,
|
||||
};
|
||||
}, renderHookConfig);
|
||||
|
||||
expect(result.current.currentViewSorts).toStrictEqual([viewSort]);
|
||||
|
||||
// remove an existing sort
|
||||
act(() => {
|
||||
result.current.viewBar.removeViewSort(sortDefinition.fieldMetadataId);
|
||||
});
|
||||
|
||||
expect(result.current.currentViewSorts).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,115 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
|
||||
export const usePersistViewFieldRecords = () => {
|
||||
const {
|
||||
updateOneRecordMutation,
|
||||
createOneRecordMutation,
|
||||
getRecordFromCache,
|
||||
objectMetadataItem,
|
||||
} = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.ViewField,
|
||||
});
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const createViewFieldRecords = useCallback(
|
||||
(viewFieldsToCreate: ViewField[], view: GraphQLView) => {
|
||||
if (!viewFieldsToCreate.length) return;
|
||||
return Promise.all(
|
||||
viewFieldsToCreate.map((viewField) =>
|
||||
apolloClient.mutate({
|
||||
mutation: createOneRecordMutation,
|
||||
variables: {
|
||||
input: {
|
||||
fieldMetadataId: viewField.fieldMetadataId,
|
||||
viewId: view.id,
|
||||
isVisible: viewField.isVisible,
|
||||
position: viewField.position,
|
||||
size: viewField.size,
|
||||
id: v4(),
|
||||
},
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['createViewField'];
|
||||
if (!record) return;
|
||||
|
||||
triggerCreateRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToCreate: [record],
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
createOneRecordMutation,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
],
|
||||
);
|
||||
|
||||
const updateViewFieldRecords = useCallback(
|
||||
(viewFieldsToUpdate: ViewField[]) => {
|
||||
if (!viewFieldsToUpdate.length) return;
|
||||
return Promise.all(
|
||||
viewFieldsToUpdate.map((viewField) =>
|
||||
apolloClient.mutate({
|
||||
mutation: updateOneRecordMutation,
|
||||
variables: {
|
||||
idToUpdate: viewField.id,
|
||||
input: {
|
||||
isVisible: viewField.isVisible,
|
||||
position: viewField.position,
|
||||
size: viewField.size,
|
||||
},
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['updateViewField'];
|
||||
if (!record) return;
|
||||
const cachedRecord = getRecordFromCache<ObjectRecord>(record.id);
|
||||
|
||||
if (!cachedRecord) return;
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
currentRecord: cachedRecord,
|
||||
updatedRecord: record,
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
getRecordFromCache,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
updateOneRecordMutation,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
createViewFieldRecords,
|
||||
updateViewFieldRecords,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,156 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
||||
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
|
||||
export const usePersistViewFilterRecords = () => {
|
||||
const {
|
||||
updateOneRecordMutation,
|
||||
createOneRecordMutation,
|
||||
deleteOneRecordMutation,
|
||||
objectMetadataItem,
|
||||
getRecordFromCache,
|
||||
} = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.ViewFilter,
|
||||
});
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const createViewFilterRecords = useCallback(
|
||||
(viewFiltersToCreate: ViewFilter[], view: GraphQLView) => {
|
||||
if (!viewFiltersToCreate.length) return;
|
||||
|
||||
return Promise.all(
|
||||
viewFiltersToCreate.map((viewFilter) =>
|
||||
apolloClient.mutate({
|
||||
mutation: createOneRecordMutation,
|
||||
variables: {
|
||||
input: {
|
||||
fieldMetadataId: viewFilter.fieldMetadataId,
|
||||
viewId: view.id,
|
||||
value: viewFilter.value,
|
||||
displayValue: viewFilter.displayValue,
|
||||
operand: viewFilter.operand,
|
||||
},
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['createViewFilter'];
|
||||
if (!record) return;
|
||||
|
||||
triggerCreateRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToCreate: [record],
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
createOneRecordMutation,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
],
|
||||
);
|
||||
|
||||
const updateViewFilterRecords = useCallback(
|
||||
(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,
|
||||
},
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['updateViewFilter'];
|
||||
if (!record) return;
|
||||
const cachedRecord = getRecordFromCache<ObjectRecord>(record.id);
|
||||
|
||||
if (!cachedRecord) return;
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
currentRecord: cachedRecord,
|
||||
updatedRecord: record,
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
getRecordFromCache,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
updateOneRecordMutation,
|
||||
],
|
||||
);
|
||||
|
||||
const deleteViewFilterRecords = useCallback(
|
||||
(viewFilterIdsToDelete: string[]) => {
|
||||
if (!viewFilterIdsToDelete.length) return;
|
||||
return Promise.all(
|
||||
viewFilterIdsToDelete.map((viewFilterId) =>
|
||||
apolloClient.mutate({
|
||||
mutation: deleteOneRecordMutation,
|
||||
variables: {
|
||||
idToDelete: viewFilterId,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['deleteViewFilter'];
|
||||
|
||||
if (!record) return;
|
||||
|
||||
const cachedRecord = getRecordFromCache(record.id, cache);
|
||||
|
||||
if (!cachedRecord) return;
|
||||
|
||||
triggerDeleteRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToDelete: [cachedRecord],
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
deleteOneRecordMutation,
|
||||
getRecordFromCache,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
createViewFilterRecords,
|
||||
updateViewFilterRecords,
|
||||
deleteViewFilterRecords,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,151 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect';
|
||||
import { triggerDeleteRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDeleteRecordsOptimisticEffect';
|
||||
import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
|
||||
export const usePersistViewSortRecords = () => {
|
||||
const {
|
||||
updateOneRecordMutation,
|
||||
createOneRecordMutation,
|
||||
deleteOneRecordMutation,
|
||||
objectMetadataItem,
|
||||
getRecordFromCache,
|
||||
} = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.ViewSort,
|
||||
});
|
||||
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const createViewSortRecords = useCallback(
|
||||
(viewSortsToCreate: ViewSort[], view: GraphQLView) => {
|
||||
if (!viewSortsToCreate.length) return;
|
||||
return Promise.all(
|
||||
viewSortsToCreate.map((viewSort) =>
|
||||
apolloClient.mutate({
|
||||
mutation: createOneRecordMutation,
|
||||
variables: {
|
||||
input: {
|
||||
fieldMetadataId: viewSort.fieldMetadataId,
|
||||
viewId: view.id,
|
||||
direction: viewSort.direction,
|
||||
},
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['createViewSort'];
|
||||
if (!record) return;
|
||||
|
||||
triggerCreateRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToCreate: [record],
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
createOneRecordMutation,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
],
|
||||
);
|
||||
|
||||
const updateViewSortRecords = useCallback(
|
||||
(viewSortsToUpdate: ViewSort[]) => {
|
||||
if (!viewSortsToUpdate.length) return;
|
||||
return Promise.all(
|
||||
viewSortsToUpdate.map((viewSort) =>
|
||||
apolloClient.mutate({
|
||||
mutation: updateOneRecordMutation,
|
||||
variables: {
|
||||
idToUpdate: viewSort.id,
|
||||
input: {
|
||||
direction: viewSort.direction,
|
||||
},
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['updateViewSort'];
|
||||
if (!record) return;
|
||||
const cachedRecord = getRecordFromCache<ObjectRecord>(record.id);
|
||||
|
||||
if (!cachedRecord) return;
|
||||
|
||||
triggerUpdateRecordOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
currentRecord: cachedRecord,
|
||||
updatedRecord: record,
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
getRecordFromCache,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
updateOneRecordMutation,
|
||||
],
|
||||
);
|
||||
|
||||
const deleteViewSortRecords = useCallback(
|
||||
(viewSortIdsToDelete: string[]) => {
|
||||
if (!viewSortIdsToDelete.length) return;
|
||||
return Promise.all(
|
||||
viewSortIdsToDelete.map((viewSortId) =>
|
||||
apolloClient.mutate({
|
||||
mutation: deleteOneRecordMutation,
|
||||
variables: {
|
||||
idToDelete: viewSortId,
|
||||
},
|
||||
update: (cache, { data }) => {
|
||||
const record = data?.['deleteViewSort'];
|
||||
|
||||
if (!record) return;
|
||||
|
||||
const cachedRecord = getRecordFromCache(record.id, cache);
|
||||
|
||||
if (!cachedRecord) return;
|
||||
|
||||
triggerDeleteRecordsOptimisticEffect({
|
||||
cache,
|
||||
objectMetadataItem,
|
||||
recordsToDelete: [cachedRecord],
|
||||
objectMetadataItems,
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
[
|
||||
apolloClient,
|
||||
deleteOneRecordMutation,
|
||||
getRecordFromCache,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
createViewSortRecords,
|
||||
updateViewSortRecords,
|
||||
deleteViewSortRecords,
|
||||
};
|
||||
};
|
||||
@ -1,160 +0,0 @@
|
||||
import { Reference, useApolloClient } from '@apollo/client';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
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: CoreObjectNameSingular.ViewField,
|
||||
});
|
||||
|
||||
const { modifyRecordFromCache } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.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: (viewFieldsRef, { readField }) => {
|
||||
const edges = readField<{ node: Reference }[]>(
|
||||
'edges',
|
||||
viewFieldsRef,
|
||||
);
|
||||
|
||||
if (!edges) return viewFieldsRef;
|
||||
|
||||
return {
|
||||
...viewFieldsRef,
|
||||
edges: viewFieldsToPersist.map((viewField) => ({
|
||||
node: viewField,
|
||||
cursor: '',
|
||||
})),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
onViewFieldsChange?.(viewFieldsToPersist);
|
||||
},
|
||||
[
|
||||
viewScopeId,
|
||||
modifyRecordFromCache,
|
||||
apolloClient,
|
||||
createOneRecordMutation,
|
||||
updateOneRecordMutation,
|
||||
],
|
||||
);
|
||||
|
||||
return { persistViewFields };
|
||||
};
|
||||
@ -1,246 +0,0 @@
|
||||
import { Reference, useApolloClient } from '@apollo/client';
|
||||
import { produce } from 'immer';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
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: CoreObjectNameSingular.ViewFilter,
|
||||
});
|
||||
|
||||
const { modifyRecordFromCache } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.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 || !currentViewFilters || !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: (viewFiltersRef, { readField }) => {
|
||||
const edges = readField<{ node: Reference }[]>(
|
||||
'edges',
|
||||
viewFiltersRef,
|
||||
);
|
||||
|
||||
if (!edges) return viewFiltersRef;
|
||||
|
||||
return {
|
||||
...viewFiltersRef,
|
||||
edges: currentViewFilters.map((viewFilter) => ({
|
||||
node: viewFilter,
|
||||
cursor: '',
|
||||
})),
|
||||
};
|
||||
},
|
||||
});
|
||||
},
|
||||
[
|
||||
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;
|
||||
}
|
||||
|
||||
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 };
|
||||
};
|
||||
@ -19,17 +19,20 @@ import { isDefined } from '~/utils/isDefined';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
const filterQueryParamsSchema = z.object({
|
||||
filter: z.record(
|
||||
z.record(
|
||||
z.nativeEnum(ViewFilterOperand),
|
||||
z.string().or(z.array(z.string())),
|
||||
),
|
||||
),
|
||||
view: z.string().optional(),
|
||||
filter: z
|
||||
.record(
|
||||
z.record(
|
||||
z.nativeEnum(ViewFilterOperand),
|
||||
z.string().or(z.array(z.string())),
|
||||
),
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type FilterQueryParams = z.infer<typeof filterQueryParamsSchema>;
|
||||
|
||||
export const useFiltersFromQueryParams = () => {
|
||||
export const useViewFromQueryParams = () => {
|
||||
const apolloClient = useApolloClient();
|
||||
const [searchParams] = useSearchParams();
|
||||
const { objectNamePlural = '' } = useParams();
|
||||
@ -39,15 +42,21 @@ export const useFiltersFromQueryParams = () => {
|
||||
const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular });
|
||||
const generateFindManyRecordsQuery = useGenerateFindManyRecordsQuery();
|
||||
|
||||
const filterParamsValidation = filterQueryParamsSchema.safeParse(
|
||||
const queryParamsValidation = filterQueryParamsSchema.safeParse(
|
||||
qs.parse(searchParams.toString()),
|
||||
);
|
||||
|
||||
const filterQueryParams = useMemo(
|
||||
() =>
|
||||
filterParamsValidation.success ? filterParamsValidation.data.filter : {},
|
||||
[filterParamsValidation],
|
||||
queryParamsValidation.success ? queryParamsValidation.data.filter : {},
|
||||
[queryParamsValidation],
|
||||
);
|
||||
const hasFiltersQueryParams = filterParamsValidation.success;
|
||||
const viewIdQueryParam = useMemo(
|
||||
() => queryParamsValidation.success && queryParamsValidation.data.view,
|
||||
[queryParamsValidation],
|
||||
);
|
||||
|
||||
const hasFiltersQueryParams = filterQueryParams;
|
||||
|
||||
const getFiltersFromQueryParams = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
@ -129,6 +138,7 @@ export const useFiltersFromQueryParams = () => {
|
||||
: filterValueFromURL;
|
||||
|
||||
return {
|
||||
__typename: 'ViewFilter',
|
||||
id: `tmp-${[
|
||||
fieldName,
|
||||
filterOperandFromURL,
|
||||
@ -140,6 +150,7 @@ export const useFiltersFromQueryParams = () => {
|
||||
displayValue:
|
||||
relationRecordNames?.join(', ') ?? filterValueAsString,
|
||||
definition: filterDefinition,
|
||||
persistAction: 'NONE',
|
||||
};
|
||||
},
|
||||
),
|
||||
@ -156,6 +167,7 @@ export const useFiltersFromQueryParams = () => {
|
||||
);
|
||||
|
||||
return {
|
||||
viewIdQueryParam,
|
||||
hasFiltersQueryParams,
|
||||
getFiltersFromQueryParams,
|
||||
};
|
||||
@ -1,90 +0,0 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated';
|
||||
|
||||
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(
|
||||
getScopedStateDeprecated(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,
|
||||
onViewTypeChangeState,
|
||||
onViewCompactModeChangeState,
|
||||
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,
|
||||
onViewTypeChangeState,
|
||||
onViewCompactModeChangeState,
|
||||
savedViewFieldsByKeySelector,
|
||||
savedViewFieldsState,
|
||||
savedViewFiltersByKeySelector,
|
||||
savedViewFiltersState,
|
||||
savedViewSortsByKeySelector,
|
||||
savedViewSortsState,
|
||||
viewEditModeState,
|
||||
viewObjectMetadataIdState,
|
||||
viewTypeState,
|
||||
viewsState,
|
||||
};
|
||||
};
|
||||
@ -1,236 +0,0 @@
|
||||
import { Reference, useApolloClient } from '@apollo/client';
|
||||
import { produce } from 'immer';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
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 { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
import { useViewScopedStates } from './useViewScopedStates';
|
||||
|
||||
export const useViewSorts = (viewScopeId: string) => {
|
||||
const {
|
||||
updateOneRecordMutation,
|
||||
createOneRecordMutation,
|
||||
deleteOneRecordMutation,
|
||||
} = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.ViewSort,
|
||||
});
|
||||
|
||||
const { modifyRecordFromCache } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.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 (isUndefinedOrNull(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: (viewSortsRef, { readField }) => {
|
||||
const edges = readField<{ node: Reference }[]>(
|
||||
'edges',
|
||||
viewSortsRef,
|
||||
);
|
||||
|
||||
if (!edges) return viewSortsRef;
|
||||
|
||||
return {
|
||||
...viewSortsRef,
|
||||
edges: currentViewSorts.map((viewSort) => ({
|
||||
node: viewSort,
|
||||
cursor: '',
|
||||
})),
|
||||
};
|
||||
},
|
||||
});
|
||||
},
|
||||
[
|
||||
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 };
|
||||
};
|
||||
@ -0,0 +1,96 @@
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
import { extractComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/extractComponentReadOnlySelector';
|
||||
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
|
||||
import { availableFieldDefinitionsComponentState } from '@/views/states/availableFieldDefinitionsComponentState';
|
||||
import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState';
|
||||
import { availableSortDefinitionsComponentState } from '@/views/states/availableSortDefinitionsComponentState';
|
||||
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
|
||||
import { entityCountInCurrentViewComponentState } from '@/views/states/entityCountInCurrentViewComponentState';
|
||||
import { isCurrentViewKeyIndexComponentState } from '@/views/states/isCurrentViewIndexComponentState';
|
||||
import { isPersistingViewFieldsComponentState } from '@/views/states/isPersistingViewFieldsComponentState';
|
||||
import { isViewBarExpandedComponentState } from '@/views/states/isViewBarExpandedComponentState';
|
||||
import { onCurrentViewChangeComponentState } from '@/views/states/onCurrentViewChangeComponentState';
|
||||
import { canPersistViewComponentSelector } from '@/views/states/selectors/canPersistViewComponentSelector';
|
||||
import { unsavedToDeleteViewFilterIdsComponentState } from '@/views/states/unsavedToDeleteViewFilterIdsComponentState';
|
||||
import { unsavedToDeleteViewSortIdsComponentState } from '@/views/states/unsavedToDeleteViewSortIdsComponentState';
|
||||
import { unsavedToUpsertViewFiltersComponentState } from '@/views/states/unsavedToUpsertViewFiltersComponentState';
|
||||
import { unsavedToUpsertViewSortsComponentState } from '@/views/states/unsavedToUpsertViewSortsComponentState';
|
||||
import { viewEditModeComponentState } from '@/views/states/viewEditModeComponentState';
|
||||
import { viewObjectMetadataIdComponentState } from '@/views/states/viewObjectMetadataIdComponentState';
|
||||
|
||||
import { ViewScopeInternalContext } from '../../scopes/scope-internal-context/ViewScopeInternalContext';
|
||||
|
||||
export const useViewStates = (viewComponentId?: string) => {
|
||||
const componentId = useAvailableScopeIdOrThrow(
|
||||
ViewScopeInternalContext,
|
||||
viewComponentId,
|
||||
);
|
||||
|
||||
return {
|
||||
componentId,
|
||||
currentViewIdState: extractComponentState(
|
||||
currentViewIdComponentState,
|
||||
componentId,
|
||||
),
|
||||
availableFieldDefinitionsState: extractComponentState(
|
||||
availableFieldDefinitionsComponentState,
|
||||
componentId,
|
||||
),
|
||||
availableFilterDefinitionsState: extractComponentState(
|
||||
availableFilterDefinitionsComponentState,
|
||||
componentId,
|
||||
),
|
||||
availableSortDefinitionsState: extractComponentState(
|
||||
availableSortDefinitionsComponentState,
|
||||
componentId,
|
||||
),
|
||||
canPersistViewSelector: extractComponentReadOnlySelector(
|
||||
canPersistViewComponentSelector,
|
||||
componentId,
|
||||
),
|
||||
isViewBarExpandedState: extractComponentState(
|
||||
isViewBarExpandedComponentState,
|
||||
componentId,
|
||||
),
|
||||
onCurrentViewChangeState: extractComponentState(
|
||||
onCurrentViewChangeComponentState,
|
||||
componentId,
|
||||
),
|
||||
entityCountInCurrentViewState: extractComponentState(
|
||||
entityCountInCurrentViewComponentState,
|
||||
componentId,
|
||||
),
|
||||
viewEditModeState: extractComponentState(
|
||||
viewEditModeComponentState,
|
||||
componentId,
|
||||
),
|
||||
viewObjectMetadataIdState: extractComponentState(
|
||||
viewObjectMetadataIdComponentState,
|
||||
componentId,
|
||||
),
|
||||
unsavedToUpsertViewFiltersState: extractComponentState(
|
||||
unsavedToUpsertViewFiltersComponentState,
|
||||
componentId,
|
||||
),
|
||||
unsavedToUpsertViewSortsState: extractComponentState(
|
||||
unsavedToUpsertViewSortsComponentState,
|
||||
componentId,
|
||||
),
|
||||
unsavedToDeleteViewFilterIdsState: extractComponentState(
|
||||
unsavedToDeleteViewFilterIdsComponentState,
|
||||
componentId,
|
||||
),
|
||||
unsavedToDeleteViewSortIdsState: extractComponentState(
|
||||
unsavedToDeleteViewSortIdsComponentState,
|
||||
componentId,
|
||||
),
|
||||
isPersistingViewFieldsState: extractComponentState(
|
||||
isPersistingViewFieldsComponentState,
|
||||
componentId,
|
||||
),
|
||||
isCurrentViewKeyIndexState: extractComponentState(
|
||||
isCurrentViewKeyIndexComponentState,
|
||||
componentId,
|
||||
),
|
||||
};
|
||||
};
|
||||
@ -1,80 +0,0 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
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: CoreObjectNameSingular.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,
|
||||
isCompact: view.isCompact,
|
||||
},
|
||||
},
|
||||
refetchQueries: [findManyQuery],
|
||||
});
|
||||
};
|
||||
|
||||
const deleteView = async (viewId: string) => {
|
||||
await apolloClient.mutate({
|
||||
mutation: deleteOneMutation,
|
||||
variables: {
|
||||
idToDelete: viewId,
|
||||
},
|
||||
refetchQueries: [findManyQuery],
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
createView,
|
||||
deleteView,
|
||||
isFetchingViews: false,
|
||||
updateView,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,158 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { useGetViewFromCache } from '@/views/hooks/useGetViewFromCache';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const useCombinedViewFilters = (viewBarComponentId?: string) => {
|
||||
const {
|
||||
unsavedToUpsertViewFiltersState,
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
currentViewIdState,
|
||||
} = useViewStates(viewBarComponentId);
|
||||
|
||||
const { getViewFromCache } = useGetViewFromCache();
|
||||
|
||||
const upsertCombinedViewFilter = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (upsertedFilter: Filter) => {
|
||||
const unsavedToUpsertViewFilters = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToUpsertViewFiltersState,
|
||||
);
|
||||
|
||||
const unsavedToDeleteViewFilterIds = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
);
|
||||
|
||||
const currentViewId = getSnapshotValue(snapshot, currentViewIdState);
|
||||
|
||||
if (!currentViewId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentView = await getViewFromCache(currentViewId);
|
||||
|
||||
if (!currentView) {
|
||||
return;
|
||||
}
|
||||
|
||||
const matchingFilterInCurrentView = currentView.viewFilters.find(
|
||||
(viewFilter) =>
|
||||
viewFilter.fieldMetadataId === upsertedFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
const matchingFilterInUnsavedFilters = unsavedToUpsertViewFilters.find(
|
||||
(viewFilter) =>
|
||||
viewFilter.fieldMetadataId === upsertedFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
if (isDefined(matchingFilterInUnsavedFilters)) {
|
||||
const updatedFilters = unsavedToUpsertViewFilters.map((viewFilter) =>
|
||||
viewFilter.id === matchingFilterInUnsavedFilters.id
|
||||
? { ...viewFilter, ...upsertedFilter }
|
||||
: viewFilter,
|
||||
);
|
||||
|
||||
set(unsavedToUpsertViewFiltersState, updatedFilters);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDefined(matchingFilterInCurrentView)) {
|
||||
set(unsavedToUpsertViewFiltersState, [
|
||||
...unsavedToUpsertViewFilters,
|
||||
{ ...matchingFilterInCurrentView, ...upsertedFilter },
|
||||
]);
|
||||
set(
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
unsavedToDeleteViewFilterIds.filter(
|
||||
(id) => id !== matchingFilterInCurrentView.id,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
set(unsavedToUpsertViewFiltersState, [
|
||||
...unsavedToUpsertViewFilters,
|
||||
{
|
||||
...upsertedFilter,
|
||||
id: v4(),
|
||||
__typename: 'ViewFilter',
|
||||
} satisfies ViewFilter,
|
||||
]);
|
||||
},
|
||||
[
|
||||
currentViewIdState,
|
||||
getViewFromCache,
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
unsavedToUpsertViewFiltersState,
|
||||
],
|
||||
);
|
||||
const removeCombinedViewFilter = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (fieldMetadataId: string) => {
|
||||
const unsavedToUpsertViewFilters = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToUpsertViewFiltersState,
|
||||
);
|
||||
|
||||
const unsavedToDeleteViewFilterIds = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
);
|
||||
|
||||
const currentViewId = getSnapshotValue(snapshot, currentViewIdState);
|
||||
|
||||
if (!currentViewId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentView = await getViewFromCache(currentViewId);
|
||||
|
||||
if (!currentView) {
|
||||
return;
|
||||
}
|
||||
|
||||
const matchingFilterInCurrentView = currentView.viewFilters.find(
|
||||
(viewFilter) => viewFilter.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
|
||||
const matchingFilterInUnsavedFilters = unsavedToUpsertViewFilters.find(
|
||||
(viewFilter) => viewFilter.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
|
||||
if (isDefined(matchingFilterInUnsavedFilters)) {
|
||||
set(
|
||||
unsavedToUpsertViewFiltersState,
|
||||
unsavedToUpsertViewFilters.filter(
|
||||
(viewFilter) => viewFilter.fieldMetadataId !== fieldMetadataId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (isDefined(matchingFilterInCurrentView)) {
|
||||
set(unsavedToDeleteViewFilterIdsState, [
|
||||
...new Set([
|
||||
...unsavedToDeleteViewFilterIds,
|
||||
matchingFilterInCurrentView.id,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
},
|
||||
[
|
||||
currentViewIdState,
|
||||
getViewFromCache,
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
unsavedToUpsertViewFiltersState,
|
||||
],
|
||||
);
|
||||
return {
|
||||
upsertCombinedViewFilter,
|
||||
removeCombinedViewFilter,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,159 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { useGetViewFromCache } from '@/views/hooks/useGetViewFromCache';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const useCombinedViewSorts = (viewBarComponentId?: string) => {
|
||||
const {
|
||||
unsavedToUpsertViewSortsState,
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
currentViewIdState,
|
||||
} = useViewStates(viewBarComponentId);
|
||||
|
||||
const { getViewFromCache } = useGetViewFromCache();
|
||||
|
||||
const upsertCombinedViewSort = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (upsertedSort: Sort) => {
|
||||
const unsavedToUpsertViewSorts = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToUpsertViewSortsState,
|
||||
);
|
||||
|
||||
const unsavedToDeleteViewSortIds = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
);
|
||||
|
||||
const currentViewId = getSnapshotValue(snapshot, currentViewIdState);
|
||||
|
||||
if (!currentViewId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentView = await getViewFromCache(currentViewId);
|
||||
|
||||
if (!currentView) {
|
||||
return;
|
||||
}
|
||||
|
||||
const matchingSortInCurrentView = currentView.viewSorts.find(
|
||||
(viewSort) =>
|
||||
viewSort.fieldMetadataId === upsertedSort.fieldMetadataId,
|
||||
);
|
||||
|
||||
const matchingSortInUnsavedSorts = unsavedToUpsertViewSorts.find(
|
||||
(viewSort) =>
|
||||
viewSort.fieldMetadataId === upsertedSort.fieldMetadataId,
|
||||
);
|
||||
|
||||
if (isDefined(matchingSortInUnsavedSorts)) {
|
||||
const updatedSorts = unsavedToUpsertViewSorts.map((viewSort) =>
|
||||
viewSort.id === matchingSortInUnsavedSorts.id
|
||||
? { ...viewSort, ...upsertedSort }
|
||||
: viewSort,
|
||||
);
|
||||
|
||||
set(unsavedToUpsertViewSortsState, updatedSorts);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDefined(matchingSortInCurrentView)) {
|
||||
set(unsavedToUpsertViewSortsState, [
|
||||
...unsavedToUpsertViewSorts,
|
||||
{ ...matchingSortInCurrentView, ...upsertedSort },
|
||||
]);
|
||||
set(
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
unsavedToDeleteViewSortIds.filter(
|
||||
(id) => id !== matchingSortInCurrentView.id,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
set(unsavedToUpsertViewSortsState, [
|
||||
...unsavedToUpsertViewSorts,
|
||||
{
|
||||
...upsertedSort,
|
||||
id: v4(),
|
||||
__typename: 'ViewSort',
|
||||
} satisfies ViewSort,
|
||||
]);
|
||||
},
|
||||
[
|
||||
currentViewIdState,
|
||||
getViewFromCache,
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
unsavedToUpsertViewSortsState,
|
||||
],
|
||||
);
|
||||
const removeCombinedViewSort = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (fieldMetadataId: string) => {
|
||||
const unsavedToUpsertViewSorts = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToUpsertViewSortsState,
|
||||
);
|
||||
|
||||
const unsavedToDeleteViewSortIds = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
);
|
||||
|
||||
const currentViewId = getSnapshotValue(snapshot, currentViewIdState);
|
||||
|
||||
if (!currentViewId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentView = await getViewFromCache(currentViewId);
|
||||
|
||||
if (!currentView) {
|
||||
return;
|
||||
}
|
||||
|
||||
const matchingSortInCurrentView = currentView.viewSorts.find(
|
||||
(viewSort) => viewSort.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
|
||||
const matchingSortInUnsavedSorts = unsavedToUpsertViewSorts.find(
|
||||
(viewSort) => viewSort.fieldMetadataId === fieldMetadataId,
|
||||
);
|
||||
|
||||
if (isDefined(matchingSortInUnsavedSorts)) {
|
||||
set(
|
||||
unsavedToUpsertViewSortsState,
|
||||
unsavedToUpsertViewSorts.filter(
|
||||
(viewSort) => viewSort.fieldMetadataId !== fieldMetadataId,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDefined(matchingSortInCurrentView)) {
|
||||
set(unsavedToDeleteViewSortIdsState, [
|
||||
...new Set([
|
||||
...unsavedToDeleteViewSortIds,
|
||||
matchingSortInCurrentView.id,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
},
|
||||
[
|
||||
currentViewIdState,
|
||||
getViewFromCache,
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
unsavedToUpsertViewSortsState,
|
||||
],
|
||||
);
|
||||
return {
|
||||
upsertCombinedViewSort,
|
||||
removeCombinedViewSort,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,107 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
|
||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { ViewScopeInternalContext } from '@/views/scopes/scope-internal-context/ViewScopeInternalContext';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { combinedViewFilters } from '@/views/utils/combinedViewFilters';
|
||||
import { combinedViewSorts } from '@/views/utils/combinedViewSorts';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const useGetCurrentView = (viewBarComponentId?: string) => {
|
||||
const componentId = useAvailableScopeIdOrThrow(
|
||||
ViewScopeInternalContext,
|
||||
viewBarComponentId,
|
||||
);
|
||||
|
||||
const { records: views } = usePrefetchedData<GraphQLView>(
|
||||
PrefetchKey.AllViews,
|
||||
);
|
||||
|
||||
const {
|
||||
currentViewIdState,
|
||||
viewObjectMetadataIdState,
|
||||
unsavedToUpsertViewFiltersState,
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
unsavedToUpsertViewSortsState,
|
||||
isCurrentViewKeyIndexState,
|
||||
} = useViewStates(componentId);
|
||||
|
||||
const currentViewId = useRecoilValue(currentViewIdState);
|
||||
const viewObjectMetadataId = useRecoilValue(viewObjectMetadataIdState);
|
||||
const setIsCurrentViewKeyIndex = useSetRecoilState(
|
||||
isCurrentViewKeyIndexState,
|
||||
);
|
||||
|
||||
const currentViewFromCurrentViewId = views.find(
|
||||
(view) => view.id === currentViewId,
|
||||
);
|
||||
const indexView = views.find(
|
||||
(view) =>
|
||||
view.key === 'INDEX' && view.objectMetadataId === viewObjectMetadataId,
|
||||
);
|
||||
|
||||
const currentView = currentViewId ? currentViewFromCurrentViewId : indexView;
|
||||
|
||||
useEffect(() => {
|
||||
setIsCurrentViewKeyIndex(currentView?.key === 'INDEX');
|
||||
}, [currentView, setIsCurrentViewKeyIndex]);
|
||||
|
||||
const viewsOnCurrentObject = views
|
||||
.filter((view) => view.objectMetadataId === viewObjectMetadataId)
|
||||
.map((view) => ({
|
||||
id: view.id,
|
||||
name: view.name,
|
||||
type: view.type,
|
||||
key: view.key,
|
||||
objectMetadataId: view.objectMetadataId,
|
||||
icon: view.icon,
|
||||
}));
|
||||
|
||||
const unsavedToUpsertViewFilters = useRecoilValue(
|
||||
unsavedToUpsertViewFiltersState,
|
||||
);
|
||||
const unsavedToUpsertViewSorts = useRecoilValue(
|
||||
unsavedToUpsertViewSortsState,
|
||||
);
|
||||
const unsavedToDeleteViewFilterIds = useRecoilValue(
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
);
|
||||
const unsavedToDeleteViewSortIds = useRecoilValue(
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
);
|
||||
|
||||
if (!isDefined(currentView)) {
|
||||
return {
|
||||
componentId,
|
||||
currentViewWithSavedFiltersAndSorts: undefined,
|
||||
currentViewWithCombinedFiltersAndSorts: undefined,
|
||||
viewsOnCurrentObject: viewsOnCurrentObject ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
const currentViewWithCombinedFiltersAndSorts = {
|
||||
...currentView,
|
||||
viewFilters: combinedViewFilters(
|
||||
currentView.viewFilters,
|
||||
unsavedToUpsertViewFilters,
|
||||
unsavedToDeleteViewFilterIds,
|
||||
),
|
||||
viewSorts: combinedViewSorts(
|
||||
currentView.viewSorts,
|
||||
unsavedToUpsertViewSorts,
|
||||
unsavedToDeleteViewSortIds,
|
||||
),
|
||||
};
|
||||
|
||||
return {
|
||||
componentId,
|
||||
currentViewWithSavedFiltersAndSorts: currentView,
|
||||
currentViewWithCombinedFiltersAndSorts,
|
||||
viewsOnCurrentObject: viewsOnCurrentObject ?? [],
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,48 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { ObjectRecordEdge } from '@/object-record/types/ObjectRecordEdge';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useGetViewFromCache = () => {
|
||||
const client = useApolloClient();
|
||||
const cache = client.cache;
|
||||
|
||||
const { getRecordFromCache } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.View,
|
||||
});
|
||||
|
||||
const getViewFromCache = useCallback(
|
||||
async (viewId: string) => {
|
||||
// Todo Fix typing once we have figured out record connections
|
||||
const viewWithConnections = getRecordFromCache<any>(viewId, cache);
|
||||
|
||||
if (isUndefinedOrNull(viewWithConnections)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const view = {
|
||||
...viewWithConnections,
|
||||
viewFilters: viewWithConnections.viewFilters?.edges.map(
|
||||
(edge: ObjectRecordEdge) => edge.node,
|
||||
),
|
||||
viewSorts: viewWithConnections.viewSorts?.edges.map(
|
||||
(edge: ObjectRecordEdge) => edge.node,
|
||||
),
|
||||
viewFields: viewWithConnections.viewFields?.edges.map(
|
||||
(edge: ObjectRecordEdge) => edge.node,
|
||||
),
|
||||
} as GraphQLView;
|
||||
|
||||
return view;
|
||||
},
|
||||
[cache, getRecordFromCache],
|
||||
);
|
||||
|
||||
return {
|
||||
getViewFromCache,
|
||||
};
|
||||
};
|
||||
130
packages/twenty-front/src/modules/views/hooks/useHandleViews.ts
Normal file
130
packages/twenty-front/src/modules/views/hooks/useHandleViews.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { usePersistViewFieldRecords } from '@/views/hooks/internal/usePersistViewFieldRecords';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { useGetViewFromCache } from '@/views/hooks/useGetViewFromCache';
|
||||
import { useResetCurrentView } from '@/views/hooks/useResetCurrentView';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useHandleViews = (viewBarComponentId?: string) => {
|
||||
const { resetCurrentView } = useResetCurrentView(viewBarComponentId);
|
||||
|
||||
const { currentViewIdState } = useViewStates(viewBarComponentId);
|
||||
|
||||
const { getViewFromCache } = useGetViewFromCache();
|
||||
|
||||
const { deleteOneRecord } = useDeleteOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.View,
|
||||
});
|
||||
|
||||
const { createOneRecord } = useCreateOneRecord<GraphQLView>({
|
||||
objectNameSingular: CoreObjectNameSingular.View,
|
||||
});
|
||||
|
||||
const { updateOneRecord } = useUpdateOneRecord({
|
||||
objectNameSingular: CoreObjectNameSingular.View,
|
||||
});
|
||||
|
||||
const { createViewFieldRecords } = usePersistViewFieldRecords();
|
||||
|
||||
const createViewFromCurrent = useRecoilCallback(() => () => {}, []);
|
||||
|
||||
const [_, setSearchParams] = useSearchParams();
|
||||
|
||||
const removeView = useRecoilCallback(
|
||||
() => async (viewId: string) => {
|
||||
await deleteOneRecord(viewId);
|
||||
},
|
||||
[deleteOneRecord],
|
||||
);
|
||||
|
||||
const createEmptyView = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (id: string, name: string) => {
|
||||
const currentViewId = getSnapshotValue(snapshot, currentViewIdState);
|
||||
|
||||
if (!isDefined(currentViewId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const view = await getViewFromCache(currentViewId);
|
||||
|
||||
if (!isDefined(view)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newView = await createOneRecord({
|
||||
id: id ?? v4(),
|
||||
name: name,
|
||||
objectMetadataId: view.objectMetadataId,
|
||||
type: view.type,
|
||||
});
|
||||
|
||||
if (isUndefinedOrNull(newView)) {
|
||||
throw new Error('Failed to create view');
|
||||
}
|
||||
|
||||
await createViewFieldRecords(view.viewFields, newView);
|
||||
},
|
||||
[
|
||||
createOneRecord,
|
||||
createViewFieldRecords,
|
||||
currentViewIdState,
|
||||
getViewFromCache,
|
||||
],
|
||||
);
|
||||
|
||||
const changeViewInUrl = useCallback(
|
||||
(viewId: string) => {
|
||||
setSearchParams((previousSearchParams) => {
|
||||
previousSearchParams.set('view', viewId);
|
||||
return previousSearchParams;
|
||||
});
|
||||
},
|
||||
[setSearchParams],
|
||||
);
|
||||
|
||||
const selectView = useRecoilCallback(
|
||||
({ set }) =>
|
||||
async (viewId: string) => {
|
||||
set(currentViewIdState, viewId);
|
||||
changeViewInUrl(viewId);
|
||||
resetCurrentView();
|
||||
},
|
||||
[changeViewInUrl, currentViewIdState, resetCurrentView],
|
||||
);
|
||||
|
||||
const updateCurrentView = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (view: Partial<GraphQLView>) => {
|
||||
const currentViewId = snapshot
|
||||
.getLoadable(currentViewIdState)
|
||||
.getValue();
|
||||
if (isDefined(currentViewId)) {
|
||||
await updateOneRecord({
|
||||
idToUpdate: currentViewId,
|
||||
updateOneRecordInput: view,
|
||||
});
|
||||
}
|
||||
},
|
||||
[currentViewIdState, updateOneRecord],
|
||||
);
|
||||
|
||||
return {
|
||||
selectView,
|
||||
updateCurrentView,
|
||||
removeView,
|
||||
createEmptyView,
|
||||
createViewFromCurrent,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,31 @@
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
|
||||
export const useInitViewBar = (viewBarComponentId?: string) => {
|
||||
const {
|
||||
availableFieldDefinitionsState,
|
||||
availableSortDefinitionsState,
|
||||
availableFilterDefinitionsState,
|
||||
viewObjectMetadataIdState,
|
||||
} = useViewStates(viewBarComponentId);
|
||||
|
||||
const setAvailableFieldDefinitions = useSetRecoilState(
|
||||
availableFieldDefinitionsState,
|
||||
);
|
||||
const setAvailableSortDefinitions = useSetRecoilState(
|
||||
availableSortDefinitionsState,
|
||||
);
|
||||
const setAvailableFilterDefinitions = useSetRecoilState(
|
||||
availableFilterDefinitionsState,
|
||||
);
|
||||
|
||||
const setViewObjectMetadataId = useSetRecoilState(viewObjectMetadataIdState);
|
||||
|
||||
return {
|
||||
setAvailableFieldDefinitions,
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
setViewObjectMetadataId,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,32 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
|
||||
export const useResetCurrentView = (viewBarComponentId?: string) => {
|
||||
const {
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
unsavedToUpsertViewSortsState,
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
unsavedToUpsertViewFiltersState,
|
||||
} = useViewStates(viewBarComponentId);
|
||||
|
||||
const resetCurrentView = useRecoilCallback(
|
||||
({ set }) =>
|
||||
async () => {
|
||||
set(unsavedToDeleteViewFilterIdsState, []);
|
||||
set(unsavedToDeleteViewSortIdsState, []);
|
||||
set(unsavedToUpsertViewFiltersState, []);
|
||||
set(unsavedToUpsertViewSortsState, []);
|
||||
},
|
||||
[
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
unsavedToUpsertViewFiltersState,
|
||||
unsavedToUpsertViewSortsState,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
resetCurrentView,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,87 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { usePersistViewFieldRecords } from '@/views/hooks/internal/usePersistViewFieldRecords';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { useGetViewFromCache } from '@/views/hooks/useGetViewFromCache';
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useSaveCurrentViewFields = (viewBarComponentId?: string) => {
|
||||
const { createViewFieldRecords, updateViewFieldRecords } =
|
||||
usePersistViewFieldRecords();
|
||||
|
||||
const { getViewFromCache } = useGetViewFromCache();
|
||||
|
||||
const { isPersistingViewFieldsState, currentViewIdState } =
|
||||
useViewStates(viewBarComponentId);
|
||||
|
||||
const saveViewFields = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (fields: ViewField[]) => {
|
||||
const currentViewId = snapshot
|
||||
.getLoadable(currentViewIdState)
|
||||
.getValue();
|
||||
|
||||
if (!currentViewId) {
|
||||
return;
|
||||
}
|
||||
|
||||
set(isPersistingViewFieldsState, true);
|
||||
const view = await getViewFromCache(currentViewId);
|
||||
|
||||
if (isUndefinedOrNull(view)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewFieldsToUpdate = fields
|
||||
.map((field) => {
|
||||
const existingField = view.viewFields.find(
|
||||
(viewField) => viewField.id === field.id,
|
||||
);
|
||||
|
||||
if (isUndefinedOrNull(existingField)) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
isDeeplyEqual(
|
||||
{
|
||||
position: existingField.position,
|
||||
size: existingField.size,
|
||||
isVisible: existingField.isVisible,
|
||||
},
|
||||
{
|
||||
position: field.position,
|
||||
size: field.size,
|
||||
isVisible: field.isVisible,
|
||||
},
|
||||
)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
return field;
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
const viewFieldsToCreate = fields.filter((field) => !field.id);
|
||||
|
||||
await Promise.all([
|
||||
createViewFieldRecords(viewFieldsToCreate, view),
|
||||
updateViewFieldRecords(viewFieldsToUpdate),
|
||||
]);
|
||||
set(isPersistingViewFieldsState, false);
|
||||
},
|
||||
[
|
||||
createViewFieldRecords,
|
||||
currentViewIdState,
|
||||
getViewFromCache,
|
||||
isPersistingViewFieldsState,
|
||||
updateViewFieldRecords,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
saveViewFields,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,147 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { usePersistViewFilterRecords } from '@/views/hooks/internal/usePersistViewFilterRecords';
|
||||
import { usePersistViewSortRecords } from '@/views/hooks/internal/usePersistViewSortRecords';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { useGetViewFromCache } from '@/views/hooks/useGetViewFromCache';
|
||||
import { useResetCurrentView } from '@/views/hooks/useResetCurrentView';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
export const useSaveCurrentViewFiltersAndSorts = (
|
||||
viewBarComponentId?: string,
|
||||
) => {
|
||||
const { getViewFromCache } = useGetViewFromCache();
|
||||
|
||||
const {
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
unsavedToUpsertViewSortsState,
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
unsavedToUpsertViewFiltersState,
|
||||
currentViewIdState,
|
||||
} = useViewStates(viewBarComponentId);
|
||||
|
||||
const {
|
||||
createViewSortRecords,
|
||||
updateViewSortRecords,
|
||||
deleteViewSortRecords,
|
||||
} = usePersistViewSortRecords();
|
||||
|
||||
const {
|
||||
createViewFilterRecords,
|
||||
updateViewFilterRecords,
|
||||
deleteViewFilterRecords,
|
||||
} = usePersistViewFilterRecords();
|
||||
|
||||
const { resetCurrentView } = useResetCurrentView(viewBarComponentId);
|
||||
|
||||
const saveViewSorts = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const unsavedToDeleteViewSortIds = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
);
|
||||
|
||||
const unsavedToUpsertViewSorts = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToUpsertViewSortsState,
|
||||
);
|
||||
|
||||
const view = await getViewFromCache(viewId);
|
||||
|
||||
if (isUndefinedOrNull(view)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewSortsToCreate = unsavedToUpsertViewSorts.filter(
|
||||
(viewSort) =>
|
||||
!view.viewSorts.some(
|
||||
(vf) => vf.fieldMetadataId === viewSort.fieldMetadataId,
|
||||
),
|
||||
);
|
||||
|
||||
const viewSortsToUpdate = unsavedToUpsertViewSorts.filter((viewSort) =>
|
||||
view.viewSorts.some((vf) => vf.id === viewSort.id),
|
||||
);
|
||||
|
||||
await createViewSortRecords(viewSortsToCreate, view);
|
||||
await updateViewSortRecords(viewSortsToUpdate);
|
||||
await deleteViewSortRecords(unsavedToDeleteViewSortIds);
|
||||
},
|
||||
[
|
||||
createViewSortRecords,
|
||||
deleteViewSortRecords,
|
||||
getViewFromCache,
|
||||
unsavedToDeleteViewSortIdsState,
|
||||
unsavedToUpsertViewSortsState,
|
||||
updateViewSortRecords,
|
||||
],
|
||||
);
|
||||
|
||||
const saveViewFilters = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const unsavedToDeleteViewFilterIds = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
);
|
||||
|
||||
const unsavedToUpsertViewFilters = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToUpsertViewFiltersState,
|
||||
);
|
||||
|
||||
const view = await getViewFromCache(viewId);
|
||||
|
||||
if (isUndefinedOrNull(view)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewFiltersToCreate = unsavedToUpsertViewFilters.filter(
|
||||
(viewFilter) =>
|
||||
!view.viewFilters.some((vf) => vf.id === viewFilter.id),
|
||||
);
|
||||
|
||||
const viewFiltersToUpdate = unsavedToUpsertViewFilters.filter(
|
||||
(viewFilter) =>
|
||||
view.viewFilters.some((vf) => vf.id === viewFilter.id),
|
||||
);
|
||||
|
||||
await createViewFilterRecords(viewFiltersToCreate, view);
|
||||
await updateViewFilterRecords(viewFiltersToUpdate);
|
||||
await deleteViewFilterRecords(unsavedToDeleteViewFilterIds);
|
||||
},
|
||||
[
|
||||
createViewFilterRecords,
|
||||
deleteViewFilterRecords,
|
||||
getViewFromCache,
|
||||
unsavedToDeleteViewFilterIdsState,
|
||||
unsavedToUpsertViewFiltersState,
|
||||
updateViewFilterRecords,
|
||||
],
|
||||
);
|
||||
|
||||
const saveCurrentViewFilterAndSorts = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
const currentViewId = snapshot
|
||||
.getLoadable(currentViewIdState)
|
||||
.getValue();
|
||||
|
||||
if (!isDefined(currentViewId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await saveViewFilters(currentViewId);
|
||||
await saveViewSorts(currentViewId);
|
||||
resetCurrentView();
|
||||
},
|
||||
[currentViewIdState, resetCurrentView, saveViewFilters, saveViewSorts],
|
||||
);
|
||||
|
||||
return {
|
||||
saveCurrentViewFilterAndSorts,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,15 @@
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
|
||||
export const useSetRecordCountInCurrentView = (viewBarComponentId?: string) => {
|
||||
const { entityCountInCurrentViewState } = useViewStates(viewBarComponentId);
|
||||
|
||||
const setEntityCountInCurrentView = useSetRecoilState(
|
||||
entityCountInCurrentViewState,
|
||||
);
|
||||
|
||||
return {
|
||||
setRecordCountInCurrentView: setEntityCountInCurrentView,
|
||||
};
|
||||
};
|
||||
@ -1,441 +0,0 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilCallback, useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
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 { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
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,
|
||||
} = 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 [_, setSearchParams] = useSearchParams();
|
||||
|
||||
const changeViewInUrl = useCallback(
|
||||
(viewId: string) => {
|
||||
setSearchParams((previousSearchParams) => {
|
||||
previousSearchParams.set('view', viewId);
|
||||
return previousSearchParams;
|
||||
});
|
||||
},
|
||||
[setSearchParams],
|
||||
);
|
||||
|
||||
const loadViewFields = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (viewFields: ViewField[], currentViewId: string) => {
|
||||
const {
|
||||
availableFieldDefinitions,
|
||||
onViewFieldsChange,
|
||||
savedViewFields,
|
||||
isPersistingView,
|
||||
} = getViewScopedStateValuesFromSnapshot({
|
||||
snapshot,
|
||||
viewScopeId: scopeId,
|
||||
viewId: currentViewId,
|
||||
});
|
||||
|
||||
const { savedViewFieldsState, currentViewFieldsState } =
|
||||
getViewScopedStatesFromSnapshot({
|
||||
snapshot,
|
||||
viewScopeId: scopeId,
|
||||
viewId: currentViewId,
|
||||
});
|
||||
|
||||
if (isUndefinedOrNull(availableFieldDefinitions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const queriedViewFields = viewFields.filter(isDefined);
|
||||
|
||||
if (isPersistingView) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDeeplyEqual(savedViewFields, queriedViewFields)) {
|
||||
set(currentViewFieldsState, queriedViewFields);
|
||||
set(savedViewFieldsState, queriedViewFields);
|
||||
}
|
||||
|
||||
onViewFieldsChange?.(queriedViewFields);
|
||||
},
|
||||
[scopeId],
|
||||
);
|
||||
|
||||
const loadViewFilters = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (viewFilters: ViewFilter[], currentViewId: string) => {
|
||||
const {
|
||||
availableFilterDefinitions,
|
||||
savedViewFilters,
|
||||
onViewFiltersChange,
|
||||
} = getViewScopedStateValuesFromSnapshot({
|
||||
snapshot,
|
||||
viewScopeId: scopeId,
|
||||
viewId: currentViewId,
|
||||
});
|
||||
|
||||
const { savedViewFiltersState, currentViewFiltersState } =
|
||||
getViewScopedStatesFromSnapshot({
|
||||
snapshot,
|
||||
viewScopeId: scopeId,
|
||||
viewId: currentViewId,
|
||||
});
|
||||
|
||||
if (isUndefinedOrNull(availableFilterDefinitions)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const queriedViewFilters = viewFilters
|
||||
.map((viewFilter) => {
|
||||
const availableFilterDefinition = availableFilterDefinitions.find(
|
||||
(filterDefinition) =>
|
||||
filterDefinition.fieldMetadataId === viewFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
if (!availableFilterDefinition) return null;
|
||||
|
||||
return {
|
||||
...viewFilter,
|
||||
displayValue: viewFilter.displayValue ?? viewFilter.value,
|
||||
definition: availableFilterDefinition,
|
||||
};
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
if (!isDeeplyEqual(savedViewFilters, queriedViewFilters)) {
|
||||
set(savedViewFiltersState, queriedViewFilters);
|
||||
set(currentViewFiltersState, queriedViewFilters);
|
||||
}
|
||||
onViewFiltersChange?.(queriedViewFilters);
|
||||
},
|
||||
[scopeId],
|
||||
);
|
||||
|
||||
const loadViewSorts = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (viewSorts: 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 = viewSorts
|
||||
.map((viewSort) => {
|
||||
const availableSortDefinition = availableSortDefinitions.find(
|
||||
(sort) => sort.fieldMetadataId === viewSort.fieldMetadataId,
|
||||
);
|
||||
|
||||
if (!availableSortDefinition) return null;
|
||||
|
||||
return {
|
||||
id: viewSort.id,
|
||||
fieldMetadataId: viewSort.fieldMetadataId,
|
||||
direction: viewSort.direction,
|
||||
definition: availableSortDefinition,
|
||||
};
|
||||
})
|
||||
.filter(isDefined);
|
||||
|
||||
if (!isDeeplyEqual(savedViewSorts, queriedViewSorts)) {
|
||||
set(savedViewSortsState, queriedViewSorts);
|
||||
set(currentViewSortsState, queriedViewSorts);
|
||||
}
|
||||
onViewSortsChange?.(queriedViewSorts);
|
||||
},
|
||||
[scopeId],
|
||||
);
|
||||
|
||||
const loadView = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(viewId: string) => {
|
||||
setCurrentViewId?.(viewId);
|
||||
|
||||
const { currentView, onViewTypeChange, onViewCompactModeChange } =
|
||||
getViewScopedStateValuesFromSnapshot({
|
||||
snapshot,
|
||||
viewScopeId: scopeId,
|
||||
viewId,
|
||||
});
|
||||
|
||||
if (!currentView) {
|
||||
return;
|
||||
}
|
||||
|
||||
onViewTypeChange?.(currentView.type);
|
||||
onViewCompactModeChange?.(currentView.isCompact);
|
||||
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 (isDefined(savedViewFilters)) {
|
||||
set(currentViewFiltersState, savedViewFilters);
|
||||
onViewFiltersChange?.(savedViewFilters);
|
||||
}
|
||||
if (isDefined(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' && isNonEmptyString(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,
|
||||
setEntityCountInCurrentView,
|
||||
setAvailableFieldDefinitions,
|
||||
|
||||
setAvailableSortDefinitions,
|
||||
upsertViewSort,
|
||||
removeViewSort,
|
||||
|
||||
setAvailableFilterDefinitions,
|
||||
upsertViewFilter,
|
||||
removeViewFilter,
|
||||
|
||||
persistViewFields,
|
||||
changeViewInUrl,
|
||||
loadView,
|
||||
loadViewFields,
|
||||
loadViewFilters,
|
||||
loadViewSorts,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,14 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
|
||||
export const useViewBarEditMode = (viewBarComponentId?: string) => {
|
||||
const { viewEditModeState } = useViewStates(viewBarComponentId);
|
||||
|
||||
const [viewEditMode, setViewEditMode] = useRecoilState(viewEditModeState);
|
||||
|
||||
return {
|
||||
viewEditMode,
|
||||
setViewEditMode,
|
||||
};
|
||||
};
|
||||
@ -1,10 +1,6 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
|
||||
import { ViewField } from '../types/ViewField';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
|
||||
import { ViewScopeInitEffect } from './init-effect/ViewScopeInitEffect';
|
||||
import { ViewScopeInternalContext } from './scope-internal-context/ViewScopeInternalContext';
|
||||
@ -12,23 +8,13 @@ import { ViewScopeInternalContext } from './scope-internal-context/ViewScopeInte
|
||||
type ViewScopeProps = {
|
||||
children: ReactNode;
|
||||
viewScopeId: string;
|
||||
onViewSortsChange?: (sorts: ViewSort[]) => void | Promise<void>;
|
||||
onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise<void>;
|
||||
onViewFieldsChange?: (fields: ViewField[]) => void | Promise<void>;
|
||||
onViewTypeChange?: (viewType: ViewType) => void | Promise<void>;
|
||||
onViewCompactModeChange?: (
|
||||
isCompactModeActive: boolean,
|
||||
) => void | Promise<void>;
|
||||
onCurrentViewChange: (view: GraphQLView | undefined) => void | Promise<void>;
|
||||
};
|
||||
|
||||
export const ViewScope = ({
|
||||
children,
|
||||
viewScopeId,
|
||||
onViewSortsChange,
|
||||
onViewFiltersChange,
|
||||
onViewFieldsChange,
|
||||
onViewTypeChange,
|
||||
onViewCompactModeChange,
|
||||
onCurrentViewChange,
|
||||
}: ViewScopeProps) => {
|
||||
return (
|
||||
<ViewScopeInternalContext.Provider
|
||||
@ -38,11 +24,7 @@ export const ViewScope = ({
|
||||
>
|
||||
<ViewScopeInitEffect
|
||||
viewScopeId={viewScopeId}
|
||||
onViewSortsChange={onViewSortsChange}
|
||||
onViewFiltersChange={onViewFiltersChange}
|
||||
onViewFieldsChange={onViewFieldsChange}
|
||||
onViewTypeChange={onViewTypeChange}
|
||||
onViewCompactModeChange={onViewCompactModeChange}
|
||||
onCurrentViewChange={onCurrentViewChange}
|
||||
/>
|
||||
{children}
|
||||
</ViewScopeInternalContext.Provider>
|
||||
|
||||
@ -1,64 +1,24 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { useViewStates } from '@/views/hooks/internal/useViewStates';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
|
||||
type ViewScopeInitEffectProps = {
|
||||
viewScopeId: string;
|
||||
onViewSortsChange?: (sorts: ViewSort[]) => void | Promise<void>;
|
||||
onViewFiltersChange?: (filters: ViewFilter[]) => void | Promise<void>;
|
||||
onViewFieldsChange?: (fields: ViewField[]) => void | Promise<void>;
|
||||
onViewTypeChange?: (viewType: ViewType) => void | Promise<void>;
|
||||
onViewCompactModeChange?: (
|
||||
isCompactModeActive: boolean,
|
||||
) => void | Promise<void>;
|
||||
onCurrentViewChange: (view: GraphQLView | undefined) => void | Promise<void>;
|
||||
};
|
||||
|
||||
export const ViewScopeInitEffect = ({
|
||||
onViewSortsChange,
|
||||
onViewFiltersChange,
|
||||
onViewFieldsChange,
|
||||
onViewTypeChange,
|
||||
onViewCompactModeChange,
|
||||
onCurrentViewChange,
|
||||
}: ViewScopeInitEffectProps) => {
|
||||
const {
|
||||
onViewFieldsChangeState,
|
||||
onViewFiltersChangeState,
|
||||
onViewSortsChangeState,
|
||||
onViewTypeChangeState,
|
||||
onViewCompactModeChangeState,
|
||||
} = useViewScopedStates();
|
||||
const { onCurrentViewChangeState } = useViewStates();
|
||||
|
||||
const setOnViewSortsChange = useSetRecoilState(onViewSortsChangeState);
|
||||
const setOnViewFiltersChange = useSetRecoilState(onViewFiltersChangeState);
|
||||
const setOnViewFieldsChange = useSetRecoilState(onViewFieldsChangeState);
|
||||
const setOnViewTypeChange = useSetRecoilState(onViewTypeChangeState);
|
||||
const setOnViewCompactModeChange = useSetRecoilState(
|
||||
onViewCompactModeChangeState,
|
||||
);
|
||||
const setOnCurrentViewChange = useSetRecoilState(onCurrentViewChangeState);
|
||||
|
||||
useEffect(() => {
|
||||
setOnViewSortsChange(() => onViewSortsChange);
|
||||
setOnViewFiltersChange(() => onViewFiltersChange);
|
||||
setOnViewFieldsChange(() => onViewFieldsChange);
|
||||
setOnViewTypeChange(() => onViewTypeChange);
|
||||
setOnViewCompactModeChange(() => onViewCompactModeChange);
|
||||
}, [
|
||||
onViewCompactModeChange,
|
||||
onViewFieldsChange,
|
||||
onViewFiltersChange,
|
||||
onViewSortsChange,
|
||||
onViewTypeChange,
|
||||
setOnViewCompactModeChange,
|
||||
setOnViewFieldsChange,
|
||||
setOnViewFiltersChange,
|
||||
setOnViewSortsChange,
|
||||
setOnViewTypeChange,
|
||||
]);
|
||||
setOnCurrentViewChange(() => onCurrentViewChange);
|
||||
}, [onCurrentViewChange, setOnCurrentViewChange]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
@ -2,9 +2,9 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const availableFieldDefinitionsScopedState = createComponentState<
|
||||
export const availableFieldDefinitionsComponentState = createComponentState<
|
||||
ColumnDefinition<FieldMetadata>[]
|
||||
>({
|
||||
key: 'availableFieldDefinitionsScopedState',
|
||||
key: 'availableFieldDefinitionsComponentState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,9 +1,9 @@
|
||||
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const availableFilterDefinitionsScopedState = createComponentState<
|
||||
export const availableFilterDefinitionsComponentState = createComponentState<
|
||||
FilterDefinition[]
|
||||
>({
|
||||
key: 'availableFilterDefinitionsScopedState',
|
||||
key: 'availableFilterDefinitionsComponentState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,9 +1,9 @@
|
||||
import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const availableSortDefinitionsScopedState = createComponentState<
|
||||
export const availableSortDefinitionsComponentState = createComponentState<
|
||||
SortDefinition[]
|
||||
>({
|
||||
key: 'availableSortDefinitionsScopedState',
|
||||
key: 'availableSortDefinitionsComponentState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,11 +0,0 @@
|
||||
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
|
||||
|
||||
import { ViewField } from '../types/ViewField';
|
||||
|
||||
export const currentViewFieldsScopedFamilyState = createComponentFamilyState<
|
||||
ViewField[],
|
||||
string
|
||||
>({
|
||||
key: 'currentViewFieldsScopedFamilyState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,11 +0,0 @@
|
||||
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
|
||||
|
||||
import { ViewFilter } from '../types/ViewFilter';
|
||||
|
||||
export const currentViewFiltersScopedFamilyState = createComponentFamilyState<
|
||||
ViewFilter[],
|
||||
string
|
||||
>({
|
||||
key: 'currentViewFiltersScopedFamilyState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,8 +1,8 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const currentViewIdScopedState = createComponentState<
|
||||
export const currentViewIdComponentState = createComponentState<
|
||||
string | undefined
|
||||
>({
|
||||
key: 'currentViewIdScopedState',
|
||||
key: 'currentViewIdComponentState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -1,11 +0,0 @@
|
||||
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
|
||||
|
||||
import { ViewSort } from '../types/ViewSort';
|
||||
|
||||
export const currentViewSortsScopedFamilyState = createComponentFamilyState<
|
||||
ViewSort[],
|
||||
string
|
||||
>({
|
||||
key: 'currentViewSortsScopedFamilyState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const entityCountInCurrentViewComponentState =
|
||||
createComponentState<number>({
|
||||
key: 'entityCountInCurrentViewComponentState',
|
||||
defaultValue: 0,
|
||||
});
|
||||
@ -1,8 +0,0 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const entityCountInCurrentViewScopedState = createComponentState<number>(
|
||||
{
|
||||
key: 'entityCountInCurrentViewScopedState',
|
||||
defaultValue: 0,
|
||||
},
|
||||
);
|
||||
@ -0,0 +1,7 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const isCurrentViewKeyIndexComponentState =
|
||||
createComponentState<boolean>({
|
||||
key: 'isCurrentViewKeyIndexComponentState',
|
||||
defaultValue: true,
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const isPersistingViewFieldsComponentState =
|
||||
createComponentState<boolean>({
|
||||
key: 'isPersistingViewFieldsComponentState',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -1,6 +0,0 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const isPersistingViewScopedState = createComponentState<boolean>({
|
||||
key: 'isPersistingViewScopedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -1,6 +1,6 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const isViewBarExpandedScopedState = createComponentState<boolean>({
|
||||
key: 'isViewBarExpandedScopedState',
|
||||
export const isViewBarExpandedComponentState = createComponentState<boolean>({
|
||||
key: 'isViewBarExpandedComponentState',
|
||||
defaultValue: true,
|
||||
});
|
||||
@ -1,6 +0,0 @@
|
||||
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
|
||||
|
||||
export const noneScopedFamilyState = createComponentFamilyState<any, string>({
|
||||
key: 'noneScopedFamilyState',
|
||||
defaultValue: null,
|
||||
});
|
||||
@ -0,0 +1,9 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
|
||||
export const onCurrentViewChangeComponentState = createComponentState<
|
||||
((view: GraphQLView | undefined) => void | Promise<void>) | undefined
|
||||
>({
|
||||
key: 'onCurrentViewChangeComponentState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -1,8 +0,0 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const onViewCompactModeChangeScopeState = createComponentState<
|
||||
((isCompactModeActive: boolean) => void | Promise<void>) | undefined
|
||||
>({
|
||||
key: 'onViewCompactModeChangeScopeState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -1,10 +0,0 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
import { ViewField } from '../types/ViewField';
|
||||
|
||||
export const onViewFieldsChangeScopedState = createComponentState<
|
||||
((fields: ViewField[]) => void | Promise<void>) | undefined
|
||||
>({
|
||||
key: 'onViewFieldsChangeScopedState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
|
||||
export const onViewFiltersChangeScopedState = createComponentState<
|
||||
((filters: ViewFilter[]) => void | Promise<void>) | undefined
|
||||
>({
|
||||
key: 'onViewFiltersChangeScopedState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
|
||||
export const onViewSortsChangeScopedState = createComponentState<
|
||||
((sorts: ViewSort[]) => void | Promise<void>) | undefined
|
||||
>({
|
||||
key: 'onViewSortsChangeScopedState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
|
||||
export const onViewTypeChangeScopedState = createComponentState<
|
||||
((viewType: ViewType) => void | Promise<void>) | undefined
|
||||
>({
|
||||
key: 'onViewTypeChangeScopedState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -1,11 +0,0 @@
|
||||
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
|
||||
|
||||
import { ViewField } from '../types/ViewField';
|
||||
|
||||
export const savedViewFieldsScopedFamilyState = createComponentFamilyState<
|
||||
ViewField[],
|
||||
string
|
||||
>({
|
||||
key: 'savedViewFieldsScopedFamilyState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,11 +0,0 @@
|
||||
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
|
||||
|
||||
import { ViewFilter } from '../types/ViewFilter';
|
||||
|
||||
export const savedViewFiltersScopedFamilyState = createComponentFamilyState<
|
||||
ViewFilter[],
|
||||
string
|
||||
>({
|
||||
key: 'savedViewFiltersScopedFamilyState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,11 +0,0 @@
|
||||
import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState';
|
||||
|
||||
import { ViewSort } from '../types/ViewSort';
|
||||
|
||||
export const savedViewSortsScopedFamilyState = createComponentFamilyState<
|
||||
ViewSort[],
|
||||
string
|
||||
>({
|
||||
key: 'savedViewSortsScopedFamilyState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -0,0 +1,21 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { unsavedToDeleteViewFilterIdsComponentState } from '@/views/states/unsavedToDeleteViewFilterIdsComponentState';
|
||||
import { unsavedToDeleteViewSortIdsComponentState } from '@/views/states/unsavedToDeleteViewSortIdsComponentState';
|
||||
import { unsavedToUpsertViewFiltersComponentState } from '@/views/states/unsavedToUpsertViewFiltersComponentState';
|
||||
import { unsavedToUpsertViewSortsComponentState } from '@/views/states/unsavedToUpsertViewSortsComponentState';
|
||||
|
||||
export const canPersistViewComponentSelector = selectorFamily({
|
||||
key: 'canPersistViewComponentSelector',
|
||||
get:
|
||||
({ scopeId }: { scopeId: string }) =>
|
||||
({ get }) => {
|
||||
return (
|
||||
get(unsavedToUpsertViewFiltersComponentState({ scopeId })).length > 0 ||
|
||||
get(unsavedToUpsertViewSortsComponentState({ scopeId })).length > 0 ||
|
||||
get(unsavedToDeleteViewFilterIdsComponentState({ scopeId })).length >
|
||||
0 ||
|
||||
get(unsavedToDeleteViewSortIdsComponentState({ scopeId })).length > 0
|
||||
);
|
||||
},
|
||||
});
|
||||
@ -1,32 +0,0 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
import { currentViewFiltersScopedFamilyState } from '../currentViewFiltersScopedFamilyState';
|
||||
import { savedViewFiltersScopedFamilyState } from '../savedViewFiltersScopedFamilyState';
|
||||
|
||||
export const canPersistViewFiltersScopedFamilySelector = selectorFamily({
|
||||
key: 'canPersistFiltersScopedFamilySelector',
|
||||
get:
|
||||
({ viewScopeId, viewId }: { viewScopeId: string; viewId?: string }) =>
|
||||
({ get }) => {
|
||||
if (!viewId) {
|
||||
return;
|
||||
}
|
||||
|
||||
return !isDeeplyEqual(
|
||||
get(
|
||||
savedViewFiltersScopedFamilyState({
|
||||
scopeId: viewScopeId,
|
||||
familyKey: viewId,
|
||||
}),
|
||||
),
|
||||
get(
|
||||
currentViewFiltersScopedFamilyState({
|
||||
scopeId: viewScopeId,
|
||||
familyKey: viewId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
});
|
||||
@ -1,31 +0,0 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
import { currentViewSortsScopedFamilyState } from '../currentViewSortsScopedFamilyState';
|
||||
import { savedViewSortsScopedFamilyState } from '../savedViewSortsScopedFamilyState';
|
||||
|
||||
export const canPersistViewSortsScopedFamilySelector = selectorFamily({
|
||||
key: 'canPersistSortsScopedFamilySelector',
|
||||
get:
|
||||
({ viewScopeId, viewId }: { viewScopeId: string; viewId?: string }) =>
|
||||
({ get }) => {
|
||||
if (!viewId) {
|
||||
return;
|
||||
}
|
||||
return !isDeeplyEqual(
|
||||
get(
|
||||
savedViewSortsScopedFamilyState({
|
||||
scopeId: viewScopeId,
|
||||
familyKey: viewId,
|
||||
}),
|
||||
),
|
||||
get(
|
||||
currentViewSortsScopedFamilyState({
|
||||
scopeId: viewScopeId,
|
||||
familyKey: viewId,
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,22 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { unsavedToDeleteViewFilterIdsComponentState } from '@/views/states/unsavedToDeleteViewFilterIdsComponentState';
|
||||
import { unsavedToDeleteViewSortIdsComponentState } from '@/views/states/unsavedToDeleteViewSortIdsComponentState';
|
||||
import { unsavedToUpsertViewFiltersComponentState } from '@/views/states/unsavedToUpsertViewFiltersComponentState';
|
||||
import { unsavedToUpsertViewSortsComponentState } from '@/views/states/unsavedToUpsertViewSortsComponentState';
|
||||
|
||||
export const canResetViewComponentSelector = selectorFamily({
|
||||
key: 'canResetViewComponentSelector',
|
||||
get:
|
||||
({ scopeId }: { scopeId: string }) =>
|
||||
({ get }) => {
|
||||
return (
|
||||
get(unsavedToUpsertViewFiltersComponentState({ scopeId })).length ===
|
||||
0 &&
|
||||
get(unsavedToUpsertViewSortsComponentState({ scopeId })).length === 0 &&
|
||||
get(unsavedToDeleteViewFilterIdsComponentState({ scopeId })).length ===
|
||||
0 &&
|
||||
get(unsavedToDeleteViewSortIdsComponentState({ scopeId })).length === 0
|
||||
);
|
||||
},
|
||||
});
|
||||
@ -1,21 +0,0 @@
|
||||
import { createComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/createComponentReadOnlySelector';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
|
||||
import { currentViewIdScopedState } from '../currentViewIdScopedState';
|
||||
|
||||
import { viewsByIdScopedSelector } from './viewsByIdScopedSelector';
|
||||
|
||||
export const currentViewComponentSelector = createComponentReadOnlySelector<
|
||||
GraphQLView | undefined
|
||||
>({
|
||||
key: 'currentViewScopedSelector',
|
||||
get:
|
||||
({ scopeId }: { scopeId: string }) =>
|
||||
({ get }) => {
|
||||
const currentViewId = get(currentViewIdScopedState({ scopeId: scopeId }));
|
||||
|
||||
return currentViewId
|
||||
? get(viewsByIdScopedSelector(scopeId))[currentViewId]
|
||||
: undefined;
|
||||
},
|
||||
});
|
||||
@ -1,32 +0,0 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
|
||||
import { savedViewFieldsScopedFamilyState } from '../savedViewFieldsScopedFamilyState';
|
||||
|
||||
export const savedViewFieldByKeyScopedFamilySelector = selectorFamily({
|
||||
key: 'savedViewFieldByKeyScopedFamilySelector',
|
||||
get:
|
||||
({
|
||||
viewScopeId,
|
||||
viewId,
|
||||
}: {
|
||||
viewScopeId: string;
|
||||
viewId: string | undefined;
|
||||
}) =>
|
||||
({ get }) => {
|
||||
if (viewId === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return get(
|
||||
savedViewFieldsScopedFamilyState({
|
||||
scopeId: viewScopeId,
|
||||
familyKey: viewId,
|
||||
}),
|
||||
).reduce<Record<string, ViewField>>(
|
||||
(result, column) => ({ ...result, [column.fieldMetadataId]: column }),
|
||||
{},
|
||||
);
|
||||
},
|
||||
});
|
||||
@ -1,25 +0,0 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
|
||||
import { savedViewFiltersScopedFamilyState } from '../savedViewFiltersScopedFamilyState';
|
||||
|
||||
export const savedViewFiltersByKeyScopedFamilySelector = selectorFamily({
|
||||
key: 'savedViewFiltersByKeyScopedFamilySelector',
|
||||
get:
|
||||
({ scopeId, viewId }: { scopeId: string; viewId: string | undefined }) =>
|
||||
({ get }) => {
|
||||
if (viewId === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return get(
|
||||
savedViewFiltersScopedFamilyState({
|
||||
scopeId: scopeId,
|
||||
familyKey: viewId,
|
||||
}),
|
||||
).reduce<Record<string, ViewFilter>>(
|
||||
(result, filter) => ({ ...result, [filter.fieldMetadataId]: filter }),
|
||||
{},
|
||||
);
|
||||
},
|
||||
});
|
||||
@ -1,25 +0,0 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
|
||||
import { savedViewSortsScopedFamilyState } from '../savedViewSortsScopedFamilyState';
|
||||
|
||||
export const savedViewSortsByKeyScopedFamilySelector = selectorFamily({
|
||||
key: 'savedViewSortsByKeyScopedFamilySelector',
|
||||
get:
|
||||
({ scopeId, viewId }: { scopeId: string; viewId: string | undefined }) =>
|
||||
({ get }) => {
|
||||
if (viewId === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
return get(
|
||||
savedViewSortsScopedFamilyState({
|
||||
scopeId: scopeId,
|
||||
familyKey: viewId,
|
||||
}),
|
||||
).reduce<Record<string, ViewSort>>(
|
||||
(result, sort) => ({ ...result, [sort.fieldMetadataId]: sort }),
|
||||
{},
|
||||
);
|
||||
},
|
||||
});
|
||||
@ -1,16 +0,0 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { savedViewSortsScopedFamilyState } from '../savedViewSortsScopedFamilyState';
|
||||
|
||||
export const savedViewSortsFamilySelector = selectorFamily({
|
||||
key: 'savedViewSortsFamilySelector',
|
||||
get:
|
||||
({ scopeId, viewId }: { scopeId: string; viewId: string }) =>
|
||||
({ get }) =>
|
||||
get(
|
||||
savedViewSortsScopedFamilyState({
|
||||
scopeId: scopeId,
|
||||
familyKey: viewId,
|
||||
}),
|
||||
),
|
||||
});
|
||||
@ -1,18 +0,0 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
|
||||
import { viewsScopedState } from '../viewsScopedState';
|
||||
|
||||
export const viewsByIdScopedSelector = selectorFamily<
|
||||
Record<string, GraphQLView>,
|
||||
string
|
||||
>({
|
||||
key: 'viewsByIdScopedSelector',
|
||||
get:
|
||||
(scopeId) =>
|
||||
({ get }) =>
|
||||
get(viewsScopedState({ scopeId: scopeId })).reduce<
|
||||
Record<string, GraphQLView>
|
||||
>((result, view) => ({ ...result, [view.id]: view }), {}),
|
||||
});
|
||||
@ -0,0 +1,8 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const unsavedToDeleteViewFilterIdsComponentState = createComponentState<
|
||||
string[]
|
||||
>({
|
||||
key: 'unsavedToDeleteViewFilterIdsComponentState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -0,0 +1,8 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const unsavedToDeleteViewSortIdsComponentState = createComponentState<
|
||||
string[]
|
||||
>({
|
||||
key: 'unsavedToDeleteViewSortIdsComponentState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -0,0 +1,10 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
import { ViewFilter } from '../types/ViewFilter';
|
||||
|
||||
export const unsavedToUpsertViewFiltersComponentState = createComponentState<
|
||||
ViewFilter[]
|
||||
>({
|
||||
key: 'unsavedToUpsertViewFiltersComponentState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -0,0 +1,10 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
import { ViewSort } from '../types/ViewSort';
|
||||
|
||||
export const unsavedToUpsertViewSortsComponentState = createComponentState<
|
||||
ViewSort[]
|
||||
>({
|
||||
key: 'unsavedToUpsertViewSortsComponentState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,8 +1,8 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const viewEditModeScopedState = createComponentState<
|
||||
export const viewEditModeComponentState = createComponentState<
|
||||
'none' | 'edit' | 'create'
|
||||
>({
|
||||
key: 'viewEditModeScopedState',
|
||||
key: 'viewEditModeComponentState',
|
||||
defaultValue: 'none',
|
||||
});
|
||||
@ -1,8 +1,8 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const viewObjectMetadataIdScopeState = createComponentState<
|
||||
export const viewObjectMetadataIdComponentState = createComponentState<
|
||||
string | undefined
|
||||
>({
|
||||
key: 'viewObjectMetadataIdScopeState',
|
||||
key: 'viewObjectMetadataIdComponentState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -1,8 +0,0 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
import { ViewType } from '../types/ViewType';
|
||||
|
||||
export const viewTypeScopedState = createComponentState<ViewType>({
|
||||
key: 'viewTypeScopedState',
|
||||
defaultValue: ViewType.Table,
|
||||
});
|
||||
@ -1,7 +0,0 @@
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
|
||||
export const viewsScopedState = createComponentState<GraphQLView[]>({
|
||||
key: 'viewsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -3,6 +3,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
|
||||
export type ViewField = {
|
||||
__typename: 'ViewField';
|
||||
id: string;
|
||||
fieldMetadataId: string;
|
||||
position: number;
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
|
||||
|
||||
import { ViewFilterOperand } from './ViewFilterOperand';
|
||||
|
||||
export type ViewFilter = {
|
||||
__typename: 'ViewFilter';
|
||||
id: string;
|
||||
fieldMetadataId: string;
|
||||
operand: ViewFilterOperand;
|
||||
value: string;
|
||||
displayValue: string;
|
||||
definition: FilterDefinition;
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
viewId?: string;
|
||||
};
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition';
|
||||
import { SortDirection } from '@/object-record/object-sort-dropdown/types/SortDirection';
|
||||
|
||||
export type ViewSort = {
|
||||
__typename: 'ViewSort';
|
||||
id: string;
|
||||
fieldMetadataId: string;
|
||||
direction: SortDirection;
|
||||
definition: SortDefinition;
|
||||
};
|
||||
|
||||
@ -12,7 +12,7 @@ import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
|
||||
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
|
||||
|
||||
const baseDefinition = {
|
||||
fieldMetadataId: 'fieldMetadataId',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
label: 'label',
|
||||
iconName: 'iconName',
|
||||
};
|
||||
@ -21,20 +21,22 @@ describe('mapViewSortsToSorts', () => {
|
||||
it('should map each ViewSort object to a corresponding Sort object', () => {
|
||||
const viewSorts: ViewSort[] = [
|
||||
{
|
||||
__typename: 'ViewSort',
|
||||
id: 'id',
|
||||
fieldMetadataId: 'fieldMetadataId',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
direction: 'asc',
|
||||
definition: baseDefinition,
|
||||
},
|
||||
];
|
||||
const expectedSorts: Sort[] = [
|
||||
{
|
||||
fieldMetadataId: 'fieldMetadataId',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
direction: 'asc',
|
||||
definition: baseDefinition,
|
||||
},
|
||||
];
|
||||
expect(mapViewSortsToSorts(viewSorts)).toEqual(expectedSorts);
|
||||
expect(mapViewSortsToSorts(viewSorts, [baseDefinition])).toEqual(
|
||||
expectedSorts,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -42,20 +44,17 @@ describe('mapViewFiltersToFilters', () => {
|
||||
it('should map each ViewFilter object to a corresponding Filter object', () => {
|
||||
const viewFilters: ViewFilter[] = [
|
||||
{
|
||||
__typename: 'ViewFilter',
|
||||
id: 'id',
|
||||
fieldMetadataId: '1',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
value: 'testValue',
|
||||
displayValue: 'Test Display Value',
|
||||
operand: ViewFilterOperand.Is,
|
||||
definition: {
|
||||
...baseDefinition,
|
||||
type: 'FULL_NAME',
|
||||
},
|
||||
},
|
||||
];
|
||||
const expectedFilters: Filter[] = [
|
||||
{
|
||||
fieldMetadataId: '1',
|
||||
fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482',
|
||||
value: 'testValue',
|
||||
displayValue: 'Test Display Value',
|
||||
operand: ViewFilterOperand.Is,
|
||||
@ -65,7 +64,14 @@ describe('mapViewFiltersToFilters', () => {
|
||||
},
|
||||
},
|
||||
];
|
||||
expect(mapViewFiltersToFilters(viewFilters)).toEqual(expectedFilters);
|
||||
expect(
|
||||
mapViewFiltersToFilters(viewFilters, [
|
||||
{
|
||||
...baseDefinition,
|
||||
type: 'FULL_NAME',
|
||||
},
|
||||
]),
|
||||
).toEqual(expectedFilters);
|
||||
});
|
||||
});
|
||||
|
||||
@ -73,6 +79,7 @@ describe('mapViewFieldsToColumnDefinitions', () => {
|
||||
it('should map visible ViewFields to ColumnDefinitions and filter out missing fieldMetadata', () => {
|
||||
const viewFields: ViewField[] = [
|
||||
{
|
||||
__typename: 'ViewField',
|
||||
id: '1',
|
||||
fieldMetadataId: '1',
|
||||
position: 1,
|
||||
@ -92,6 +99,7 @@ describe('mapViewFieldsToColumnDefinitions', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'ViewField',
|
||||
id: '2',
|
||||
fieldMetadataId: '2',
|
||||
position: 2,
|
||||
@ -111,6 +119,7 @@ describe('mapViewFieldsToColumnDefinitions', () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'ViewField',
|
||||
id: '3',
|
||||
fieldMetadataId: '3',
|
||||
position: 3,
|
||||
@ -209,13 +218,16 @@ describe('mapColumnDefinitionsToViewFields', () => {
|
||||
|
||||
const expectedViewFields = [
|
||||
{
|
||||
__typename: 'ViewField',
|
||||
id: 'custom-id-1',
|
||||
fieldMetadataId: 1,
|
||||
position: 1,
|
||||
isVisible: true,
|
||||
definition: columnDefinitions[0],
|
||||
size: undefined,
|
||||
},
|
||||
{
|
||||
__typename: 'ViewField',
|
||||
id: '',
|
||||
fieldMetadataId: 2,
|
||||
position: 2,
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
|
||||
export const combinedViewFilters = (
|
||||
viewFilter: ViewFilter[],
|
||||
toUpsertViewFilters: ViewFilter[],
|
||||
toDeleteViewFilterIds: string[],
|
||||
): ViewFilter[] => {
|
||||
const toCreateViewFilters = toUpsertViewFilters.filter(
|
||||
(toUpsertViewFilter) =>
|
||||
!viewFilter.some((viewFilter) => viewFilter.id === toUpsertViewFilter.id),
|
||||
);
|
||||
|
||||
const toUpdateViewFilters = toUpsertViewFilters.filter((toUpsertViewFilter) =>
|
||||
viewFilter.some((viewFilter) => viewFilter.id === toUpsertViewFilter.id),
|
||||
);
|
||||
|
||||
const combinedViewFilters = viewFilter
|
||||
.filter((viewFilter) => !toDeleteViewFilterIds.includes(viewFilter.id))
|
||||
.map((viewFilter) => {
|
||||
const toUpdateViewFilter = toUpdateViewFilters.find(
|
||||
(toUpdateViewFilter) => toUpdateViewFilter.id === viewFilter.id,
|
||||
);
|
||||
|
||||
return toUpdateViewFilter ?? viewFilter;
|
||||
})
|
||||
.concat(toCreateViewFilters);
|
||||
|
||||
return Object.values(
|
||||
combinedViewFilters.reduce(
|
||||
(acc, obj) => ({ ...acc, [obj.fieldMetadataId]: obj }),
|
||||
{},
|
||||
),
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,34 @@
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
|
||||
export const combinedViewSorts = (
|
||||
viewSort: ViewSort[],
|
||||
toUpsertViewSorts: ViewSort[],
|
||||
toDeleteViewSortIds: string[],
|
||||
): ViewSort[] => {
|
||||
const toCreateViewSorts = toUpsertViewSorts.filter(
|
||||
(toUpsertViewSort) =>
|
||||
!viewSort.some((viewSort) => viewSort.id === toUpsertViewSort.id),
|
||||
);
|
||||
|
||||
const toUpdateViewSorts = toUpsertViewSorts.filter((toUpsertViewSort) =>
|
||||
viewSort.some((viewSort) => viewSort.id === toUpsertViewSort.id),
|
||||
);
|
||||
|
||||
const combinedViewSorts = viewSort
|
||||
.filter((viewSort) => !toDeleteViewSortIds.includes(viewSort.id))
|
||||
.map((viewSort) => {
|
||||
const toUpdateViewSort = toUpdateViewSorts.find(
|
||||
(toUpdateViewSort) => toUpdateViewSort.id === viewSort.id,
|
||||
);
|
||||
|
||||
return toUpdateViewSort ?? viewSort;
|
||||
})
|
||||
.concat(toCreateViewSorts);
|
||||
|
||||
return Object.values(
|
||||
combinedViewSorts.reduce(
|
||||
(acc, obj) => ({ ...acc, [obj.fieldMetadataId]: obj }),
|
||||
{},
|
||||
),
|
||||
);
|
||||
};
|
||||
@ -1,115 +0,0 @@
|
||||
import { Snapshot } from 'recoil';
|
||||
|
||||
import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
|
||||
import { UNDEFINED_FAMILY_ITEM_ID } from '../constants';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
|
||||
import { getViewScopedStates } from './internal/getViewScopedStates';
|
||||
|
||||
export const getViewScopedStateValuesFromSnapshot = ({
|
||||
snapshot,
|
||||
viewScopeId,
|
||||
viewId,
|
||||
}: {
|
||||
snapshot: Snapshot;
|
||||
viewScopeId: string;
|
||||
viewId?: string;
|
||||
}) => {
|
||||
const currentViewId = getSnapshotValue(
|
||||
snapshot,
|
||||
getScopedStateDeprecated(currentViewIdScopedState, viewScopeId),
|
||||
);
|
||||
|
||||
const familyItemId = viewId ?? currentViewId ?? UNDEFINED_FAMILY_ITEM_ID;
|
||||
|
||||
const {
|
||||
availableFieldDefinitionsState,
|
||||
availableFilterDefinitionsState,
|
||||
availableSortDefinitionsState,
|
||||
canPersistFiltersSelector,
|
||||
canPersistSortsSelector,
|
||||
currentViewFieldsState,
|
||||
currentViewFiltersState,
|
||||
currentViewIdState,
|
||||
currentViewSelector,
|
||||
currentViewSortsState,
|
||||
entityCountInCurrentViewState,
|
||||
isViewBarExpandedState,
|
||||
isPersistingViewState,
|
||||
onViewFieldsChangeState,
|
||||
onViewFiltersChangeState,
|
||||
onViewSortsChangeState,
|
||||
onViewTypeChangeState,
|
||||
onViewCompactModeChangeState,
|
||||
savedViewFieldsByKeySelector,
|
||||
savedViewFieldsState,
|
||||
savedViewFiltersByKeySelector,
|
||||
savedViewFiltersState,
|
||||
savedViewSortsByKeySelector,
|
||||
savedViewSortsState,
|
||||
viewEditModeState,
|
||||
viewObjectMetadataIdState,
|
||||
viewTypeState,
|
||||
viewsState,
|
||||
} = getViewScopedStates({
|
||||
viewScopeId,
|
||||
viewId: familyItemId,
|
||||
});
|
||||
|
||||
return {
|
||||
availableFieldDefinitions: getSnapshotValue(
|
||||
snapshot,
|
||||
availableFieldDefinitionsState,
|
||||
),
|
||||
availableFilterDefinitions: getSnapshotValue(
|
||||
snapshot,
|
||||
availableFilterDefinitionsState,
|
||||
),
|
||||
availableSortDefinitions: getSnapshotValue(
|
||||
snapshot,
|
||||
availableSortDefinitionsState,
|
||||
),
|
||||
canPersistFilters: getSnapshotValue(snapshot, canPersistFiltersSelector),
|
||||
canPersistSorts: getSnapshotValue(snapshot, canPersistSortsSelector),
|
||||
currentViewFields: getSnapshotValue(snapshot, currentViewFieldsState),
|
||||
currentViewFilters: getSnapshotValue(snapshot, currentViewFiltersState),
|
||||
currentViewId: getSnapshotValue(snapshot, currentViewIdState),
|
||||
currentView: getSnapshotValue(snapshot, currentViewSelector),
|
||||
currentViewSorts: getSnapshotValue(snapshot, currentViewSortsState),
|
||||
entityCountInCurrentView: getSnapshotValue(
|
||||
snapshot,
|
||||
entityCountInCurrentViewState,
|
||||
),
|
||||
isViewBarExpanded: getSnapshotValue(snapshot, isViewBarExpandedState),
|
||||
isPersistingView: getSnapshotValue(snapshot, isPersistingViewState),
|
||||
onViewFieldsChange: getSnapshotValue(snapshot, onViewFieldsChangeState),
|
||||
onViewFiltersChange: getSnapshotValue(snapshot, onViewFiltersChangeState),
|
||||
onViewSortsChange: getSnapshotValue(snapshot, onViewSortsChangeState),
|
||||
onViewTypeChange: getSnapshotValue(snapshot, onViewTypeChangeState),
|
||||
onViewCompactModeChange: getSnapshotValue(
|
||||
snapshot,
|
||||
onViewCompactModeChangeState,
|
||||
),
|
||||
savedViewFieldsByKey: getSnapshotValue(
|
||||
snapshot,
|
||||
savedViewFieldsByKeySelector,
|
||||
),
|
||||
savedViewFields: getSnapshotValue(snapshot, savedViewFieldsState),
|
||||
savedViewFiltersByKey: getSnapshotValue(
|
||||
snapshot,
|
||||
savedViewFiltersByKeySelector,
|
||||
),
|
||||
savedViewFilters: getSnapshotValue(snapshot, savedViewFiltersState),
|
||||
savedViewSortsByKey: getSnapshotValue(
|
||||
snapshot,
|
||||
savedViewSortsByKeySelector,
|
||||
),
|
||||
savedViewSorts: getSnapshotValue(snapshot, savedViewSortsState),
|
||||
viewEditMode: getSnapshotValue(snapshot, viewEditModeState),
|
||||
viewObjectMetadataId: getSnapshotValue(snapshot, viewObjectMetadataIdState),
|
||||
viewType: getSnapshotValue(snapshot, viewTypeState),
|
||||
views: getSnapshotValue(snapshot, viewsState),
|
||||
};
|
||||
};
|
||||
@ -1,87 +0,0 @@
|
||||
import { Snapshot } from 'recoil';
|
||||
|
||||
import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
|
||||
import { UNDEFINED_FAMILY_ITEM_ID } from '../constants';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
|
||||
import { getViewScopedStates } from './internal/getViewScopedStates';
|
||||
|
||||
export const getViewScopedStatesFromSnapshot = ({
|
||||
snapshot,
|
||||
viewScopeId,
|
||||
viewId,
|
||||
}: {
|
||||
snapshot: Snapshot;
|
||||
viewScopeId: string;
|
||||
viewId?: string;
|
||||
}) => {
|
||||
const currentViewId = getSnapshotValue(
|
||||
snapshot,
|
||||
getScopedStateDeprecated(currentViewIdScopedState, viewScopeId),
|
||||
);
|
||||
|
||||
const usedViewId = 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,
|
||||
viewId: usedViewId,
|
||||
});
|
||||
|
||||
return {
|
||||
availableFieldDefinitionsState,
|
||||
availableFilterDefinitionsState,
|
||||
availableSortDefinitionsState,
|
||||
canPersistFiltersReadOnlyState: canPersistFiltersSelector,
|
||||
canPersistSortsReadOnlyState: canPersistSortsSelector,
|
||||
currentViewFieldsState,
|
||||
currentViewFiltersState,
|
||||
currentViewIdState,
|
||||
currentViewSelector,
|
||||
currentViewSortsState,
|
||||
entityCountInCurrentViewState,
|
||||
isViewBarExpandedState,
|
||||
isPersistingViewState,
|
||||
onViewFieldsChangeState,
|
||||
onViewFiltersChangeState,
|
||||
onViewSortsChangeState,
|
||||
savedViewFieldsByKeyReadOnlyState: savedViewFieldsByKeySelector,
|
||||
savedViewFieldsState,
|
||||
savedViewFiltersByKeyReadOnlyState: savedViewFiltersByKeySelector,
|
||||
savedViewFiltersState,
|
||||
savedViewSortsByKeyReadOnlyState: savedViewSortsByKeySelector,
|
||||
savedViewSortsState,
|
||||
viewEditModeState,
|
||||
viewObjectMetadataIdState,
|
||||
viewTypeState,
|
||||
viewsState,
|
||||
};
|
||||
};
|
||||
@ -1,225 +0,0 @@
|
||||
import { getScopedFamilyStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedFamilyStateDeprecated';
|
||||
import { getScopedSelectorDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedSelectorDeprecated';
|
||||
import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated';
|
||||
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
|
||||
import { isPersistingViewScopedState } from '@/views/states/isPersistingViewScopedState';
|
||||
import { onViewCompactModeChangeScopeState } from '@/views/states/onViewCompactModeChangeScopeState';
|
||||
import { onViewTypeChangeScopedState } from '@/views/states/onViewTypeChangeScopedState';
|
||||
import { currentViewComponentSelector } from '@/views/states/selectors/currentViewComponentSelector';
|
||||
import { savedViewFieldByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewFieldByKeyScopedFamilySelector';
|
||||
|
||||
import { availableFieldDefinitionsScopedState } from '../../states/availableFieldDefinitionsScopedState';
|
||||
import { availableFilterDefinitionsScopedState } from '../../states/availableFilterDefinitionsScopedState';
|
||||
import { availableSortDefinitionsScopedState } from '../../states/availableSortDefinitionsScopedState';
|
||||
import { currentViewFieldsScopedFamilyState } from '../../states/currentViewFieldsScopedFamilyState';
|
||||
import { currentViewFiltersScopedFamilyState } from '../../states/currentViewFiltersScopedFamilyState';
|
||||
import { currentViewSortsScopedFamilyState } from '../../states/currentViewSortsScopedFamilyState';
|
||||
import { entityCountInCurrentViewScopedState } from '../../states/entityCountInCurrentViewScopedState';
|
||||
import { isViewBarExpandedScopedState } from '../../states/isViewBarExpandedScopedState';
|
||||
import { onViewFieldsChangeScopedState } from '../../states/onViewFieldsChangeScopedState';
|
||||
import { onViewFiltersChangeScopedState } from '../../states/onViewFiltersChangeScopedState';
|
||||
import { onViewSortsChangeScopedState } from '../../states/onViewSortsChangeScopedState';
|
||||
import { savedViewFieldsScopedFamilyState } from '../../states/savedViewFieldsScopedFamilyState';
|
||||
import { savedViewFiltersScopedFamilyState } from '../../states/savedViewFiltersScopedFamilyState';
|
||||
import { savedViewSortsScopedFamilyState } from '../../states/savedViewSortsScopedFamilyState';
|
||||
import { canPersistViewFiltersScopedFamilySelector } from '../../states/selectors/canPersistViewFiltersScopedFamilySelector';
|
||||
import { canPersistViewSortsScopedFamilySelector } from '../../states/selectors/canPersistViewSortsScopedFamilySelector';
|
||||
import { savedViewFiltersByKeyScopedFamilySelector } from '../../states/selectors/savedViewFiltersByKeyScopedFamilySelector';
|
||||
import { savedViewSortsByKeyScopedFamilySelector } from '../../states/selectors/savedViewSortsByKeyScopedFamilySelector';
|
||||
import { viewEditModeScopedState } from '../../states/viewEditModeScopedState';
|
||||
import { viewObjectMetadataIdScopeState } from '../../states/viewObjectMetadataIdScopeState';
|
||||
import { viewsScopedState } from '../../states/viewsScopedState';
|
||||
import { viewTypeScopedState } from '../../states/viewTypeScopedState';
|
||||
|
||||
export const getViewScopedStates = ({
|
||||
viewScopeId,
|
||||
viewId,
|
||||
}: {
|
||||
viewScopeId: string;
|
||||
viewId: string;
|
||||
}) => {
|
||||
const viewEditModeState = getScopedStateDeprecated(
|
||||
viewEditModeScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const viewsState = getScopedStateDeprecated(viewsScopedState, viewScopeId);
|
||||
|
||||
const viewObjectMetadataIdState = getScopedStateDeprecated(
|
||||
viewObjectMetadataIdScopeState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const viewTypeState = getScopedStateDeprecated(
|
||||
viewTypeScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const entityCountInCurrentViewState = getScopedStateDeprecated(
|
||||
entityCountInCurrentViewScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const isViewBarExpandedState = getScopedStateDeprecated(
|
||||
isViewBarExpandedScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const isPersistingViewState = getScopedStateDeprecated(
|
||||
isPersistingViewScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
// ViewSorts
|
||||
const currentViewSortsState = getScopedFamilyStateDeprecated(
|
||||
currentViewSortsScopedFamilyState,
|
||||
viewScopeId,
|
||||
viewId,
|
||||
);
|
||||
|
||||
const savedViewSortsState = getScopedFamilyStateDeprecated(
|
||||
savedViewSortsScopedFamilyState,
|
||||
viewScopeId,
|
||||
viewId,
|
||||
);
|
||||
|
||||
const savedViewSortsByKeySelector = savedViewSortsByKeyScopedFamilySelector({
|
||||
scopeId: viewScopeId,
|
||||
viewId: viewId,
|
||||
});
|
||||
|
||||
const availableSortDefinitionsState = getScopedStateDeprecated(
|
||||
availableSortDefinitionsScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const canPersistSortsSelector = canPersistViewSortsScopedFamilySelector({
|
||||
viewScopeId: viewScopeId,
|
||||
viewId: viewId,
|
||||
});
|
||||
|
||||
// ViewFilters
|
||||
const currentViewFiltersState = getScopedFamilyStateDeprecated(
|
||||
currentViewFiltersScopedFamilyState,
|
||||
viewScopeId,
|
||||
viewId,
|
||||
);
|
||||
|
||||
const savedViewFiltersState = getScopedFamilyStateDeprecated(
|
||||
savedViewFiltersScopedFamilyState,
|
||||
viewScopeId,
|
||||
viewId,
|
||||
);
|
||||
|
||||
const savedViewFiltersByKeySelector =
|
||||
savedViewFiltersByKeyScopedFamilySelector({
|
||||
scopeId: viewScopeId,
|
||||
viewId: viewId,
|
||||
});
|
||||
|
||||
const availableFilterDefinitionsState = getScopedStateDeprecated(
|
||||
availableFilterDefinitionsScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const canPersistFiltersSelector = canPersistViewFiltersScopedFamilySelector({
|
||||
viewScopeId: viewScopeId,
|
||||
viewId: viewId,
|
||||
});
|
||||
|
||||
// ViewFields
|
||||
const availableFieldDefinitionsState = getScopedStateDeprecated(
|
||||
availableFieldDefinitionsScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const currentViewFieldsState = getScopedFamilyStateDeprecated(
|
||||
currentViewFieldsScopedFamilyState,
|
||||
viewScopeId,
|
||||
viewId,
|
||||
);
|
||||
|
||||
const savedViewFieldsState = getScopedFamilyStateDeprecated(
|
||||
savedViewFieldsScopedFamilyState,
|
||||
viewScopeId,
|
||||
viewId,
|
||||
);
|
||||
|
||||
const savedViewFieldsByKeySelector = savedViewFieldByKeyScopedFamilySelector({
|
||||
viewScopeId,
|
||||
viewId,
|
||||
});
|
||||
|
||||
// ViewChangeHandlers
|
||||
const onViewSortsChangeState = getScopedStateDeprecated(
|
||||
onViewSortsChangeScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const onViewFiltersChangeState = getScopedStateDeprecated(
|
||||
onViewFiltersChangeScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const onViewFieldsChangeState = getScopedStateDeprecated(
|
||||
onViewFieldsChangeScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const onViewTypeChangeState = getScopedStateDeprecated(
|
||||
onViewTypeChangeScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const onViewCompactModeChangeState = getScopedStateDeprecated(
|
||||
onViewCompactModeChangeScopeState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const currentViewIdState = getScopedStateDeprecated(
|
||||
currentViewIdScopedState,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
const currentViewSelector = getScopedSelectorDeprecated(
|
||||
currentViewComponentSelector,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
return {
|
||||
currentViewIdState,
|
||||
currentViewSelector,
|
||||
|
||||
isViewBarExpandedState,
|
||||
isPersistingViewState,
|
||||
|
||||
viewsState,
|
||||
viewEditModeState,
|
||||
viewObjectMetadataIdState,
|
||||
viewTypeState,
|
||||
entityCountInCurrentViewState,
|
||||
|
||||
availableSortDefinitionsState,
|
||||
currentViewSortsState,
|
||||
savedViewSortsState,
|
||||
savedViewSortsByKeySelector,
|
||||
canPersistSortsSelector,
|
||||
|
||||
availableFilterDefinitionsState,
|
||||
currentViewFiltersState,
|
||||
savedViewFiltersState,
|
||||
savedViewFiltersByKeySelector,
|
||||
canPersistFiltersSelector,
|
||||
|
||||
availableFieldDefinitionsState,
|
||||
currentViewFieldsState,
|
||||
savedViewFieldsByKeySelector,
|
||||
savedViewFieldsState,
|
||||
|
||||
onViewSortsChangeState,
|
||||
onViewFiltersChangeState,
|
||||
onViewFieldsChangeState,
|
||||
onViewTypeChangeState,
|
||||
onViewCompactModeChangeState,
|
||||
};
|
||||
};
|
||||
@ -1,5 +1,3 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
@ -9,7 +7,8 @@ export const mapBoardFieldDefinitionsToViewFields = (
|
||||
): ViewField[] => {
|
||||
return fieldsDefinitions.map(
|
||||
(fieldDefinition): ViewField => ({
|
||||
id: fieldDefinition.viewFieldId || v4(),
|
||||
__typename: 'ViewField',
|
||||
id: fieldDefinition.viewFieldId || '',
|
||||
fieldMetadataId: fieldDefinition.fieldMetadataId,
|
||||
size: 0,
|
||||
position: fieldDefinition.position,
|
||||
|
||||
@ -7,6 +7,7 @@ export const mapColumnDefinitionsToViewFields = (
|
||||
columnDefinitions: ColumnDefinition<FieldMetadata>[],
|
||||
): ViewField[] => {
|
||||
return columnDefinitions.map((columnDefinition) => ({
|
||||
__typename: 'ViewField',
|
||||
id: columnDefinition.viewFieldId || '',
|
||||
fieldMetadataId: columnDefinition.fieldMetadataId,
|
||||
position: columnDefinition.position,
|
||||
|
||||
@ -1,17 +1,29 @@
|
||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
import { ViewFilter } from '../types/ViewFilter';
|
||||
|
||||
export const mapViewFiltersToFilters = (
|
||||
viewFilters: ViewFilter[],
|
||||
availableFilterDefinitions: FilterDefinition[],
|
||||
): Filter[] => {
|
||||
return viewFilters.map((viewFilter) => {
|
||||
return {
|
||||
fieldMetadataId: viewFilter.fieldMetadataId,
|
||||
value: viewFilter.value,
|
||||
displayValue: viewFilter.displayValue,
|
||||
operand: viewFilter.operand,
|
||||
definition: viewFilter.definition,
|
||||
};
|
||||
});
|
||||
return viewFilters
|
||||
.map((viewFilter) => {
|
||||
const availableFilterDefinition = availableFilterDefinitions.find(
|
||||
(filterDefinition) =>
|
||||
filterDefinition.fieldMetadataId === viewFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
if (!availableFilterDefinition) return null;
|
||||
|
||||
return {
|
||||
fieldMetadataId: viewFilter.fieldMetadataId,
|
||||
value: viewFilter.value,
|
||||
displayValue: viewFilter.displayValue,
|
||||
operand: viewFilter.operand,
|
||||
definition: availableFilterDefinition,
|
||||
};
|
||||
})
|
||||
.filter(isDefined);
|
||||
};
|
||||
|
||||
@ -1,13 +1,26 @@
|
||||
import { Sort } from '@/object-record/object-sort-dropdown/types/Sort';
|
||||
import { SortDefinition } from '@/object-record/object-sort-dropdown/types/SortDefinition';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
import { ViewSort } from '../types/ViewSort';
|
||||
|
||||
export const mapViewSortsToSorts = (viewSorts: ViewSort[]): Sort[] => {
|
||||
return viewSorts.map((viewSort) => {
|
||||
return {
|
||||
fieldMetadataId: viewSort.fieldMetadataId,
|
||||
direction: viewSort.direction,
|
||||
definition: viewSort.definition,
|
||||
};
|
||||
});
|
||||
export const mapViewSortsToSorts = (
|
||||
viewSorts: ViewSort[],
|
||||
availableSortDefinitions: SortDefinition[],
|
||||
): Sort[] => {
|
||||
return viewSorts
|
||||
.map((viewSort) => {
|
||||
const availableSortDefinition = availableSortDefinitions.find(
|
||||
(sortDefinition) =>
|
||||
sortDefinition.fieldMetadataId === viewSort.fieldMetadataId,
|
||||
);
|
||||
|
||||
if (!availableSortDefinition) return null;
|
||||
return {
|
||||
fieldMetadataId: viewSort.fieldMetadataId,
|
||||
direction: viewSort.direction,
|
||||
definition: availableSortDefinition,
|
||||
};
|
||||
})
|
||||
.filter(isDefined);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user