Complete Fix view work (#2272)

* Fix views

* Make view sorts and view filters functional

* Complete Company table view fix

* Fix model creation

* Start fixing board

* Complete work
This commit is contained in:
Charles Bochet
2023-10-29 16:29:00 +01:00
committed by GitHub
parent 685d342170
commit 9bab28912d
118 changed files with 1806 additions and 1413 deletions

View File

@ -4,17 +4,13 @@ import { PageAddButton } from '@/ui/layout/page/PageAddButton';
import { ActivityType } from '~/generated/graphql'; import { ActivityType } from '~/generated/graphql';
export const PageAddTaskButton = () => { export const PageAddTaskButton = () => {
const { selectedFilters } = useFilter(); const { selectedFilter } = useFilter();
const openCreateActivity = useOpenCreateActivityDrawer(); const openCreateActivity = useOpenCreateActivityDrawer();
const assigneeIdFilter = selectedFilters.find(
(filter) => filter.key === 'assigneeId',
);
const handleClick = () => { const handleClick = () => {
openCreateActivity({ openCreateActivity({
type: ActivityType.Task, type: ActivityType.Task,
assigneeId: assigneeIdFilter?.value, assigneeId: selectedFilter?.value,
}); });
}; };

View File

@ -17,12 +17,14 @@ export const useCurrentUserTaskCount = () => {
completedAt: { equals: null }, completedAt: { equals: null },
...(currentUser ...(currentUser
? turnFilterIntoWhereClause({ ? turnFilterIntoWhereClause({
key: 'assigneeId', fieldId: 'assigneeId',
type: 'entity',
value: currentUser.id, value: currentUser.id,
operand: ViewFilterOperand.Is, operand: ViewFilterOperand.Is,
displayValue: currentUser.displayName, displayValue: currentUser.displayName,
displayAvatarUrl: currentUser.avatarUrl ?? undefined, displayAvatarUrl: currentUser.avatarUrl ?? undefined,
definition: {
type: 'entity',
},
}) })
: {}), : {}),
}, },

View File

@ -7,7 +7,7 @@ import { ActivityType, useGetActivitiesQuery } from '~/generated/graphql';
import { parseDate } from '~/utils/date-utils'; import { parseDate } from '~/utils/date-utils';
export const useTasks = (entity?: ActivityTargetableEntity) => { export const useTasks = (entity?: ActivityTargetableEntity) => {
const { selectedFilters } = useFilter(); const { selectedFilter } = useFilter();
const whereFilters = entity const whereFilters = entity
? { ? {
@ -20,12 +20,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
}, },
}, },
} }
: Object.assign( : Object.assign({}, turnFilterIntoWhereClause(selectedFilter));
{},
...selectedFilters.map((filter) => {
return turnFilterIntoWhereClause(filter);
}),
);
const { data: completeTasksData } = useGetActivitiesQuery({ const { data: completeTasksData } = useGetActivitiesQuery({
variables: { variables: {
@ -35,7 +30,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
...whereFilters, ...whereFilters,
}, },
}, },
skip: !entity && selectedFilters.length === 0, skip: !entity && !selectedFilter,
}); });
const { data: incompleteTaskData } = useGetActivitiesQuery({ const { data: incompleteTaskData } = useGetActivitiesQuery({
@ -46,7 +41,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
...whereFilters, ...whereFilters,
}, },
}, },
skip: !entity && selectedFilters.length === 0, skip: !entity && !selectedFilter,
}); });
const todayOrPreviousTasks = incompleteTaskData?.findManyActivities.filter( const todayOrPreviousTasks = incompleteTaskData?.findManyActivities.filter(

View File

@ -1,15 +1,29 @@
import styled from '@emotion/styled';
import { BoardContext } from '@/companies/states/contexts/BoardContext'; import { BoardContext } from '@/companies/states/contexts/BoardContext';
import { BoardOptionsDropdown } from '@/ui/layout/board/components/BoardOptionsDropdown';
import { BoardOptionsDropdownId } from '@/ui/layout/board/components/constants/BoardOptionsDropdownId';
import { import {
EntityBoard, EntityBoard,
EntityBoardProps, EntityBoardProps,
} from '@/ui/layout/board/components/EntityBoard'; } from '@/ui/layout/board/components/EntityBoard';
import { EntityBoardActionBar } from '@/ui/layout/board/components/EntityBoardActionBar'; import { EntityBoardActionBar } from '@/ui/layout/board/components/EntityBoardActionBar';
import { EntityBoardContextMenu } from '@/ui/layout/board/components/EntityBoardContextMenu'; import { EntityBoardContextMenu } from '@/ui/layout/board/components/EntityBoardContextMenu';
import { ViewBar } from '@/views/components/ViewBar';
import { ViewScope } from '@/views/scopes/ViewScope';
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions'; import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
import { HooksCompanyBoardEffect } from '../../components/HooksCompanyBoardEffect'; import { HooksCompanyBoardEffect } from '../../components/HooksCompanyBoardEffect';
import { CompanyBoardRecoilScopeContext } from '../../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext'; import { CompanyBoardRecoilScopeContext } from '../../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
overflow: auto;
width: 100%;
`;
type CompanyBoardProps = Pick< type CompanyBoardProps = Pick<
EntityBoardProps, EntityBoardProps,
'onColumnAdd' | 'onColumnDelete' | 'onEditColumnTitle' 'onColumnAdd' | 'onColumnDelete' | 'onEditColumnTitle'
@ -20,24 +34,30 @@ export const CompanyBoard = ({
onColumnDelete, onColumnDelete,
onEditColumnTitle, onEditColumnTitle,
}: CompanyBoardProps) => { }: CompanyBoardProps) => {
const viewScopeId = 'company-board-view';
return ( return (
<> <ViewScope viewScopeId={viewScopeId}>
<BoardContext.Provider <StyledContainer>
value={{ <BoardContext.Provider
BoardRecoilScopeContext: CompanyBoardRecoilScopeContext, value={{
}} BoardRecoilScopeContext: CompanyBoardRecoilScopeContext,
> }}
<HooksCompanyBoardEffect /> >
<ViewBar
<EntityBoard optionsDropdownButton={<BoardOptionsDropdown />}
boardOptions={opportunitiesBoardOptions} optionsDropdownScopeId={BoardOptionsDropdownId}
onColumnAdd={onColumnAdd} />
onColumnDelete={onColumnDelete} <HooksCompanyBoardEffect />
onEditColumnTitle={onEditColumnTitle} <EntityBoard
/> boardOptions={opportunitiesBoardOptions}
<EntityBoardActionBar /> onColumnAdd={onColumnAdd}
<EntityBoardContextMenu /> onColumnDelete={onColumnDelete}
</BoardContext.Provider> onEditColumnTitle={onEditColumnTitle}
</> />
<EntityBoardActionBar />
<EntityBoardContextMenu />
</BoardContext.Provider>
</StyledContainer>
</ViewScope>
); );
}; };

View File

@ -2,12 +2,19 @@ import { useEffect, useMemo } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
import { turnFilterIntoWhereClause } from '@/ui/data/filter/utils/turnFilterIntoWhereClause'; import { turnFilterIntoWhereClause } from '@/ui/data/filter/utils/turnFilterIntoWhereClause';
import { useBoardActionBarEntries } from '@/ui/layout/board/hooks/useBoardActionBarEntries'; import { useBoardActionBarEntries } from '@/ui/layout/board/hooks/useBoardActionBarEntries';
import { useBoardContext } from '@/ui/layout/board/hooks/useBoardContext';
import { useBoardContextMenuEntries } from '@/ui/layout/board/hooks/useBoardContextMenuEntries'; import { useBoardContextMenuEntries } from '@/ui/layout/board/hooks/useBoardContextMenuEntries';
import { availableBoardCardFieldsScopedState } from '@/ui/layout/board/states/availableBoardCardFieldsScopedState';
import { boardCardFieldsScopedState } from '@/ui/layout/board/states/boardCardFieldsScopedState';
import { isBoardLoadedState } from '@/ui/layout/board/states/isBoardLoadedState'; import { isBoardLoadedState } from '@/ui/layout/board/states/isBoardLoadedState';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useView } from '@/views/hooks/useView'; import { useView } from '@/views/hooks/useView';
import { useViewInternalStates } from '@/views/hooks/useViewInternalStates'; import { useViewGetStates } from '@/views/hooks/useViewGetStates';
import { ViewType } from '@/views/types/ViewType';
import { viewFieldsToBoardFieldDefinitions } from '@/views/utils/viewFieldsToBoardFieldDefinitions';
import { import {
Pipeline, Pipeline,
PipelineProgressableType, PipelineProgressableType,
@ -22,20 +29,30 @@ import { useUpdateCompanyBoard } from '../hooks/useUpdateCompanyBoardColumns';
export const HooksCompanyBoardEffect = () => { export const HooksCompanyBoardEffect = () => {
const { const {
setAvailableFilters, setAvailableFilterDefinitions,
setAvailableSorts, setAvailableSortDefinitions,
setAvailableFieldDefinitions,
setEntityCountInCurrentView, setEntityCountInCurrentView,
setViewObjectId,
setViewType,
} = useView(); } = useView();
const { currentViewFilters } = useViewInternalStates(); const { currentViewFilters, currentViewFields } = useViewGetStates();
useEffect(() => {
setAvailableFilters(opportunitiesBoardOptions.filters);
setAvailableSorts?.(opportunitiesBoardOptions.sorts);
}, [setAvailableFilters, setAvailableSorts]);
const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState); const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState);
const { BoardRecoilScopeContext } = useBoardContext();
const [, setBoardCardFields] = useRecoilScopedState(
boardCardFieldsScopedState,
BoardRecoilScopeContext,
);
const [, setAvailableBoardCardFields] = useRecoilScopedState(
availableBoardCardFieldsScopedState,
BoardRecoilScopeContext,
);
const updateCompanyBoard = useUpdateCompanyBoard(); const updateCompanyBoard = useUpdateCompanyBoard();
const { data: pipelineData, loading: loadingGetPipelines } = const { data: pipelineData, loading: loadingGetPipelines } =
@ -51,6 +68,21 @@ export const HooksCompanyBoardEffect = () => {
const pipeline = pipelineData?.findManyPipeline[0] as Pipeline | undefined; const pipeline = pipelineData?.findManyPipeline[0] as Pipeline | undefined;
useEffect(() => {
setAvailableFilterDefinitions(opportunitiesBoardOptions.filterDefinitions);
setAvailableSortDefinitions?.(opportunitiesBoardOptions.sortDefinitions);
setAvailableFieldDefinitions?.(pipelineAvailableFieldDefinitions);
}, [
setAvailableFieldDefinitions,
setAvailableFilterDefinitions,
setAvailableSortDefinitions,
]);
useEffect(() => {
setViewObjectId?.('company');
setViewType?.(ViewType.Kanban);
}, [setViewObjectId, setViewType]);
const pipelineStageIds = pipeline?.pipelineStages const pipelineStageIds = pipeline?.pipelineStages
?.map((pipelineStage) => pipelineStage.id) ?.map((pipelineStage) => pipelineStage.id)
.flat(); .flat();
@ -107,6 +139,7 @@ export const HooksCompanyBoardEffect = () => {
if (!loading && pipeline && pipelineProgresses && companiesData) { if (!loading && pipeline && pipelineProgresses && companiesData) {
setActionBarEntries(); setActionBarEntries();
setContextMenuEntries(); setContextMenuEntries();
setAvailableBoardCardFields(pipelineAvailableFieldDefinitions);
updateCompanyBoard(pipeline, pipelineProgresses, companiesData.companies); updateCompanyBoard(pipeline, pipelineProgresses, companiesData.companies);
setEntityCountInCurrentView(companiesData.companies.length); setEntityCountInCurrentView(companiesData.companies.length);
} }
@ -120,7 +153,19 @@ export const HooksCompanyBoardEffect = () => {
setContextMenuEntries, setContextMenuEntries,
searchParams, searchParams,
setEntityCountInCurrentView, setEntityCountInCurrentView,
setAvailableBoardCardFields,
]); ]);
useEffect(() => {
if (currentViewFields) {
setBoardCardFields(
viewFieldsToBoardFieldDefinitions(
currentViewFields,
pipelineAvailableFieldDefinitions,
),
);
}
}, [currentViewFields, setBoardCardFields]);
return <></>; return <></>;
}; };

View File

@ -8,40 +8,59 @@ import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyT
import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport'; import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport';
import { DataTable } from '@/ui/data/data-table/components/DataTable'; import { DataTable } from '@/ui/data/data-table/components/DataTable';
import { DataTableEffect } from '@/ui/data/data-table/components/DataTableEffect'; import { DataTableEffect } from '@/ui/data/data-table/components/DataTableEffect';
import { TableOptionsDropdownId } from '@/ui/data/data-table/constants/TableOptionsDropdownId';
import { TableContext } from '@/ui/data/data-table/contexts/TableContext'; import { TableContext } from '@/ui/data/data-table/contexts/TableContext';
import { useUpsertDataTableItem } from '@/ui/data/data-table/hooks/useUpsertDataTableItem'; import { useUpsertDataTableItem } from '@/ui/data/data-table/hooks/useUpsertDataTableItem';
import { TableOptionsDropdown } from '@/ui/data/data-table/options/components/TableOptionsDropdown'; import { TableOptionsDropdown } from '@/ui/data/data-table/options/components/TableOptionsDropdown';
import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumnsScopedState'; import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumnsScopedState';
import { tableFiltersScopedState } from '@/ui/data/data-table/states/tableFiltersScopedState';
import { tableSortsScopedState } from '@/ui/data/data-table/states/tableSortsScopedState';
import { ViewBar } from '@/views/components/ViewBar'; import { ViewBar } from '@/views/components/ViewBar';
import { useViewFields } from '@/views/hooks/internal/useViewFields'; import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { useView } from '@/views/hooks/useView'; import { useView } from '@/views/hooks/useView';
import { ViewScope } from '@/views/scopes/ViewScope'; import { ViewScope } from '@/views/scopes/ViewScope';
import { columnDefinitionsToViewFields } from '@/views/utils/columnDefinitionToViewField'; import { columnDefinitionsToViewFields } from '@/views/utils/columnDefinitionToViewField';
import { viewFieldsToColumnDefinitions } from '@/views/utils/viewFieldsToColumnDefinitions'; import { viewFieldsToColumnDefinitions } from '@/views/utils/viewFieldsToColumnDefinitions';
import { viewFiltersToFilters } from '@/views/utils/viewFiltersToFilters';
import { viewSortsToSorts } from '@/views/utils/viewSortsToSorts';
import { import {
UpdateOneCompanyMutationVariables, UpdateOneCompanyMutationVariables,
useGetCompaniesQuery, useGetCompaniesQuery,
useGetWorkspaceMembersLazyQuery, useGetWorkspaceMembersLazyQuery,
useUpdateOneCompanyMutation, useUpdateOneCompanyMutation,
} from '~/generated/graphql'; } from '~/generated/graphql';
import { companyAvailableFilters } from '~/pages/companies/companies-filters'; import { companyTableFilterDefinitions } from '~/pages/companies/constants/companyTableFilterDefinitions';
import { companyAvailableSorts } from '~/pages/companies/companies-sorts'; import { companyTableSortDefinitions } from '~/pages/companies/constants/companyTableSortDefinitions';
import CompanyTableEffect from './CompanyTableEffect'; import CompanyTableEffect from './CompanyTableEffect';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
overflow: auto;
`;
export const CompanyTable = () => { export const CompanyTable = () => {
const tableViewScopeId = 'company-table'; const viewScopeId = 'company-table-view';
const tableScopeId = 'companies';
const setTableColumns = useSetRecoilState( const setTableColumns = useSetRecoilState(
tableColumnsScopedState('companies'), tableColumnsScopedState(tableScopeId),
); );
const setTableFilters = useSetRecoilState(
tableFiltersScopedState(tableScopeId),
);
const setTableSorts = useSetRecoilState(tableSortsScopedState(tableScopeId));
const [updateEntityMutation] = useUpdateOneCompanyMutation(); const [updateEntityMutation] = useUpdateOneCompanyMutation();
const upsertDataTableItem = useUpsertDataTableItem(); const upsertDataTableItem = useUpsertDataTableItem();
const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery(); const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery();
const { persistViewFields } = useViewFields(tableViewScopeId); const { persistViewFields } = useViewFields(viewScopeId);
const { setCurrentViewFields, currentViewId } = useView({ const { setCurrentViewFields } = useView({
viewScopeId: tableViewScopeId, viewScopeId,
}); });
const { setContextMenuEntries } = useCompanyTableContextMenuEntries(); const { setContextMenuEntries } = useCompanyTableContextMenuEntries();
@ -79,16 +98,9 @@ export const CompanyTable = () => {
const { openCompanySpreadsheetImport: onImport } = const { openCompanySpreadsheetImport: onImport } =
useSpreadsheetCompanyImport(); useSpreadsheetCompanyImport();
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
overflow: auto;
`;
return ( return (
<ViewScope <ViewScope
viewScopeId={tableViewScopeId} viewScopeId={viewScopeId}
onViewFieldsChange={(viewFields) => { onViewFieldsChange={(viewFields) => {
setTableColumns( setTableColumns(
viewFieldsToColumnDefinitions( viewFieldsToColumnDefinitions(
@ -97,7 +109,12 @@ export const CompanyTable = () => {
), ),
); );
}} }}
onViewFiltersChange={() => {}} onViewFiltersChange={(viewFilters) => {
setTableFilters(viewFiltersToFilters(viewFilters));
}}
onViewSortsChange={(viewSorts) => {
setTableSorts(viewSortsToSorts(viewSorts));
}}
> >
<StyledContainer> <StyledContainer>
<TableContext.Provider <TableContext.Provider
@ -110,7 +127,7 @@ export const CompanyTable = () => {
> >
<ViewBar <ViewBar
optionsDropdownButton={<TableOptionsDropdown onImport={onImport} />} optionsDropdownButton={<TableOptionsDropdown onImport={onImport} />}
optionsDropdownScopeId="table-dropdown-option" optionsDropdownScopeId={TableOptionsDropdownId}
/> />
<CompanyTableEffect /> <CompanyTableEffect />
<DataTableEffect <DataTableEffect
@ -119,8 +136,8 @@ export const CompanyTable = () => {
getRequestOptimisticEffectDefinition={ getRequestOptimisticEffectDefinition={
getCompaniesOptimisticEffectDefinition getCompaniesOptimisticEffectDefinition
} }
filterDefinitionArray={companyAvailableFilters} filterDefinitionArray={companyTableFilterDefinitions}
sortDefinitionArray={companyAvailableSorts} sortDefinitionArray={companyTableSortDefinitions}
setContextMenuEntries={setContextMenuEntries} setContextMenuEntries={setContextMenuEntries}
setActionBarEntries={setActionBarEntries} setActionBarEntries={setActionBarEntries}
/> />

View File

@ -6,14 +6,14 @@ import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scop
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useView } from '@/views/hooks/useView'; import { useView } from '@/views/hooks/useView';
import { ViewType } from '@/views/types/ViewType'; import { ViewType } from '@/views/types/ViewType';
import { companyAvailableFilters } from '~/pages/companies/companies-filters'; import { companyTableFilterDefinitions } from '~/pages/companies/constants/companyTableFilterDefinitions';
import { companyAvailableSorts } from '~/pages/companies/companies-sorts'; import { companyTableSortDefinitions } from '~/pages/companies/constants/companyTableSortDefinitions';
const CompanyTableEffect = () => { const CompanyTableEffect = () => {
const { const {
setAvailableSorts, setAvailableSortDefinitions,
setAvailableFilters, setAvailableFilterDefinitions,
setAvailableFields, setAvailableFieldDefinitions,
setViewType, setViewType,
setViewObjectId, setViewObjectId,
} = useView(); } = useView();
@ -24,26 +24,21 @@ const CompanyTableEffect = () => {
); );
useEffect(() => { useEffect(() => {
setAvailableSorts?.(companyAvailableSorts); setAvailableSortDefinitions?.(companyTableSortDefinitions);
setAvailableFilters?.(companyAvailableFilters); setAvailableFilterDefinitions?.(companyTableFilterDefinitions);
setAvailableFields?.(companiesAvailableFieldDefinitions); setAvailableFieldDefinitions?.(companiesAvailableFieldDefinitions);
setViewObjectId?.('company'); setViewObjectId?.('company');
setViewType?.(ViewType.Table); setViewType?.(ViewType.Table);
setAvailableTableColumns(companiesAvailableFieldDefinitions); setAvailableTableColumns(companiesAvailableFieldDefinitions);
}, [ }, [
setAvailableFields, setAvailableFieldDefinitions,
setAvailableFilters, setAvailableFilterDefinitions,
setAvailableSorts, setAvailableSortDefinitions,
setAvailableTableColumns, setAvailableTableColumns,
setViewObjectId, setViewObjectId,
setViewType, setViewType,
]); ]);
// useEffect(() => {
// if (currentViewFields) {
// setTableColumns([...currentViewFields].sort((a, b) => a.index - b.index));
// }
// }, [currentViewFields, setTableColumns]);
// useEffect(() => { // useEffect(() => {
// if (currentViewSorts) { // if (currentViewSorts) {

View File

@ -17,7 +17,6 @@ export const CompanyTableMockDataEffect = () => {
useEffect(() => { useEffect(() => {
setDataTableData(mockedCompaniesData, [], []); setDataTableData(mockedCompaniesData, [], []);
setTableColumns(companiesAvailableFieldDefinitions); setTableColumns(companiesAvailableFieldDefinitions);
}, [setDataTableData, setTableColumns]); }, [setDataTableData, setTableColumns]);

View File

@ -1,6 +1,7 @@
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { IconBuildingSkyscraper } from '@/ui/display/icon'; import { Icon123 } from '@/ui/input/constants/icons';
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
import NavItem from '@/ui/navigation/navbar/components/NavItem'; import NavItem from '@/ui/navigation/navbar/components/NavItem';
import { capitalize } from '~/utils/string/capitalize'; import { capitalize } from '~/utils/string/capitalize';
@ -10,22 +11,26 @@ export const MetadataObjectNavItems = () => {
const { metadataObjects } = useFindManyMetadataObjects(); const { metadataObjects } = useFindManyMetadataObjects();
const navigate = useNavigate(); const navigate = useNavigate();
const { icons } = useLazyLoadIcons();
return ( return (
<> <>
{metadataObjects {metadataObjects
.filter((metadataObject) => !!metadataObject.isActive) .filter((metadataObject) => !!metadataObject.isActive)
.map((metadataObject) => ( .filter((metadataObjects) => !metadataObjects.namePlural.endsWith('V2'))
<NavItem .map((metadataObject) => {
key={metadataObject.id} return (
label={capitalize(metadataObject.namePlural)} <NavItem
to={`/objects/${metadataObject.namePlural}`} key={metadataObject.id}
Icon={IconBuildingSkyscraper} label={capitalize(metadataObject.namePlural)}
onClick={() => { to={`/objects/${metadataObject.namePlural}`}
navigate(`/objects/${metadataObject.namePlural}`); Icon={metadataObject.icon ? icons[metadataObject.icon] : Icon123}
}} onClick={() => {
/> navigate(`/objects/${metadataObject.namePlural}`);
))} }}
/>
);
})}
</> </>
); );
}; };

View File

@ -9,9 +9,9 @@ import { useMetadataObjectInContext } from '../hooks/useMetadataObjectInContext'
export const ObjectTableEffect = () => { export const ObjectTableEffect = () => {
const { const {
setAvailableSorts, setAvailableSortDefinitions,
setAvailableFilters, setAvailableFilterDefinitions,
setAvailableFields, setAvailableFieldDefinitions,
setViewType, setViewType,
setViewObjectId, setViewObjectId,
} = useView(); } = useView();
@ -38,22 +38,22 @@ export const ObjectTableEffect = () => {
); );
useEffect(() => { useEffect(() => {
setAvailableSorts?.([]); // TODO: extract from metadata fields setAvailableSortDefinitions?.([]); // TODO: extract from metadata fields
setAvailableFilters?.([]); // TODO: extract from metadata fields setAvailableFilterDefinitions?.([]); // TODO: extract from metadata fields
setAvailableFields?.(columnDefinitions); setAvailableFieldDefinitions?.(columnDefinitions);
setViewObjectId?.(objectNamePlural); setViewObjectId?.(objectNamePlural);
setViewType?.(ViewType.Table); setViewType?.(ViewType.Table);
setAvailableTableColumns(columnDefinitions); setAvailableTableColumns(columnDefinitions);
}, [ }, [
setAvailableFields,
setAvailableFilters,
setAvailableSorts,
setAvailableTableColumns, setAvailableTableColumns,
setViewObjectId, setViewObjectId,
setViewType, setViewType,
columnDefinitions, columnDefinitions,
objectNamePlural, objectNamePlural,
setAvailableSortDefinitions,
setAvailableFilterDefinitions,
setAvailableFieldDefinitions,
]); ]);
// useEffect(() => { // useEffect(() => {

View File

@ -6,6 +6,7 @@ import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier'; import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier';
import { formatMetadataFieldAsColumnDefinition } from '../utils/formatMetadataFieldAsColumnDefinition'; import { formatMetadataFieldAsColumnDefinition } from '../utils/formatMetadataFieldAsColumnDefinition';
import { generateCreateOneObjectMutation } from '../utils/generateCreateOneObjectMutation'; import { generateCreateOneObjectMutation } from '../utils/generateCreateOneObjectMutation';
import { generateDeleteOneObjectMutation } from '../utils/generateDeleteOneObjectMutation';
import { generateFindManyCustomObjectsQuery } from '../utils/generateFindManyCustomObjectsQuery'; import { generateFindManyCustomObjectsQuery } from '../utils/generateFindManyCustomObjectsQuery';
import { generateFindOneCustomObjectQuery } from '../utils/generateFindOneCustomObjectQuery'; import { generateFindOneCustomObjectQuery } from '../utils/generateFindOneCustomObjectQuery';
import { generateUpdateOneObjectMutation } from '../utils/generateUpdateOneObjectMutation'; import { generateUpdateOneObjectMutation } from '../utils/generateUpdateOneObjectMutation';
@ -79,7 +80,7 @@ export const useFindOneMetadataObject = ({
// TODO: implement backend delete // TODO: implement backend delete
const deleteOneMutation = foundMetadataObject const deleteOneMutation = foundMetadataObject
? generateCreateOneObjectMutation({ ? generateDeleteOneObjectMutation({
metadataObject: foundMetadataObject, metadataObject: foundMetadataObject,
}) })
: gql` : gql`

View File

@ -7,7 +7,7 @@ import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scop
import { tableRowIdsState } from '@/ui/data/data-table/states/tableRowIdsState'; import { tableRowIdsState } from '@/ui/data/data-table/states/tableRowIdsState';
import { entityFieldsFamilyState } from '@/ui/data/field/states/entityFieldsFamilyState'; import { entityFieldsFamilyState } from '@/ui/data/field/states/entityFieldsFamilyState';
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId'; import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
import { availableSortsScopedState } from '@/views/states/availableSortsScopedState'; import { availableSortDefinitionsScopedState } from '@/views/states/availableSortDefinitionsScopedState';
export const useSetObjectDataTableData = () => { export const useSetObjectDataTableData = () => {
const resetTableRowSelection = useResetTableRowSelection(); const resetTableRowSelection = useResetTableRowSelection();
@ -41,7 +41,10 @@ export const useSetObjectDataTableData = () => {
set(numberOfTableRowsState, entityIds.length); set(numberOfTableRowsState, entityIds.length);
set(availableSortsScopedState({ scopeId: tableContextScopeId }), []); set(
availableSortDefinitionsScopedState({ scopeId: tableContextScopeId }),
[],
);
set(isFetchingDataTableDataState, false); set(isFetchingDataTableDataState, false);
}, },

View File

@ -1,97 +0,0 @@
import styled from '@emotion/styled';
import { getPeopleOptimisticEffectDefinition } from '@/people/graphql/optimistic-effect-definitions/getPeopleOptimisticEffectDefinition';
import { usePersonTableContextMenuEntries } from '@/people/hooks/usePeopleTableContextMenuEntries';
import { usePersonTableActionBarEntries } from '@/people/hooks/usePersonTableActionBarEntries';
import { useSpreadsheetPersonImport } from '@/people/hooks/useSpreadsheetPersonImport';
import { DataTable } from '@/ui/data/data-table/components/DataTable';
import { DataTableEffect } from '@/ui/data/data-table/components/DataTableEffect';
import { TableContext } from '@/ui/data/data-table/contexts/TableContext';
import { useUpsertDataTableItem } from '@/ui/data/data-table/hooks/useUpsertDataTableItem';
import { TableOptionsDropdown } from '@/ui/data/data-table/options/components/TableOptionsDropdown';
import { ViewBar } from '@/views/components/ViewBar';
import { ViewBarEffect } from '@/views/components/ViewBarEffect';
import { ViewScope } from '@/views/scopes/ViewScope';
import {
UpdateOnePersonMutationVariables,
useGetPeopleQuery,
useUpdateOnePersonMutation,
} from '~/generated/graphql';
import { peopleAvailableFilters } from '~/pages/people/people-filters';
import { peopleAvailableSorts } from '~/pages/people/people-sorts';
import PersonTableEffect from './PersonTableEffect';
export const PeopleTable = () => {
const tableViewScopeId = 'person-table';
const [updateEntityMutation] = useUpdateOnePersonMutation();
const upsertDataTableItem = useUpsertDataTableItem();
const { setContextMenuEntries } = usePersonTableContextMenuEntries();
const { setActionBarEntries } = usePersonTableActionBarEntries();
const { openPersonSpreadsheetImport: onImport } =
useSpreadsheetPersonImport();
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
overflow: auto;
`;
return (
<ViewScope
viewScopeId={tableViewScopeId}
onViewFieldsChange={() => {}}
onViewSortsChange={() => {}}
onViewFiltersChange={() => {}}
>
<StyledContainer>
<TableContext.Provider
value={{
onColumnsChange: () => {},
}}
>
<ViewBarEffect />
<ViewBar
optionsDropdownButton={<TableOptionsDropdown onImport={onImport} />}
optionsDropdownScopeId="table-dropdown-option"
/>
<PersonTableEffect />
<DataTableEffect
getRequestResultKey="people"
useGetRequest={useGetPeopleQuery}
getRequestOptimisticEffectDefinition={
getPeopleOptimisticEffectDefinition
}
filterDefinitionArray={peopleAvailableFilters}
sortDefinitionArray={peopleAvailableSorts}
setContextMenuEntries={setContextMenuEntries}
setActionBarEntries={setActionBarEntries}
/>
<DataTable
updateEntityMutation={({
variables,
}: {
variables: UpdateOnePersonMutationVariables;
}) =>
updateEntityMutation({
variables,
onCompleted: (data) => {
if (!data.updateOnePerson) {
return;
}
upsertDataTableItem(data.updateOnePerson);
},
})
}
/>
</TableContext.Provider>
</StyledContainer>
</ViewScope>
);
};

View File

@ -0,0 +1,135 @@
import styled from '@emotion/styled';
import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { peopleAvailableFieldDefinitions } from '@/people/constants/peopleAvailableFieldDefinitions';
import { getPeopleOptimisticEffectDefinition } from '@/people/graphql/optimistic-effect-definitions/getPeopleOptimisticEffectDefinition';
import { usePersonTableActionBarEntries } from '@/people/hooks/usePersonTableActionBarEntries';
import { usePersonTableContextMenuEntries } from '@/people/hooks/usePersonTableContextMenuEntries';
import { useSpreadsheetPersonImport } from '@/people/hooks/useSpreadsheetPersonImport';
import { DataTable } from '@/ui/data/data-table/components/DataTable';
import { DataTableEffect } from '@/ui/data/data-table/components/DataTableEffect';
import { TableContext } from '@/ui/data/data-table/contexts/TableContext';
import { useUpsertDataTableItem } from '@/ui/data/data-table/hooks/useUpsertDataTableItem';
import { TableOptionsDropdown } from '@/ui/data/data-table/options/components/TableOptionsDropdown';
import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumnsScopedState';
import { tableFiltersScopedState } from '@/ui/data/data-table/states/tableFiltersScopedState';
import { tableSortsScopedState } from '@/ui/data/data-table/states/tableSortsScopedState';
import { ViewBar } from '@/views/components/ViewBar';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { useView } from '@/views/hooks/useView';
import { ViewScope } from '@/views/scopes/ViewScope';
import { columnDefinitionsToViewFields } from '@/views/utils/columnDefinitionToViewField';
import { viewFieldsToColumnDefinitions } from '@/views/utils/viewFieldsToColumnDefinitions';
import { viewFiltersToFilters } from '@/views/utils/viewFiltersToFilters';
import { viewSortsToSorts } from '@/views/utils/viewSortsToSorts';
import {
UpdateOnePersonMutationVariables,
useGetPeopleQuery,
useUpdateOnePersonMutation,
} from '~/generated/graphql';
import { personTableFilterDefinitions } from '~/pages/people/constants/personTableFilterDefinitions';
import { personTableSortDefinitions } from '~/pages/people/constants/personTableSortDefinitions';
import PersonTableEffect from './PersonTableEffect';
export const PersonTable = () => {
const viewScopeId = 'person-table-view';
const tableScopeId = 'people';
const setTableColumns = useSetRecoilState(
tableColumnsScopedState(tableScopeId),
);
const setTableFilters = useSetRecoilState(
tableFiltersScopedState(tableScopeId),
);
const setTableSorts = useSetRecoilState(tableSortsScopedState(tableScopeId));
const [updateEntityMutation] = useUpdateOnePersonMutation();
const upsertDataTableItem = useUpsertDataTableItem();
const { persistViewFields } = useViewFields(viewScopeId);
const { setCurrentViewFields } = useView({
viewScopeId,
});
const { setContextMenuEntries } = usePersonTableContextMenuEntries();
const { setActionBarEntries } = usePersonTableActionBarEntries();
const updatePerson = async (variables: UpdateOnePersonMutationVariables) => {
updateEntityMutation({
variables: variables,
onCompleted: (data) => {
if (!data.updateOnePerson) {
return;
}
upsertDataTableItem(data.updateOnePerson);
},
});
};
const { openPersonSpreadsheetImport: onImport } =
useSpreadsheetPersonImport();
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
overflow: auto;
`;
return (
<ViewScope
viewScopeId={viewScopeId}
onViewFieldsChange={(viewFields) => {
setTableColumns(
viewFieldsToColumnDefinitions(
viewFields,
peopleAvailableFieldDefinitions,
),
);
}}
onViewFiltersChange={(viewFilters) => {
setTableFilters(viewFiltersToFilters(viewFilters));
}}
onViewSortsChange={(viewSorts) => {
setTableSorts(viewSortsToSorts(viewSorts));
}}
>
<StyledContainer>
<TableContext.Provider
value={{
onColumnsChange: useRecoilCallback(() => (columns) => {
setCurrentViewFields?.(columnDefinitionsToViewFields(columns));
persistViewFields(columnDefinitionsToViewFields(columns));
}),
}}
>
<ViewBar
optionsDropdownButton={<TableOptionsDropdown onImport={onImport} />}
optionsDropdownScopeId="table-dropdown-option"
/>
<PersonTableEffect />
<DataTableEffect
getRequestResultKey="people"
useGetRequest={useGetPeopleQuery}
getRequestOptimisticEffectDefinition={
getPeopleOptimisticEffectDefinition
}
filterDefinitionArray={personTableFilterDefinitions}
sortDefinitionArray={personTableSortDefinitions}
setContextMenuEntries={setContextMenuEntries}
setActionBarEntries={setActionBarEntries}
/>
<DataTable
updateEntityMutation={({
variables,
}: {
variables: UpdateOnePersonMutationVariables;
}) => updatePerson(variables)}
/>
</TableContext.Provider>
</StyledContainer>
</ViewScope>
);
};

View File

@ -7,14 +7,14 @@ import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumn
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useView } from '@/views/hooks/useView'; import { useView } from '@/views/hooks/useView';
import { ViewType } from '@/views/types/ViewType'; import { ViewType } from '@/views/types/ViewType';
import { peopleAvailableFilters } from '~/pages/people/people-filters'; import { personTableFilterDefinitions } from '~/pages/people/constants/personTableFilterDefinitions';
import { peopleAvailableSorts } from '~/pages/people/people-sorts'; import { personTableSortDefinitions } from '~/pages/people/constants/personTableSortDefinitions';
const PeopleTableEffect = () => { const PeopleTableEffect = () => {
const { const {
setAvailableSorts, setAvailableSortDefinitions,
setAvailableFilters, setAvailableFilterDefinitions,
setAvailableFields, setAvailableFieldDefinitions,
setViewType, setViewType,
setViewObjectId, setViewObjectId,
} = useView(); } = useView();
@ -30,17 +30,17 @@ const PeopleTableEffect = () => {
); );
useEffect(() => { useEffect(() => {
setAvailableSorts?.(peopleAvailableSorts); setAvailableSortDefinitions?.(personTableSortDefinitions);
setAvailableFilters?.(peopleAvailableFilters); setAvailableFilterDefinitions?.(personTableFilterDefinitions);
setAvailableFields?.(peopleAvailableFieldDefinitions); setAvailableFieldDefinitions?.(peopleAvailableFieldDefinitions);
setViewObjectId?.('person'); setViewObjectId?.('person');
setViewType?.(ViewType.Table); setViewType?.(ViewType.Table);
setAvailableTableColumns(peopleAvailableFieldDefinitions); setAvailableTableColumns(peopleAvailableFieldDefinitions);
}, [ }, [
setAvailableFields, setAvailableFieldDefinitions,
setAvailableFilters, setAvailableFilterDefinitions,
setAvailableSorts, setAvailableSortDefinitions,
setAvailableTableColumns, setAvailableTableColumns,
setTableColumns, setTableColumns,
setViewObjectId, setViewObjectId,

View File

@ -1,3 +1,4 @@
import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition';
import { import {
FieldDateMetadata, FieldDateMetadata,
FieldMetadata, FieldMetadata,
@ -12,10 +13,9 @@ import {
IconUser, IconUser,
} from '@/ui/display/icon'; } from '@/ui/display/icon';
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
import { BoardFieldDefinition } from '@/ui/layout/board/types/BoardFieldDefinition';
import { Person } from '~/generated/graphql'; import { Person } from '~/generated/graphql';
export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetadata>[] = export const pipelineAvailableFieldDefinitions: ColumnDefinition<FieldMetadata>[] =
[ [
{ {
fieldId: 'closeDate', fieldId: 'closeDate',
@ -26,10 +26,11 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
metadata: { metadata: {
fieldName: 'closeDate', fieldName: 'closeDate',
}, },
size: 0,
isVisible: true, isVisible: true,
infoTooltipContent: infoTooltipContent:
'Specified date by which an opportunity must be completed.', 'Specified date by which an opportunity must be completed.',
} satisfies BoardFieldDefinition<FieldDateMetadata>, } satisfies ColumnDefinition<FieldDateMetadata>,
{ {
fieldId: 'amount', fieldId: 'amount',
label: 'Amount', label: 'Amount',
@ -40,9 +41,10 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
fieldName: 'amount', fieldName: 'amount',
placeHolder: '0', placeHolder: '0',
}, },
size: 0,
isVisible: true, isVisible: true,
infoTooltipContent: 'Potential monetary value of a business opportunity.', infoTooltipContent: 'Potential monetary value of a business opportunity.',
} satisfies BoardFieldDefinition<FieldNumberMetadata>, } satisfies ColumnDefinition<FieldNumberMetadata>,
{ {
fieldId: 'probability', fieldId: 'probability',
label: 'Probability', label: 'Probability',
@ -52,10 +54,11 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
metadata: { metadata: {
fieldName: 'probability', fieldName: 'probability',
}, },
size: 0,
isVisible: true, isVisible: true,
infoTooltipContent: infoTooltipContent:
"Level of certainty in the lead's potential to convert into a success.", "Level of certainty in the lead's potential to convert into a success.",
} satisfies BoardFieldDefinition<FieldProbabilityMetadata>, } satisfies ColumnDefinition<FieldProbabilityMetadata>,
{ {
fieldId: 'pointOfContact', fieldId: 'pointOfContact',
label: 'Point of Contact', label: 'Point of Contact',
@ -67,6 +70,7 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
relationType: Entity.Person, relationType: Entity.Person,
useEditButton: true, useEditButton: true,
}, },
size: 0,
isVisible: true, isVisible: true,
infoTooltipContent: 'Primary contact within the company.', infoTooltipContent: 'Primary contact within the company.',
entityChipDisplayMapper: (dataObject: Person) => { entityChipDisplayMapper: (dataObject: Person) => {
@ -76,5 +80,5 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
avatarType: 'rounded', avatarType: 'rounded',
}; };
}, },
} satisfies BoardFieldDefinition<FieldRelationMetadata>, } satisfies ColumnDefinition<FieldRelationMetadata>,
]; ];

View File

@ -55,7 +55,12 @@ export const SettingsObjectIconSection = ({
<StyledArrowContainer> <StyledArrowContainer>
<img src={ArrowRight} alt="Arrow right" width={32} height={16} /> <img src={ArrowRight} alt="Arrow right" width={32} height={16} />
</StyledArrowContainer> </StyledArrowContainer>
<SettingsObjectIconWithLabel Icon={Icon} label={label || 'Investors'} /> {Icon && (
<SettingsObjectIconWithLabel
Icon={Icon}
label={label || 'Investors'}
/>
)}
</StyledContainer> </StyledContainer>
</Section> </Section>
); );

View File

@ -168,37 +168,35 @@ export const DataTableHeader = () => {
> >
<SelectAllCheckbox /> <SelectAllCheckbox />
</th> </th>
{[...visibleTableColumns] {visibleTableColumns.map((column) => (
.sort((columnA, columnB) => columnA.position - columnB.position) <StyledColumnHeaderCell
.map((column) => ( key={column.fieldId}
<StyledColumnHeaderCell isResizing={resizedFieldKey === column.fieldId}
key={column.fieldId} columnWidth={Math.max(
isResizing={resizedFieldKey === column.fieldId} tableColumnsByKey[column.fieldId].size +
columnWidth={Math.max( (resizedFieldKey === column.fieldId ? resizeFieldOffset : 0),
tableColumnsByKey[column.fieldId].size + COLUMN_MIN_WIDTH,
(resizedFieldKey === column.fieldId ? resizeFieldOffset : 0), )}
COLUMN_MIN_WIDTH, >
)} <StyledColumnHeadContainer>
> <ColumnHeadWithDropdown
<StyledColumnHeadContainer> column={column}
<ColumnHeadWithDropdown isFirstColumn={column.position === 1}
column={column} isLastColumn={
isFirstColumn={column.position === 1} column.position === visibleTableColumns.length - 1
isLastColumn={ }
column.position === visibleTableColumns.length - 1 primaryColumnKey={primaryColumn?.fieldId || ''}
}
primaryColumnKey={primaryColumn?.fieldId || ''}
/>
</StyledColumnHeadContainer>
<StyledResizeHandler
className="cursor-col-resize"
role="separator"
onPointerDown={() => {
setResizedFieldKey(column.fieldId);
}}
/> />
</StyledColumnHeaderCell> </StyledColumnHeadContainer>
))} <StyledResizeHandler
className="cursor-col-resize"
role="separator"
onPointerDown={() => {
setResizedFieldKey(column.fieldId);
}}
/>
</StyledColumnHeaderCell>
))}
<th> <th>
{hiddenTableColumns.length > 0 && ( {hiddenTableColumns.length > 0 && (
<StyledColumnHeadContainer> <StyledColumnHeadContainer>

View File

@ -4,7 +4,7 @@ import { entityFieldsFamilyState } from '@/ui/data/field/states/entityFieldsFami
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition'; import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId'; import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
import { useView } from '@/views/hooks/useView'; import { useView } from '@/views/hooks/useView';
import { availableSortsScopedState } from '@/views/states/availableSortsScopedState'; import { availableSortDefinitionsScopedState } from '@/views/states/availableSortDefinitionsScopedState';
import { SortDefinition } from '../../sort/types/SortDefinition'; import { SortDefinition } from '../../sort/types/SortDefinition';
import { isFetchingDataTableDataState } from '../states/isFetchingDataTableDataState'; import { isFetchingDataTableDataState } from '../states/isFetchingDataTableDataState';
@ -54,7 +54,7 @@ export const useSetDataTableData = () => {
setEntityCountInCurrentView(entityIds.length); setEntityCountInCurrentView(entityIds.length);
set( set(
availableSortsScopedState({ scopeId: tableContextScopeId }), availableSortDefinitionsScopedState({ scopeId: tableContextScopeId }),
sortDefinitionArray, sortDefinitionArray,
); );

View File

@ -13,7 +13,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection'; import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
import { useView } from '@/views/hooks/useView'; import { useView } from '@/views/hooks/useView';
import { useViewInternalStates } from '@/views/hooks/useViewInternalStates'; import { useViewGetStates } from '@/views/hooks/useViewGetStates';
import { useTableColumns } from '../../hooks/useTableColumns'; import { useTableColumns } from '../../hooks/useTableColumns';
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext'; import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
@ -29,7 +29,7 @@ export const TableOptionsDropdownContent = ({
onImport?: () => void; onImport?: () => void;
}) => { }) => {
const { setViewEditMode, handleViewNameSubmit } = useView(); const { setViewEditMode, handleViewNameSubmit } = useView();
const { viewEditMode, currentView } = useViewInternalStates(); const { viewEditMode, currentView } = useViewGetStates();
const { closeDropdown } = useDropdown(); const { closeDropdown } = useDropdown();

View File

@ -12,12 +12,13 @@ type FilterDropdownButtonProps = {
export const FilterDropdownButton = ({ export const FilterDropdownButton = ({
hotkeyScope, hotkeyScope,
}: FilterDropdownButtonProps) => { }: FilterDropdownButtonProps) => {
const { availableFilters } = useFilter(); const { availableFilterDefinitions } = useFilter();
const hasOnlyOneEntityFilter = const hasOnlyOneEntityFilter =
availableFilters.length === 1 && availableFilters[0].type === 'entity'; availableFilterDefinitions.length === 1 &&
availableFilterDefinitions[0].type === 'entity';
if (!availableFilters.length) { if (!availableFilterDefinitions.length) {
return <></>; return <></>;
} }

View File

@ -1,5 +1,4 @@
import { InternalDatePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker'; import { InternalDatePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
import { useUpsertFilter } from '@/views/hooks/useUpsertFilter';
import { useFilter } from '../hooks/useFilter'; import { useFilter } from '../hooks/useFilter';
@ -8,19 +7,18 @@ export const FilterDropdownDateSearchInput = () => {
filterDefinitionUsedInDropdown, filterDefinitionUsedInDropdown,
selectedOperandInDropdown, selectedOperandInDropdown,
setIsFilterDropdownUnfolded, setIsFilterDropdownUnfolded,
selectFilter,
} = useFilter(); } = useFilter();
const upsertFilter = useUpsertFilter();
const handleChange = (date: Date) => { const handleChange = (date: Date) => {
if (!filterDefinitionUsedInDropdown || !selectedOperandInDropdown) return; if (!filterDefinitionUsedInDropdown || !selectedOperandInDropdown) return;
upsertFilter({ selectFilter?.({
key: filterDefinitionUsedInDropdown.key, fieldId: filterDefinitionUsedInDropdown.fieldId,
type: filterDefinitionUsedInDropdown.type,
value: date.toISOString(), value: date.toISOString(),
operand: selectedOperandInDropdown, operand: selectedOperandInDropdown,
displayValue: date.toLocaleDateString(), displayValue: date.toLocaleDateString(),
definition: filterDefinitionUsedInDropdown,
}); });
setIsFilterDropdownUnfolded(false); setIsFilterDropdownUnfolded(false);

View File

@ -1,11 +1,8 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useFilterCurrentlyEdited } from '@/ui/data/filter/hooks/useFilterCurrentlyEdited';
import { EntitiesForMultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect'; import { EntitiesForMultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect';
import { SingleEntitySelectBase } from '@/ui/input/relation-picker/components/SingleEntitySelectBase'; import { SingleEntitySelectBase } from '@/ui/input/relation-picker/components/SingleEntitySelectBase';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { useRemoveFilter } from '@/views/hooks/useRemoveFilter';
import { useUpsertFilter } from '@/views/hooks/useUpsertFilter';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { useFilter } from '../hooks/useFilter'; import { useFilter } from '../hooks/useFilter';
@ -16,20 +13,16 @@ export const FilterDropdownEntitySearchSelect = ({
entitiesForSelect: EntitiesForMultipleEntitySelect<EntityForSelect>; entitiesForSelect: EntitiesForMultipleEntitySelect<EntityForSelect>;
}) => { }) => {
const { const {
filterDropdownSelectedEntityId,
setFilterDropdownSelectedEntityId, setFilterDropdownSelectedEntityId,
filterDefinitionUsedInDropdown, filterDefinitionUsedInDropdown,
selectedOperandInDropdown, selectedOperandInDropdown,
filterDropdownSearchInput, filterDropdownSearchInput,
selectedFilter,
selectFilter,
} = useFilter(); } = useFilter();
const [isAllEntitySelected, setIsAllEntitySelected] = useState(false); const [isAllEntitySelected, setIsAllEntitySelected] = useState(false);
const upsertFilter = useUpsertFilter();
const removeFilter = useRemoveFilter();
const filterCurrentlyEdited = useFilterCurrentlyEdited();
const handleUserSelected = ( const handleUserSelected = (
selectedEntity: EntityForSelect | null | undefined, selectedEntity: EntityForSelect | null | undefined,
) => { ) => {
@ -45,24 +38,16 @@ export const FilterDropdownEntitySearchSelect = ({
setIsAllEntitySelected(false); setIsAllEntitySelected(false);
} }
const clickedOnAlreadySelectedEntity = setFilterDropdownSelectedEntityId(selectedEntity.id);
selectedEntity.id === filterDropdownSelectedEntityId;
if (clickedOnAlreadySelectedEntity) { selectFilter?.({
removeFilter(filterDefinitionUsedInDropdown.key); displayValue: selectedEntity.name,
setFilterDropdownSelectedEntityId(null); fieldId: filterDefinitionUsedInDropdown.fieldId,
} else { operand: selectedOperandInDropdown,
setFilterDropdownSelectedEntityId(selectedEntity.id); value: selectedEntity.id,
displayAvatarUrl: selectedEntity.avatarUrl,
upsertFilter({ definition: filterDefinitionUsedInDropdown,
displayValue: selectedEntity.name, });
key: filterDefinitionUsedInDropdown.key,
operand: selectedOperandInDropdown,
type: filterDefinitionUsedInDropdown.type,
value: selectedEntity.id,
displayAvatarUrl: selectedEntity.avatarUrl,
});
}
}; };
const isAllEntitySelectShown = const isAllEntitySelectShown =
@ -81,36 +66,30 @@ export const FilterDropdownEntitySearchSelect = ({
) { ) {
return; return;
} }
if (isAllEntitySelected) {
setIsAllEntitySelected(false);
removeFilter(filterDefinitionUsedInDropdown.key); setIsAllEntitySelected(true);
} else { setFilterDropdownSelectedEntityId(null);
setIsAllEntitySelected(true);
setFilterDropdownSelectedEntityId(null); selectFilter?.({
displayValue: filterDefinitionUsedInDropdown.selectAllLabel,
upsertFilter({ fieldId: filterDefinitionUsedInDropdown.fieldId,
displayValue: filterDefinitionUsedInDropdown.selectAllLabel, operand: ViewFilterOperand.IsNotNull,
key: filterDefinitionUsedInDropdown.key, value: '',
operand: ViewFilterOperand.IsNotNull, definition: filterDefinitionUsedInDropdown,
type: filterDefinitionUsedInDropdown.type, });
value: '',
});
}
}; };
useEffect(() => { useEffect(() => {
if (!filterCurrentlyEdited) { if (!selectedFilter) {
setFilterDropdownSelectedEntityId(null); setFilterDropdownSelectedEntityId(null);
} else { } else {
setFilterDropdownSelectedEntityId(filterCurrentlyEdited.value); setFilterDropdownSelectedEntityId(selectedFilter.value);
setIsAllEntitySelected( setIsAllEntitySelected(
filterCurrentlyEdited.operand === ViewFilterOperand.IsNotNull, selectedFilter.operand === ViewFilterOperand.IsNotNull,
); );
} }
}, [ }, [
filterCurrentlyEdited, selectedFilter,
setFilterDropdownSelectedEntityId, setFilterDropdownSelectedEntityId,
entitiesForSelect.selectedEntities, entitiesForSelect.selectedEntities,
]); ]);

View File

@ -11,32 +11,32 @@ export const FilterDropdownFilterSelect = () => {
setFilterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown,
setSelectedOperandInDropdown, setSelectedOperandInDropdown,
setFilterDropdownSearchInput, setFilterDropdownSearchInput,
availableFilters, availableFilterDefinitions,
} = useFilter(); } = useFilter();
const setHotkeyScope = useSetHotkeyScope(); const setHotkeyScope = useSetHotkeyScope();
return ( return (
<DropdownMenuItemsContainer> <DropdownMenuItemsContainer>
{availableFilters.map((availableFilter, index) => ( {availableFilterDefinitions.map((availableFilterDefinition, index) => (
<MenuItem <MenuItem
key={`select-filter-${index}`} key={`select-filter-${index}`}
testId={`select-filter-${index}`} testId={`select-filter-${index}`}
onClick={() => { onClick={() => {
setFilterDefinitionUsedInDropdown(availableFilter); setFilterDefinitionUsedInDropdown(availableFilterDefinition);
if (availableFilter.type === 'entity') { if (availableFilterDefinition.type === 'entity') {
setHotkeyScope(RelationPickerHotkeyScope.RelationPicker); setHotkeyScope(RelationPickerHotkeyScope.RelationPicker);
} }
setSelectedOperandInDropdown( setSelectedOperandInDropdown(
getOperandsForFilterType(availableFilter.type)?.[0], getOperandsForFilterType(availableFilterDefinition.type)?.[0],
); );
setFilterDropdownSearchInput(''); setFilterDropdownSearchInput('');
}} }}
LeftIcon={availableFilter.Icon} LeftIcon={availableFilterDefinition.Icon}
text={availableFilter.label} text={availableFilterDefinition.label}
/> />
))} ))}
</DropdownMenuItemsContainer> </DropdownMenuItemsContainer>

View File

@ -2,16 +2,14 @@ import { ChangeEvent } from 'react';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { useRemoveFilter } from '../../../../views/hooks/useRemoveFilter';
import { useUpsertFilter } from '../../../../views/hooks/useUpsertFilter';
import { useFilter } from '../hooks/useFilter'; import { useFilter } from '../hooks/useFilter';
export const FilterDropdownNumberSearchInput = () => { export const FilterDropdownNumberSearchInput = () => {
const { selectedOperandInDropdown, filterDefinitionUsedInDropdown } = const {
useFilter(); selectedOperandInDropdown,
filterDefinitionUsedInDropdown,
const upsertFilter = useUpsertFilter(); selectFilter,
const removeFilter = useRemoveFilter(); } = useFilter();
return ( return (
filterDefinitionUsedInDropdown && filterDefinitionUsedInDropdown &&
@ -21,17 +19,13 @@ export const FilterDropdownNumberSearchInput = () => {
type="number" type="number"
placeholder={filterDefinitionUsedInDropdown.label} placeholder={filterDefinitionUsedInDropdown.label}
onChange={(event: ChangeEvent<HTMLInputElement>) => { onChange={(event: ChangeEvent<HTMLInputElement>) => {
if (event.target.value === '') { selectFilter?.({
removeFilter(filterDefinitionUsedInDropdown.key); fieldId: filterDefinitionUsedInDropdown.fieldId,
} else { value: event.target.value,
upsertFilter({ operand: selectedOperandInDropdown,
key: filterDefinitionUsedInDropdown.key, displayValue: event.target.value,
type: filterDefinitionUsedInDropdown.type, definition: filterDefinitionUsedInDropdown,
value: event.target.value, });
operand: selectedOperandInDropdown,
displayValue: event.target.value,
});
}
}} }}
/> />
) )

View File

@ -2,9 +2,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { useUpsertFilter } from '../../../../views/hooks/useUpsertFilter';
import { useFilter } from '../hooks/useFilter'; import { useFilter } from '../hooks/useFilter';
import { useFilterCurrentlyEdited } from '../hooks/useFilterCurrentlyEdited';
import { getOperandLabel } from '../utils/getOperandLabel'; import { getOperandLabel } from '../utils/getOperandLabel';
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType'; import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
@ -14,27 +12,25 @@ export const FilterDropdownOperandSelect = () => {
setSelectedOperandInDropdown, setSelectedOperandInDropdown,
isFilterDropdownOperandSelectUnfolded, isFilterDropdownOperandSelectUnfolded,
setIsFilterDropdownOperandSelectUnfolded, setIsFilterDropdownOperandSelectUnfolded,
selectedFilter,
selectFilter,
} = useFilter(); } = useFilter();
const operandsForFilterType = getOperandsForFilterType( const operandsForFilterType = getOperandsForFilterType(
filterDefinitionUsedInDropdown?.type, filterDefinitionUsedInDropdown?.type,
); );
const filterCurrentlyEdited = useFilterCurrentlyEdited();
const upsertFilter = useUpsertFilter();
const handleOperangeChange = (newOperand: ViewFilterOperand) => { const handleOperangeChange = (newOperand: ViewFilterOperand) => {
setSelectedOperandInDropdown(newOperand); setSelectedOperandInDropdown(newOperand);
setIsFilterDropdownOperandSelectUnfolded(false); setIsFilterDropdownOperandSelectUnfolded(false);
if (filterDefinitionUsedInDropdown && filterCurrentlyEdited) { if (filterDefinitionUsedInDropdown && selectedFilter) {
upsertFilter({ selectFilter?.({
key: filterCurrentlyEdited.key, fieldId: selectedFilter.fieldId,
displayValue: filterCurrentlyEdited.displayValue, displayValue: selectedFilter.displayValue,
operand: newOperand, operand: newOperand,
type: filterCurrentlyEdited.type, value: selectedFilter.value,
value: filterCurrentlyEdited.value, definition: filterDefinitionUsedInDropdown,
}); });
} }
}; };

View File

@ -2,10 +2,7 @@ import { ChangeEvent } from 'react';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { useRemoveFilter } from '../../../../views/hooks/useRemoveFilter';
import { useUpsertFilter } from '../../../../views/hooks/useUpsertFilter';
import { useFilter } from '../hooks/useFilter'; import { useFilter } from '../hooks/useFilter';
import { useFilterCurrentlyEdited } from '../hooks/useFilterCurrentlyEdited';
export const FilterDropdownTextSearchInput = () => { export const FilterDropdownTextSearchInput = () => {
const { const {
@ -13,13 +10,10 @@ export const FilterDropdownTextSearchInput = () => {
selectedOperandInDropdown, selectedOperandInDropdown,
filterDropdownSearchInput, filterDropdownSearchInput,
setFilterDropdownSearchInput, setFilterDropdownSearchInput,
selectedFilter,
selectFilter,
} = useFilter(); } = useFilter();
const upsertFilter = useUpsertFilter();
const removeFilter = useRemoveFilter();
const filterCurrentlyEdited = useFilterCurrentlyEdited();
return ( return (
filterDefinitionUsedInDropdown && filterDefinitionUsedInDropdown &&
selectedOperandInDropdown && ( selectedOperandInDropdown && (
@ -27,21 +21,17 @@ export const FilterDropdownTextSearchInput = () => {
autoFocus autoFocus
type="text" type="text"
placeholder={filterDefinitionUsedInDropdown.label} placeholder={filterDefinitionUsedInDropdown.label}
value={filterCurrentlyEdited?.value ?? filterDropdownSearchInput} value={selectedFilter?.value ?? filterDropdownSearchInput}
onChange={(event: ChangeEvent<HTMLInputElement>) => { onChange={(event: ChangeEvent<HTMLInputElement>) => {
setFilterDropdownSearchInput(event.target.value); setFilterDropdownSearchInput(event.target.value);
if (event.target.value === '') { selectFilter?.({
removeFilter(filterDefinitionUsedInDropdown.key); fieldId: filterDefinitionUsedInDropdown.fieldId,
} else { value: event.target.value,
upsertFilter({ operand: selectedOperandInDropdown,
key: filterDefinitionUsedInDropdown.key, displayValue: event.target.value,
type: filterDefinitionUsedInDropdown.type, definition: filterDefinitionUsedInDropdown,
value: event.target.value, });
operand: selectedOperandInDropdown,
displayValue: event.target.value,
});
}
}} }}
/> />
) )

View File

@ -21,13 +21,13 @@ export const SingleEntityFilterDropdownButton = ({
hotkeyScope: HotkeyScope; hotkeyScope: HotkeyScope;
}) => { }) => {
const { const {
availableFilters, availableFilterDefinitions,
selectedFilters, selectedFilter,
setFilterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown,
setSelectedOperandInDropdown, setSelectedOperandInDropdown,
} = useFilter(); } = useFilter();
const availableFilter = availableFilters[0]; const availableFilter = availableFilterDefinitions[0];
React.useEffect(() => { React.useEffect(() => {
setFilterDefinitionUsedInDropdown(availableFilter); setFilterDefinitionUsedInDropdown(availableFilter);
@ -48,11 +48,11 @@ export const SingleEntityFilterDropdownButton = ({
dropdownOffset={{ x: 0, y: -28 }} dropdownOffset={{ x: 0, y: -28 }}
clickableComponent={ clickableComponent={
<StyledHeaderDropdownButton> <StyledHeaderDropdownButton>
{selectedFilters[0] ? ( {selectedFilter ? (
<GenericEntityFilterChip <GenericEntityFilterChip
filter={selectedFilters[0]} filter={selectedFilter}
Icon={ Icon={
selectedFilters[0].operand === ViewFilterOperand.IsNotNull selectedFilter.operand === ViewFilterOperand.IsNotNull
? availableFilter.SelectAllIcon ? availableFilter.SelectAllIcon
: undefined : undefined
} }

View File

@ -1,6 +1,10 @@
import { useCallback } from 'react';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { useScopeInternalContextOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useScopeInternalContextOrThrow';
import { FilterScopeInternalContext } from '../scopes/scope-internal-context/FilterScopeInternalContext'; import { FilterScopeInternalContext } from '../scopes/scope-internal-context/FilterScopeInternalContext';
import { Filter } from '../types/Filter';
import { useFilterStates } from './useFilterStates'; import { useFilterStates } from './useFilterStates';
@ -14,8 +18,8 @@ export const useFilter = (props?: UseFilterProps) => {
props?.filterScopeId, props?.filterScopeId,
); );
const { const {
availableFilters, availableFilterDefinitions,
setAvailableFilters, setAvailableFilterDefinitions,
filterDefinitionUsedInDropdown, filterDefinitionUsedInDropdown,
setFilterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown,
filterDropdownSearchInput, filterDropdownSearchInput,
@ -26,16 +30,28 @@ export const useFilter = (props?: UseFilterProps) => {
setIsFilterDropdownOperandSelectUnfolded, setIsFilterDropdownOperandSelectUnfolded,
isFilterDropdownUnfolded, isFilterDropdownUnfolded,
setIsFilterDropdownUnfolded, setIsFilterDropdownUnfolded,
selectedFilters, selectedFilter,
setSelectedFilters, setSelectedFilter,
selectedOperandInDropdown, selectedOperandInDropdown,
setSelectedOperandInDropdown, setSelectedOperandInDropdown,
} = useFilterStates(scopeId); } = useFilterStates(scopeId);
const { onFilterSelect } = useScopeInternalContextOrThrow(
FilterScopeInternalContext,
);
const selectFilter = useCallback(
(filter: Filter) => {
setSelectedFilter(filter);
onFilterSelect?.(filter);
},
[setSelectedFilter, onFilterSelect],
);
return { return {
scopeId, scopeId,
availableFilters, availableFilterDefinitions,
setAvailableFilters, setAvailableFilterDefinitions,
filterDefinitionUsedInDropdown, filterDefinitionUsedInDropdown,
setFilterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown,
filterDropdownSearchInput, filterDropdownSearchInput,
@ -46,9 +62,10 @@ export const useFilter = (props?: UseFilterProps) => {
setIsFilterDropdownOperandSelectUnfolded, setIsFilterDropdownOperandSelectUnfolded,
isFilterDropdownUnfolded, isFilterDropdownUnfolded,
setIsFilterDropdownUnfolded, setIsFilterDropdownUnfolded,
selectedFilters, selectedFilter,
setSelectedFilters, setSelectedFilter,
selectedOperandInDropdown, selectedOperandInDropdown,
setSelectedOperandInDropdown, setSelectedOperandInDropdown,
selectFilter,
}; };
}; };

View File

@ -1,13 +0,0 @@
import { useMemo } from 'react';
import { useFilter } from './useFilter';
export const useFilterCurrentlyEdited = () => {
const { selectedFilters, filterDefinitionUsedInDropdown } = useFilter();
return useMemo(() => {
return selectedFilters?.find(
(filter) => filter.key === filterDefinitionUsedInDropdown?.key,
);
}, [filterDefinitionUsedInDropdown?.key, selectedFilters]);
};

View File

@ -1,19 +1,17 @@
import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2'; import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2';
import { availableFiltersScopedState } from '../states/availableFiltersScopedState'; import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState';
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState'; import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
import { filterDropdownSearchInputScopedState } from '../states/filterDropdownSearchInputScopedState'; import { filterDropdownSearchInputScopedState } from '../states/filterDropdownSearchInputScopedState';
import { filterDropdownSelectedEntityIdScopedState } from '../states/filterDropdownSelectedEntityIdScopedState'; import { filterDropdownSelectedEntityIdScopedState } from '../states/filterDropdownSelectedEntityIdScopedState';
import { isFilterDropdownOperandSelectUnfoldedScopedState } from '../states/isFilterDropdownOperandSelectUnfoldedScopedState'; import { isFilterDropdownOperandSelectUnfoldedScopedState } from '../states/isFilterDropdownOperandSelectUnfoldedScopedState';
import { isFilterDropdownUnfoldedScopedState } from '../states/isFilterDropdownUnfoldedScopedState'; import { isFilterDropdownUnfoldedScopedState } from '../states/isFilterDropdownUnfoldedScopedState';
import { selectedFiltersScopedState } from '../states/selectedFiltersScopedState'; import { selectedFilterScopedState } from '../states/selectedFilterScopedState';
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState'; import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
export const useFilterStates = (scopeId: string) => { export const useFilterStates = (scopeId: string) => {
const [availableFilters, setAvailableFilters] = useRecoilScopedStateV2( const [availableFilterDefinitions, setAvailableFilterDefinitions] =
availableFiltersScopedState, useRecoilScopedStateV2(availableFilterDefinitionsScopedState, scopeId);
scopeId,
);
const [filterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown] = const [filterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown] =
useRecoilScopedStateV2(filterDefinitionUsedInDropdownScopedState, scopeId); useRecoilScopedStateV2(filterDefinitionUsedInDropdownScopedState, scopeId);
@ -35,8 +33,8 @@ export const useFilterStates = (scopeId: string) => {
const [isFilterDropdownUnfolded, setIsFilterDropdownUnfolded] = const [isFilterDropdownUnfolded, setIsFilterDropdownUnfolded] =
useRecoilScopedStateV2(isFilterDropdownUnfoldedScopedState, scopeId); useRecoilScopedStateV2(isFilterDropdownUnfoldedScopedState, scopeId);
const [selectedFilters, setSelectedFilters] = useRecoilScopedStateV2( const [selectedFilter, setSelectedFilter] = useRecoilScopedStateV2(
selectedFiltersScopedState, selectedFilterScopedState,
scopeId, scopeId,
); );
@ -44,8 +42,8 @@ export const useFilterStates = (scopeId: string) => {
useRecoilScopedStateV2(selectedOperandInDropdownScopedState, scopeId); useRecoilScopedStateV2(selectedOperandInDropdownScopedState, scopeId);
return { return {
availableFilters, availableFilterDefinitions,
setAvailableFilters, setAvailableFilterDefinitions,
filterDefinitionUsedInDropdown, filterDefinitionUsedInDropdown,
setFilterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown,
filterDropdownSearchInput, filterDropdownSearchInput,
@ -56,8 +54,8 @@ export const useFilterStates = (scopeId: string) => {
setIsFilterDropdownOperandSelectUnfolded, setIsFilterDropdownOperandSelectUnfolded,
isFilterDropdownUnfolded, isFilterDropdownUnfolded,
setIsFilterDropdownUnfolded, setIsFilterDropdownUnfolded,
selectedFilters, selectedFilter,
setSelectedFilters, setSelectedFilter,
selectedOperandInDropdown, selectedOperandInDropdown,
setSelectedOperandInDropdown, setSelectedOperandInDropdown,
}; };

View File

@ -2,25 +2,31 @@ import { ReactNode } from 'react';
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition'; import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
import { Filter } from '../types/Filter';
import { FilterScopeInitEffect } from './init-effect/FilterScopeInitEffect'; import { FilterScopeInitEffect } from './init-effect/FilterScopeInitEffect';
import { FilterScopeInternalContext } from './scope-internal-context/FilterScopeInternalContext'; import { FilterScopeInternalContext } from './scope-internal-context/FilterScopeInternalContext';
type FilterScopeProps = { type FilterScopeProps = {
children: ReactNode; children: ReactNode;
filterScopeId: string; filterScopeId: string;
availableFilters?: FilterDefinition[]; availableFilterDefinitions?: FilterDefinition[];
onFilterSelect?: (filter: Filter) => void;
}; };
export const FilterScope = ({ export const FilterScope = ({
children, children,
filterScopeId, filterScopeId,
availableFilters, availableFilterDefinitions,
onFilterSelect,
}: FilterScopeProps) => { }: FilterScopeProps) => {
return ( return (
<FilterScopeInternalContext.Provider value={{ scopeId: filterScopeId }}> <FilterScopeInternalContext.Provider
value={{ scopeId: filterScopeId, onFilterSelect }}
>
<FilterScopeInitEffect <FilterScopeInitEffect
filterScopeId={filterScopeId} filterScopeId={filterScopeId}
availableFilters={availableFilters} availableFilterDefinitions={availableFilterDefinitions}
/> />
{children} {children}
</FilterScopeInternalContext.Provider> </FilterScopeInternalContext.Provider>

View File

@ -6,20 +6,20 @@ import { useFilterStates } from '../../hooks/useFilterStates';
type FilterScopeInitEffectProps = { type FilterScopeInitEffectProps = {
filterScopeId: string; filterScopeId: string;
availableFilters?: FilterDefinition[]; availableFilterDefinitions?: FilterDefinition[];
}; };
export const FilterScopeInitEffect = ({ export const FilterScopeInitEffect = ({
filterScopeId, filterScopeId,
availableFilters, availableFilterDefinitions,
}: FilterScopeInitEffectProps) => { }: FilterScopeInitEffectProps) => {
const { setAvailableFilters } = useFilterStates(filterScopeId); const { setAvailableFilterDefinitions } = useFilterStates(filterScopeId);
useEffect(() => { useEffect(() => {
if (availableFilters) { if (availableFilterDefinitions) {
setAvailableFilters(availableFilters); setAvailableFilterDefinitions(availableFilterDefinitions);
} }
}, [availableFilters, setAvailableFilters]); }, [availableFilterDefinitions, setAvailableFilterDefinitions]);
return <></>; return <></>;
}; };

View File

@ -1,8 +1,10 @@
import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey'; import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey';
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
import { Filter } from '../../types/Filter';
type FilterScopeInternalContextProps = ScopedStateKey & { type FilterScopeInternalContextProps = ScopedStateKey & {
test?: string; onFilterSelect?: (sort: Filter) => void;
}; };
export const FilterScopeInternalContext = export const FilterScopeInternalContext =

View File

@ -1,9 +1,9 @@
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition'; import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState'; import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
export const availableFiltersScopedState = createScopedState< export const availableFilterDefinitionsScopedState = createScopedState<
FilterDefinition[] FilterDefinition[]
>({ >({
key: 'availableFiltersScopedState', key: 'availableFilterDefinitionsScopedState',
defaultValue: [], defaultValue: [],
}); });

View File

@ -0,0 +1,8 @@
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
import { Filter } from '../types/Filter';
export const selectedFilterScopedState = createScopedState<Filter | undefined>({
key: 'selectedFilterScopedState',
defaultValue: undefined,
});

View File

@ -1,8 +0,0 @@
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
import { Filter } from '../types/Filter';
export const selectedFiltersScopedState = createScopedState<Filter[]>({
key: 'selectedFiltersScopedState',
defaultValue: [],
});

View File

@ -1,12 +1,12 @@
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { FilterType } from './FilterType'; import { FilterDefinition } from './FilterDefinition';
export type Filter = { export type Filter = {
key: string; fieldId: string;
type: FilterType;
value: string; value: string;
displayValue: string; displayValue: string;
displayAvatarUrl?: string; displayAvatarUrl?: string;
operand: ViewFilterOperand; operand: ViewFilterOperand;
definition: FilterDefinition;
}; };

View File

@ -3,7 +3,7 @@ import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { FilterType } from './FilterType'; import { FilterType } from './FilterType';
export type FilterDefinition = { export type FilterDefinition = {
key: string; fieldId: string;
label: string; label: string;
Icon: IconComponent; Icon: IconComponent;
type: FilterType; type: FilterType;

View File

@ -1,5 +1,5 @@
import { FilterDefinition } from './FilterDefinition'; import { FilterDefinition } from './FilterDefinition';
export type FilterDefinitionByEntity<T> = FilterDefinition & { export type FilterDefinitionByEntity<T> = FilterDefinition & {
key: keyof T; fieldId: keyof T;
}; };

View File

@ -3,28 +3,39 @@ import { QueryMode } from '~/generated/graphql';
import { Filter } from '../types/Filter'; import { Filter } from '../types/Filter';
export const turnFilterIntoWhereClause = (filter: Filter) => { type FilterToTurnIntoWhereClause = Omit<Filter, 'definition'> & {
definition: {
type: Filter['definition']['type'];
};
};
export const turnFilterIntoWhereClause = (
filter: FilterToTurnIntoWhereClause | undefined,
) => {
if (!filter) {
return {};
}
switch (filter.operand) { switch (filter.operand) {
case ViewFilterOperand.IsNotNull: case ViewFilterOperand.IsNotNull:
return { return {
[filter.key]: { [filter.fieldId]: {
not: null, not: null,
}, },
}; };
default: default:
switch (filter.type) { switch (filter.definition.type) {
case 'text': case 'text':
switch (filter.operand) { switch (filter.operand) {
case ViewFilterOperand.Contains: case ViewFilterOperand.Contains:
return { return {
[filter.key]: { [filter.fieldId]: {
contains: filter.value, contains: filter.value,
mode: QueryMode.Insensitive, mode: QueryMode.Insensitive,
}, },
}; };
case ViewFilterOperand.DoesNotContain: case ViewFilterOperand.DoesNotContain:
return { return {
[filter.key]: { [filter.fieldId]: {
not: { not: {
contains: filter.value, contains: filter.value,
mode: QueryMode.Insensitive, mode: QueryMode.Insensitive,
@ -33,64 +44,64 @@ export const turnFilterIntoWhereClause = (filter: Filter) => {
}; };
default: default:
throw new Error( throw new Error(
`Unknown operand ${filter.operand} for ${filter.type} filter`, `Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
); );
} }
case 'number': case 'number':
switch (filter.operand) { switch (filter.operand) {
case ViewFilterOperand.GreaterThan: case ViewFilterOperand.GreaterThan:
return { return {
[filter.key]: { [filter.fieldId]: {
gte: parseFloat(filter.value), gte: parseFloat(filter.value),
}, },
}; };
case ViewFilterOperand.LessThan: case ViewFilterOperand.LessThan:
return { return {
[filter.key]: { [filter.fieldId]: {
lte: parseFloat(filter.value), lte: parseFloat(filter.value),
}, },
}; };
default: default:
throw new Error( throw new Error(
`Unknown operand ${filter.operand} for ${filter.type} filter`, `Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
); );
} }
case 'date': case 'date':
switch (filter.operand) { switch (filter.operand) {
case ViewFilterOperand.GreaterThan: case ViewFilterOperand.GreaterThan:
return { return {
[filter.key]: { [filter.fieldId]: {
gte: filter.value, gte: filter.value,
}, },
}; };
case ViewFilterOperand.LessThan: case ViewFilterOperand.LessThan:
return { return {
[filter.key]: { [filter.fieldId]: {
lte: filter.value, lte: filter.value,
}, },
}; };
default: default:
throw new Error( throw new Error(
`Unknown operand ${filter.operand} for ${filter.type} filter`, `Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
); );
} }
case 'entity': case 'entity':
switch (filter.operand) { switch (filter.operand) {
case ViewFilterOperand.Is: case ViewFilterOperand.Is:
return { return {
[filter.key]: { [filter.fieldId]: {
equals: filter.value, equals: filter.value,
}, },
}; };
case ViewFilterOperand.IsNot: case ViewFilterOperand.IsNot:
return { return {
[filter.key]: { [filter.fieldId]: {
not: { equals: filter.value }, not: { equals: filter.value },
}, },
}; };
default: default:
throw new Error( throw new Error(
`Unknown operand ${filter.operand} for ${filter.type} filter`, `Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
); );
} }
default: default:

View File

@ -35,7 +35,7 @@ export const SortDropdownButton = ({
setSelectedSortDirection('asc'); setSelectedSortDirection('asc');
}, []); }, []);
const { availableSorts, onSortAdd, isSortSelected } = useSort(); const { availableSortDefinitions, onSortSelect, isSortSelected } = useSort();
const { toggleDropdown } = useDropdown({ const { toggleDropdown } = useDropdown({
dropdownScopeId: SortDropdownId, dropdownScopeId: SortDropdownId,
@ -48,8 +48,8 @@ export const SortDropdownButton = ({
const handleAddSort = (selectedSortDefinition: SortDefinition) => { const handleAddSort = (selectedSortDefinition: SortDefinition) => {
toggleDropdown(); toggleDropdown();
onSortAdd?.({ onSortSelect?.({
key: selectedSortDefinition.key, fieldId: selectedSortDefinition.fieldId,
direction: selectedSortDirection, direction: selectedSortDirection,
definition: selectedSortDefinition, definition: selectedSortDefinition,
}); });
@ -96,7 +96,7 @@ export const SortDropdownButton = ({
</DropdownMenuHeader> </DropdownMenuHeader>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItemsContainer> <DropdownMenuItemsContainer>
{availableSorts.map((availableSort, index) => ( {availableSortDefinitions.map((availableSort, index) => (
<MenuItem <MenuItem
testId={`select-sort-${index}`} testId={`select-sort-${index}`}
key={index} key={index}

View File

@ -15,22 +15,22 @@ export const useSort = (props?: UseSortProps) => {
props?.sortScopeId, props?.sortScopeId,
); );
const { const {
availableSorts, availableSortDefinitions,
setAvailableSorts, setAvailableSortDefinitions,
isSortSelected, isSortSelected,
setIsSortSelected, setIsSortSelected,
} = useSortStates(scopeId); } = useSortStates(scopeId);
const { onSortAdd } = useScopeInternalContextOrThrow( const { onSortSelect } = useScopeInternalContextOrThrow(
SortScopeInternalContext, SortScopeInternalContext,
); );
return { return {
onSortAdd, onSortSelect,
scopeId, scopeId,
availableSorts, availableSortDefinitions,
isSortSelected, isSortSelected,
setIsSortSelected, setIsSortSelected,
setAvailableSorts, setAvailableSortDefinitions,
}; };
}; };

View File

@ -1,13 +1,11 @@
import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2'; import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2';
import { availableSortsScopedState } from '@/views/states/availableSortsScopedState'; import { availableSortDefinitionsScopedState } from '@/views/states/availableSortDefinitionsScopedState';
import { isSortSelectedScopedState } from '../states/isSortSelectedScopedState'; import { isSortSelectedScopedState } from '../states/isSortSelectedScopedState';
export const useSortStates = (scopeId: string) => { export const useSortStates = (scopeId: string) => {
const [availableSorts, setAvailableSorts] = useRecoilScopedStateV2( const [availableSortDefinitions, setAvailableSortDefinitions] =
availableSortsScopedState, useRecoilScopedStateV2(availableSortDefinitionsScopedState, scopeId);
scopeId,
);
const [isSortSelected, setIsSortSelected] = useRecoilScopedStateV2( const [isSortSelected, setIsSortSelected] = useRecoilScopedStateV2(
isSortSelectedScopedState, isSortSelectedScopedState,
@ -15,8 +13,8 @@ export const useSortStates = (scopeId: string) => {
); );
return { return {
availableSorts, availableSortDefinitions,
setAvailableSorts, setAvailableSortDefinitions,
isSortSelected, isSortSelected,
setIsSortSelected, setIsSortSelected,
}; };

View File

@ -9,23 +9,23 @@ import { SortScopeInternalContext } from './scope-internal-context/SortScopeInte
type SortScopeProps = { type SortScopeProps = {
children: ReactNode; children: ReactNode;
sortScopeId: string; sortScopeId: string;
availableSorts?: SortDefinition[]; availableSortDefinitions?: SortDefinition[];
onSortAdd?: (sort: Sort) => void | Promise<void>; onSortSelect?: (sort: Sort) => void | Promise<void>;
}; };
export const SortScope = ({ export const SortScope = ({
children, children,
sortScopeId, sortScopeId,
availableSorts, availableSortDefinitions,
onSortAdd, onSortSelect,
}: SortScopeProps) => { }: SortScopeProps) => {
return ( return (
<SortScopeInternalContext.Provider <SortScopeInternalContext.Provider
value={{ scopeId: sortScopeId, onSortAdd }} value={{ scopeId: sortScopeId, onSortSelect }}
> >
<SortScopeInitEffect <SortScopeInitEffect
sortScopeId={sortScopeId} sortScopeId={sortScopeId}
availableSorts={availableSorts} availableSortDefinitions={availableSortDefinitions}
/> />
{children} {children}
</SortScopeInternalContext.Provider> </SortScopeInternalContext.Provider>

View File

@ -6,20 +6,20 @@ import { useSortStates } from '../../hooks/useSortStates';
type SortScopeInitEffectProps = { type SortScopeInitEffectProps = {
sortScopeId: string; sortScopeId: string;
availableSorts?: SortDefinition[]; availableSortDefinitions?: SortDefinition[];
}; };
export const SortScopeInitEffect = ({ export const SortScopeInitEffect = ({
sortScopeId, sortScopeId,
availableSorts, availableSortDefinitions,
}: SortScopeInitEffectProps) => { }: SortScopeInitEffectProps) => {
const { setAvailableSorts } = useSortStates(sortScopeId); const { setAvailableSortDefinitions } = useSortStates(sortScopeId);
useEffect(() => { useEffect(() => {
if (availableSorts) { if (availableSortDefinitions) {
setAvailableSorts(availableSorts); setAvailableSortDefinitions(availableSortDefinitions);
} }
}, [availableSorts, setAvailableSorts]); }, [availableSortDefinitions, setAvailableSortDefinitions]);
return <></>; return <></>;
}; };

View File

@ -2,11 +2,9 @@ import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/type
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
import { Sort } from '../../types/Sort'; import { Sort } from '../../types/Sort';
import { SortDefinition } from '../../types/SortDefinition';
type SortScopeInternalContextProps = ScopedStateKey & { type SortScopeInternalContextProps = ScopedStateKey & {
onSortAdd?: (sort: Sort) => void; onSortSelect?: (sort: Sort) => void;
availableSorts?: SortDefinition[];
}; };
export const SortScopeInternalContext = export const SortScopeInternalContext =

View File

@ -2,7 +2,7 @@ import { SortDefinition } from './SortDefinition';
import { SortDirection } from './SortDirection'; import { SortDirection } from './SortDirection';
export type Sort = { export type Sort = {
key: string; fieldId: string;
direction: SortDirection; direction: SortDirection;
definition: SortDefinition; definition: SortDefinition;
}; };

View File

@ -3,7 +3,7 @@ import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { SortDirection } from './SortDirection'; import { SortDirection } from './SortDirection';
export type SortDefinition = { export type SortDefinition = {
key: string; fieldId: string;
label: string; label: string;
Icon?: IconComponent; Icon?: IconComponent;
getOrderByTemplate?: (direction: SortDirection) => any[]; getOrderByTemplate?: (direction: SortDirection) => any[];

View File

@ -10,7 +10,7 @@ export const reduceSortsToOrderBy = (sorts: Sort[]): any[] =>
if (sort.definition.getOrderByTemplate) { if (sort.definition.getOrderByTemplate) {
return sort.definition.getOrderByTemplate(direction); return sort.definition.getOrderByTemplate(direction);
} else { } else {
return [{ [sort.definition.key]: direction }]; return [{ [sort.definition.fieldId]: direction }];
} }
}) })
.flat(); .flat();

View File

@ -2,8 +2,9 @@ import { useView } from '@/views/hooks/useView';
import { Dropdown } from '../../dropdown/components/Dropdown'; import { Dropdown } from '../../dropdown/components/Dropdown';
import { DropdownScope } from '../../dropdown/scopes/DropdownScope'; import { DropdownScope } from '../../dropdown/scopes/DropdownScope';
import { BoardScopeIds } from '../types/enums/BoardScopeIds'; import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope';
import { BoardOptionsDropdownId } from './constants/BoardOptionsDropdownId';
import { BoardOptionsDropdownButton } from './BoardOptionsDropdownButton'; import { BoardOptionsDropdownButton } from './BoardOptionsDropdownButton';
import { import {
BoardOptionsDropdownContent, BoardOptionsDropdownContent,
@ -12,26 +13,22 @@ import {
type BoardOptionsDropdownProps = Pick< type BoardOptionsDropdownProps = Pick<
BoardOptionsDropdownContentProps, BoardOptionsDropdownContentProps,
'customHotkeyScope' | 'onStageAdd' 'onStageAdd'
>; >;
export const BoardOptionsDropdown = ({ export const BoardOptionsDropdown = ({
customHotkeyScope,
onStageAdd, onStageAdd,
}: BoardOptionsDropdownProps) => { }: BoardOptionsDropdownProps) => {
const { setViewEditMode } = useView(); const { setViewEditMode } = useView();
return ( return (
<DropdownScope dropdownScopeId={BoardScopeIds.OptionsDropdown}> <DropdownScope dropdownScopeId={BoardOptionsDropdownId}>
<Dropdown <Dropdown
clickableComponent={<BoardOptionsDropdownButton />} clickableComponent={<BoardOptionsDropdownButton />}
dropdownComponents={ dropdownComponents={
<BoardOptionsDropdownContent <BoardOptionsDropdownContent onStageAdd={onStageAdd} />
customHotkeyScope={customHotkeyScope}
onStageAdd={onStageAdd}
/>
} }
dropdownHotkeyScope={customHotkeyScope} dropdownHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
onClickOutside={() => setViewEditMode('none')} onClickOutside={() => setViewEditMode('none')}
dropdownMenuWidth={170} dropdownMenuWidth={170}
/> />

View File

@ -1,12 +1,8 @@
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton'; import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { BoardScopeIds } from '../types/enums/BoardScopeIds';
export const BoardOptionsDropdownButton = () => { export const BoardOptionsDropdownButton = () => {
const { isDropdownOpen, toggleDropdown } = useDropdown({ const { isDropdownOpen, toggleDropdown } = useDropdown();
dropdownScopeId: BoardScopeIds.OptionsDropdown,
});
const handleClick = () => { const handleClick = () => {
toggleDropdown(); toggleDropdown();

View File

@ -1,5 +1,5 @@
import { useContext, useRef, useState } from 'react'; import { useContext, useRef, useState } from 'react';
import { useRecoilCallback, useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
@ -22,25 +22,20 @@ import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemN
import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle'; import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle';
import { ThemeColor } from '@/ui/theme/constants/colors'; import { ThemeColor } from '@/ui/theme/constants/colors';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection'; import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
import { useView } from '@/views/hooks/useView'; import { useView } from '@/views/hooks/useView';
import { useViewInternalStates } from '@/views/hooks/useViewInternalStates'; import { useViewGetStates } from '@/views/hooks/useViewGetStates';
import { viewEditModeScopedState } from '@/views/states/viewEditModeScopedState';
import { useBoardCardFields } from '../hooks/useBoardCardFields'; import { useBoardCardFields } from '../hooks/useBoardCardFields';
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
import { boardColumnsState } from '../states/boardColumnsState'; import { boardColumnsState } from '../states/boardColumnsState';
import { isCompactViewEnabledState } from '../states/isCompactViewEnabledState'; import { isCompactViewEnabledState } from '../states/isCompactViewEnabledState';
import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsFamilyState';
import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector'; import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector';
import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector'; import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector';
import { BoardColumnDefinition } from '../types/BoardColumnDefinition'; import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope';
export type BoardOptionsDropdownContentProps = { export type BoardOptionsDropdownContentProps = {
customHotkeyScope: HotkeyScope;
onStageAdd?: (boardColumn: BoardColumnDefinition) => void; onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
}; };
@ -54,15 +49,12 @@ type ColumnForCreate = {
}; };
export const BoardOptionsDropdownContent = ({ export const BoardOptionsDropdownContent = ({
customHotkeyScope,
onStageAdd, onStageAdd,
}: BoardOptionsDropdownContentProps) => { }: BoardOptionsDropdownContentProps) => {
const { setViewEditMode, createView, currentViewId } = useView(); const { setViewEditMode, handleViewNameSubmit } = useView();
const { viewEditMode, currentView } = useViewInternalStates(); const { viewEditMode, currentView } = useViewGetStates();
const { BoardRecoilScopeContext } = useContext(BoardContext); const { BoardRecoilScopeContext } = useContext(BoardContext);
const boardRecoilScopeId = useRecoilScopeId(BoardRecoilScopeContext);
const stageInputRef = useRef<HTMLInputElement>(null); const stageInputRef = useRef<HTMLInputElement>(null);
const viewEditInputRef = useRef<HTMLInputElement>(null); const viewEditInputRef = useRef<HTMLInputElement>(null);
@ -104,31 +96,6 @@ export const BoardOptionsDropdownContent = ({
onStageAdd?.(columnToCreate); onStageAdd?.(columnToCreate);
}; };
const handleViewNameSubmit = useRecoilCallback(
({ set, snapshot }) =>
async () => {
const viewEditMode = snapshot
.getLoadable(viewEditModeScopedState({ scopeId: boardRecoilScopeId }))
.getValue();
if (!viewEditMode) {
return;
}
const boardCardFields = await snapshot.getPromise(
boardCardFieldsScopedState(boardRecoilScopeId),
);
const isCreateMode = viewEditMode === 'create';
const name = viewEditInputRef.current?.value;
if (isCreateMode && name) {
await createView(name);
set(savedBoardCardFieldsFamilyState(currentViewId), boardCardFields);
}
},
[boardRecoilScopeId, createView, currentViewId],
);
const resetMenu = () => setCurrentMenu(undefined); const resetMenu = () => setCurrentMenu(undefined);
const handleMenuNavigate = (menu: BoardOptionsMenu) => { const handleMenuNavigate = (menu: BoardOptionsMenu) => {
@ -146,43 +113,39 @@ export const BoardOptionsDropdownContent = ({
setViewEditMode('none'); setViewEditMode('none');
closeDropdown(); closeDropdown();
}, },
customHotkeyScope.scope, BoardOptionsHotkeyScope.Dropdown,
); );
useScopedHotkeys( useScopedHotkeys(
Key.Enter, Key.Enter,
() => { () => {
const name = viewEditInputRef.current?.value;
resetMenu();
setViewEditMode('none');
closeDropdown();
handleStageSubmit(); handleStageSubmit();
handleViewNameSubmit(); handleViewNameSubmit(name);
closeDropdown(); closeDropdown();
}, },
customHotkeyScope.scope, BoardOptionsHotkeyScope.Dropdown,
); );
return ( return (
<> <>
{!currentMenu && ( {!currentMenu && (
<> <>
{viewEditMode && ( <DropdownMenuInput
<DropdownMenuInput ref={viewEditInputRef}
ref={viewEditInputRef} autoFocus={viewEditMode !== 'none'}
autoFocus={viewEditMode !== 'none'} placeholder={
placeholder={ viewEditMode === 'create'
viewEditMode === 'create' ? 'New view'
? 'New view' : viewEditMode === 'edit'
: viewEditMode === 'edit' ? 'View name'
? 'View name' : ''
: '' }
} defaultValue={viewEditMode === 'create' ? '' : currentView?.name}
defaultValue={ />
viewEditMode === 'create'
? ''
: viewEditMode === 'edit'
? currentView?.name
: ''
}
/>
)}
<DropdownMenuSeparator /> <DropdownMenuSeparator />
<DropdownMenuItemsContainer> <DropdownMenuItemsContainer>
<MenuItemNavigate <MenuItemNavigate

View File

@ -40,6 +40,8 @@ export type EntityBoardProps = {
const StyledWrapper = styled.div` const StyledWrapper = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%;
overflow: hidden;
width: 100%; width: 100%;
`; `;

View File

@ -0,0 +1,2 @@
// We should either apply the constant all caps case or maybe define a more general enum to store those ids ?
export const BoardOptionsDropdownId = 'board-options';

View File

@ -7,6 +7,6 @@ import { PipelineProgress } from '~/generated/graphql';
export type BoardOptions = { export type BoardOptions = {
newCardComponent: React.ReactNode; newCardComponent: React.ReactNode;
CardComponent: ComponentType; CardComponent: ComponentType;
filters: FilterDefinitionByEntity<PipelineProgress>[]; filterDefinitions: FilterDefinitionByEntity<PipelineProgress>[];
sorts: SortDefinition[]; sortDefinitions: SortDefinition[];
}; };

View File

@ -1,3 +0,0 @@
export enum BoardScopeIds {
OptionsDropdown = 'board-options',
}

View File

@ -10,7 +10,7 @@ import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useView } from '@/views/hooks/useView'; import { useView } from '@/views/hooks/useView';
import { useViewInternalStates } from '../hooks/useViewInternalStates'; import { useViewGetStates } from '../hooks/useViewGetStates';
const StyledContainer = styled.div` const StyledContainer = styled.div`
display: inline-flex; display: inline-flex;
@ -28,7 +28,7 @@ export const UpdateViewButtonGroup = ({
}: UpdateViewButtonGroupProps) => { }: UpdateViewButtonGroupProps) => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const { updateCurrentView, setViewEditMode } = useView(); const { updateCurrentView, setViewEditMode } = useView();
const { canPersistFilters, canPersistSorts } = useViewInternalStates(); const { canPersistFilters, canPersistSorts } = useViewGetStates();
const canPersistView = canPersistFilters || canPersistSorts; const canPersistView = canPersistFilters || canPersistSorts;

View File

@ -9,7 +9,7 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { TopBar } from '@/ui/layout/top-bar/TopBar'; import { TopBar } from '@/ui/layout/top-bar/TopBar';
import { useView } from '../hooks/useView'; import { useView } from '../hooks/useView';
import { useViewInternalStates } from '../hooks/useViewInternalStates'; import { useViewGetStates } from '../hooks/useViewGetStates';
import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope'; import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup'; import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
@ -31,18 +31,20 @@ export const ViewBar = ({
const { openDropdown: openOptionsDropdownButton } = useDropdown({ const { openDropdown: openOptionsDropdownButton } = useDropdown({
dropdownScopeId: optionsDropdownScopeId, dropdownScopeId: optionsDropdownScopeId,
}); });
const { upsertViewSort } = useView(); const { upsertViewSort, upsertViewFilter } = useView();
const { availableFilters, availableSorts } = useViewInternalStates(); const { availableFilterDefinitions, availableSortDefinitions } =
useViewGetStates();
return ( return (
<FilterScope <FilterScope
filterScopeId="view-filter" filterScopeId="view-filter"
availableFilters={availableFilters} availableFilterDefinitions={availableFilterDefinitions}
onFilterSelect={upsertViewFilter}
> >
<SortScope <SortScope
sortScopeId="view-sort" sortScopeId="view-sort"
availableSorts={availableSorts} availableSortDefinitions={availableSortDefinitions}
onSortAdd={upsertViewSort} onSortSelect={upsertViewSort}
> >
<ViewBarEffect /> <ViewBarEffect />
<TopBar <TopBar
@ -51,6 +53,7 @@ export const ViewBar = ({
<ViewsDropdownButton <ViewsDropdownButton
onViewEditModeChange={openOptionsDropdownButton} onViewEditModeChange={openOptionsDropdownButton}
hotkeyScope={{ scope: ViewsHotkeyScope.ListDropdown }} hotkeyScope={{ scope: ViewsHotkeyScope.ListDropdown }}
optionsDropdownScopeId={optionsDropdownScopeId}
/> />
} }
displayBottomBorder={false} displayBottomBorder={false}

View File

@ -5,9 +5,8 @@ import { AddFilterFromDropdownButton } from '@/ui/data/filter/components/AddFilt
import { getOperandLabelShort } from '@/ui/data/filter/utils/getOperandLabel'; import { getOperandLabelShort } from '@/ui/data/filter/utils/getOperandLabel';
import { IconArrowDown, IconArrowUp } from '@/ui/display/icon/index'; import { IconArrowDown, IconArrowUp } from '@/ui/display/icon/index';
import { useRemoveFilter } from '../hooks/useRemoveFilter';
import { useView } from '../hooks/useView'; import { useView } from '../hooks/useView';
import { useViewInternalStates } from '../hooks/useViewInternalStates'; import { useViewGetStates } from '../hooks/useViewGetStates';
import SortOrFilterChip from './SortOrFilterChip'; import SortOrFilterChip from './SortOrFilterChip';
@ -90,43 +89,23 @@ export const ViewBarDetails = ({
}: ViewBarDetailsProps) => { }: ViewBarDetailsProps) => {
const { const {
currentViewSorts, currentViewSorts,
setCurrentViewSorts,
availableFilters,
currentViewFilters, currentViewFilters,
canPersistFilters, canPersistFilters,
canPersistSorts, canPersistSorts,
isViewBarExpanded, isViewBarExpanded,
} = useViewInternalStates(); } = useViewGetStates();
const { resetViewBar } = useView(); const { resetViewBar, removeViewSort, removeViewFilter } = useView();
const canPersistView = canPersistFilters || canPersistSorts; const canPersistView = canPersistFilters || canPersistSorts;
const filtersWithDefinition = currentViewFilters?.map((filter) => {
const filterDefinition = availableFilters.find((availableFilter) => {
return availableFilter.key === filter.key;
});
return {
...filter,
...filterDefinition,
};
});
const removeFilter = useRemoveFilter();
const handleCancelClick = () => { const handleCancelClick = () => {
resetViewBar(); resetViewBar();
}; };
const handleSortRemove = (sortKey: string) =>
setCurrentViewSorts?.((previousSorts) =>
previousSorts.filter((sort) => sort.key !== sortKey),
);
const shouldExpandViewBar = const shouldExpandViewBar =
canPersistView || canPersistView ||
((filtersWithDefinition?.length || currentViewSorts?.length) && ((currentViewSorts?.length || currentViewFilters?.length) &&
isViewBarExpanded); isViewBarExpanded);
if (!shouldExpandViewBar) { if (!shouldExpandViewBar) {
@ -140,32 +119,32 @@ export const ViewBarDetails = ({
{currentViewSorts?.map((sort) => { {currentViewSorts?.map((sort) => {
return ( return (
<SortOrFilterChip <SortOrFilterChip
key={sort.key} key={sort.fieldId}
testId={sort.key} testId={sort.fieldId}
labelValue={sort.definition.label} labelValue={sort.definition.label}
Icon={sort.direction === 'desc' ? IconArrowDown : IconArrowUp} Icon={sort.direction === 'desc' ? IconArrowDown : IconArrowUp}
isSort isSort
onRemove={() => handleSortRemove(sort.key)} onRemove={() => removeViewSort(sort.fieldId)}
/> />
); );
})} })}
{!!currentViewSorts?.length && !!filtersWithDefinition?.length && ( {!!currentViewSorts?.length && !!currentViewFilters?.length && (
<StyledSeperatorContainer> <StyledSeperatorContainer>
<StyledSeperator /> <StyledSeperator />
</StyledSeperatorContainer> </StyledSeperatorContainer>
)} )}
{filtersWithDefinition?.map((filter) => { {currentViewFilters?.map((filter) => {
return ( return (
<SortOrFilterChip <SortOrFilterChip
key={filter.key} key={filter.fieldId}
testId={filter.key} testId={filter.fieldId}
labelKey={filter.label} labelKey={filter.definition.label}
labelValue={`${getOperandLabelShort(filter.operand)} ${ labelValue={`${getOperandLabelShort(filter.operand)} ${
filter.displayValue filter.displayValue
}`} }`}
Icon={filter.Icon} Icon={filter.definition.Icon}
onRemove={() => { onRemove={() => {
removeFilter(filter.key); removeViewFilter(filter.fieldId);
}} }}
/> />
); );

View File

@ -8,28 +8,65 @@ import { assertNotNull } from '~/utils/assert';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { useView } from '../hooks/useView'; import { useView } from '../hooks/useView';
import { useViewInternalStates } from '../hooks/useViewInternalStates'; import { useViewGetStates } from '../hooks/useViewGetStates';
import { availableFieldsScopedState } from '../states/availableFieldsScopedState'; import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState';
import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState';
import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState';
import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState'; import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState';
import { onViewFiltersChangeScopedState } from '../states/onViewFiltersChangeScopedState';
import { onViewSortsChangeScopedState } from '../states/onViewSortsChangeScopedState';
import { savedViewFieldsScopedFamilyState } from '../states/savedViewFieldsScopedFamilyState'; import { savedViewFieldsScopedFamilyState } from '../states/savedViewFieldsScopedFamilyState';
import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState';
import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState';
import { viewsScopedState } from '../states/viewsScopedState'; import { viewsScopedState } from '../states/viewsScopedState';
import { View } from '../types/View'; import { View } from '../types/View';
import { ViewField } from '../types/ViewField'; import { ViewField } from '../types/ViewField';
import { ViewFilter } from '../types/ViewFilter';
import { ViewSort } from '../types/ViewSort';
export const ViewBarEffect = () => { export const ViewBarEffect = () => {
const { const {
scopeId: viewScopeId, scopeId: viewScopeId,
setCurrentViewFields, setCurrentViewFields,
setSavedViewFields, setSavedViewFields,
setCurrentViewFilters,
setSavedViewFilters,
setCurrentViewSorts,
setSavedViewSorts,
currentViewId, currentViewId,
setViews, setViews,
changeView, loadView,
changeViewInUrl,
setCurrentViewId, setCurrentViewId,
} = useView(); } = useView();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const { viewType, viewObjectId } = useViewInternalStates(viewScopeId); const { viewType, viewObjectId } = useViewGetStates(viewScopeId);
useFindManyObjects({
objectNamePlural: 'viewsV2',
filter: { type: { eq: viewType }, objectId: { eq: viewObjectId } },
onCompleted: useRecoilCallback(
({ snapshot }) =>
async (data: PaginatedObjectTypeResults<View>) => {
const nextViews = data.edges.map((view) => ({
id: view.node.id,
name: view.node.name,
objectId: view.node.objectId,
}));
const views = snapshot
.getLoadable(viewsScopedState({ scopeId: viewScopeId }))
.getValue();
if (!isDeeplyEqual(views, nextViews)) setViews(nextViews);
if (!nextViews.length) return;
if (!currentViewId) return changeViewInUrl(nextViews[0].id);
},
),
});
useFindManyObjects({ useFindManyObjects({
objectNamePlural: 'viewFieldsV2', objectNamePlural: 'viewFieldsV2',
@ -38,7 +75,9 @@ export const ViewBarEffect = () => {
({ snapshot }) => ({ snapshot }) =>
async (data: PaginatedObjectTypeResults<ViewField>) => { async (data: PaginatedObjectTypeResults<ViewField>) => {
const availableFields = snapshot const availableFields = snapshot
.getLoadable(availableFieldsScopedState({ scopeId: viewScopeId })) .getLoadable(
availableFieldDefinitionsScopedState({ scopeId: viewScopeId }),
)
.getValue(); .getValue();
const onViewFieldsChange = snapshot const onViewFieldsChange = snapshot
@ -74,133 +113,122 @@ export const ViewBarEffect = () => {
}); });
useFindManyObjects({ useFindManyObjects({
objectNamePlural: 'viewsV2', objectNamePlural: 'viewFiltersV2',
filter: { type: { eq: viewType }, objectId: { eq: viewObjectId } }, filter: { viewId: { eq: currentViewId } },
onCompleted: useRecoilCallback( onCompleted: useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>
async (data: PaginatedObjectTypeResults<View>) => { async (data: PaginatedObjectTypeResults<Required<ViewFilter>>) => {
const nextViews = data.edges.map((view) => ({ const availableFilterDefinitions = snapshot
id: view.node.id, .getLoadable(
name: view.node.name, availableFilterDefinitionsScopedState({ scopeId: viewScopeId }),
objectId: view.node.objectId, )
}));
const views = snapshot
.getLoadable(viewsScopedState({ scopeId: viewScopeId }))
.getValue(); .getValue();
if (!isDeeplyEqual(views, nextViews)) setViews(nextViews); if (!availableFilterDefinitions || !currentViewId) {
return;
}
if (!nextViews.length) return; const savedViewFilters = snapshot
.getLoadable(
savedViewFiltersScopedFamilyState({
scopeId: viewScopeId,
familyKey: currentViewId,
}),
)
.getValue();
if (!currentViewId) return changeView(nextViews[0].id); const onViewFiltersChange = snapshot
.getLoadable(
onViewFiltersChangeScopedState({ scopeId: viewScopeId }),
)
.getValue();
const queriedViewFilters = data.edges
.map(({ node }) => {
const availableFilterDefinition = availableFilterDefinitions.find(
(filterDefinition) => filterDefinition.fieldId === node.fieldId,
);
if (!availableFilterDefinition) return null;
return {
...node,
displayValue: node.displayValue ?? node.value,
definition: availableFilterDefinition,
};
})
.filter(assertNotNull);
if (!isDeeplyEqual(savedViewFilters, queriedViewFilters)) {
setSavedViewFilters?.(queriedViewFilters);
setCurrentViewFilters?.(queriedViewFilters);
onViewFiltersChange?.(queriedViewFilters);
}
}, },
), ),
}); });
// useGetViewSortsQuery({ useFindManyObjects({
// skip: !currentViewId, objectNamePlural: 'viewSortsV2',
// variables: { filter: { viewId: { eq: currentViewId } },
// where: { onCompleted: useRecoilCallback(
// viewId: { equals: currentViewId }, ({ snapshot }) =>
// }, async (data: PaginatedObjectTypeResults<Required<ViewSort>>) => {
// }, const availableSortDefinitions = snapshot
// onCompleted: useRecoilCallback(({ snapshot }) => async (data) => { .getLoadable(
// const availableSorts = snapshot availableSortDefinitionsScopedState({ scopeId: viewScopeId }),
// .getLoadable(availableSortsScopedState({ scopeId: viewScopeId })) )
// .getValue(); .getValue();
// if (!availableSorts || !currentViewId) { if (!availableSortDefinitions || !currentViewId) {
// return; return;
// } }
// const savedViewSorts = snapshot const savedViewSorts = snapshot
// .getLoadable( .getLoadable(
// savedViewSortsScopedFamilyState({ savedViewSortsScopedFamilyState({
// scopeId: viewScopeId, scopeId: viewScopeId,
// familyKey: currentViewId, familyKey: currentViewId,
// }), }),
// ) )
// .getValue(); .getValue();
// const queriedViewSorts = data.viewSorts const onViewSortsChange = snapshot
// .map((viewSort) => { .getLoadable(onViewSortsChangeScopedState({ scopeId: viewScopeId }))
// const foundCorrespondingSortDefinition = availableSorts.find( .getValue();
// (sort) => sort.key === viewSort.key,
// );
// if (foundCorrespondingSortDefinition) { const queriedViewSorts = data.edges
// return { .map(({ node }) => {
// key: viewSort.key, const availableSortDefinition = availableSortDefinitions.find(
// definition: foundCorrespondingSortDefinition, (sort) => sort.fieldId === node.fieldId,
// direction: viewSort.direction.toLowerCase(), );
// } as Sort;
// } else {
// return undefined;
// }
// })
// .filter((sort): sort is Sort => !!sort);
// if (!isDeeplyEqual(savedViewSorts, queriedViewSorts)) { if (!availableSortDefinition) return null;
// setSavedViewSorts?.(queriedViewSorts);
// setCurrentViewSorts?.(queriedViewSorts);
// }
// }),
// });
// useGetViewFiltersQuery({ return {
// skip: !currentViewId, id: node.id,
// variables: { fieldId: node.fieldId,
// where: { direction: node.direction,
// viewId: { equals: currentViewId }, definition: availableSortDefinition,
// }, };
// }, })
// onCompleted: useRecoilCallback(({ snapshot }) => (data) => { .filter(assertNotNull);
// const availableFilters = snapshot
// .getLoadable(availableFiltersScopedState({ scopeId: viewScopeId }))
// .getValue();
// if (!availableFilters || !currentViewId) { if (!isDeeplyEqual(savedViewSorts, queriedViewSorts)) {
// return; setSavedViewSorts?.(queriedViewSorts);
// } setCurrentViewSorts?.(queriedViewSorts);
onViewSortsChange?.(queriedViewSorts);
// const savedViewFilters = snapshot }
// .getLoadable( },
// savedViewFiltersScopedFamilyState({ ),
// scopeId: viewScopeId, });
// familyKey: currentViewId,
// }),
// )
// .getValue();
// const queriedViewFilters = data.viewFilters
// .map(({ __typename, name: _name, ...viewFilter }) => {
// const availableFilter = availableFilters.find(
// (filter) => filter.key === viewFilter.key,
// );
// return availableFilter
// ? {
// ...viewFilter,
// displayValue: viewFilter.displayValue ?? viewFilter.value,
// type: availableFilter.type,
// }
// : undefined;
// })
// .filter((filter): filter is Filter => !!filter);
// if (!isDeeplyEqual(savedViewFilters, queriedViewFilters)) {
// setSavedViewFilters?.(queriedViewFilters);
// setCurrentViewFilters?.(queriedViewFilters);
// }
// }),
// });
const currentViewIdFromUrl = searchParams.get('view'); const currentViewIdFromUrl = searchParams.get('view');
useEffect(() => { useEffect(() => {
if (!currentViewIdFromUrl) return; if (!currentViewIdFromUrl) return;
setCurrentViewId(currentViewIdFromUrl); loadView(currentViewIdFromUrl);
}, [currentViewIdFromUrl, setCurrentViewId]); }, [currentViewIdFromUrl, loadView, setCurrentViewId]);
return <></>; return <></>;
}; };

View File

@ -3,7 +3,6 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { TableOptionsDropdownId } from '@/ui/data/data-table/constants/TableOptionsDropdownId';
import { import {
IconChevronDown, IconChevronDown,
IconList, IconList,
@ -24,7 +23,7 @@ import { assertNotNull } from '~/utils/assert';
import { ViewsDropdownId } from '../constants/ViewsDropdownId'; import { ViewsDropdownId } from '../constants/ViewsDropdownId';
import { useView } from '../hooks/useView'; import { useView } from '../hooks/useView';
import { useViewInternalStates } from '../hooks/useViewInternalStates'; import { useViewGetStates } from '../hooks/useViewGetStates';
const StyledBoldDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)` const StyledBoldDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
font-weight: ${({ theme }) => theme.font.weight.regular}; font-weight: ${({ theme }) => theme.font.weight.regular};
@ -60,17 +59,22 @@ const StyledViewName = styled.span`
export type ViewsDropdownButtonProps = { export type ViewsDropdownButtonProps = {
hotkeyScope: HotkeyScope; hotkeyScope: HotkeyScope;
onViewEditModeChange?: () => void; onViewEditModeChange?: () => void;
optionsDropdownScopeId: string;
}; };
export const ViewsDropdownButton = ({ export const ViewsDropdownButton = ({
hotkeyScope, hotkeyScope,
onViewEditModeChange, onViewEditModeChange,
optionsDropdownScopeId,
}: ViewsDropdownButtonProps) => { }: ViewsDropdownButtonProps) => {
const theme = useTheme(); const theme = useTheme();
const { scopeId, removeView, currentViewId, changeView } = useView(); const { scopeId, removeView, currentViewId, changeViewInUrl } = useView();
const { views, currentView, setViewEditMode, entityCountInCurrentView } = const { views, currentView, entityCountInCurrentView } = useViewGetStates(
useViewInternalStates(scopeId, currentViewId); scopeId,
currentViewId,
);
const { setViewEditMode } = useView();
const { const {
isDropdownOpen: isViewsDropdownOpen, isDropdownOpen: isViewsDropdownOpen,
@ -80,16 +84,16 @@ export const ViewsDropdownButton = ({
}); });
const { openDropdown: openOptionsDropdown } = useDropdown({ const { openDropdown: openOptionsDropdown } = useDropdown({
dropdownScopeId: TableOptionsDropdownId, dropdownScopeId: optionsDropdownScopeId,
}); });
const handleViewSelect = useRecoilCallback( const handleViewSelect = useRecoilCallback(
() => async (viewId: string) => { () => async (viewId: string) => {
changeView(viewId); changeViewInUrl(viewId);
closeViewsDropdown(); closeViewsDropdown();
}, },
[changeView, closeViewsDropdown], [changeViewInUrl, closeViewsDropdown],
); );
const handleAddViewButtonClick = () => { const handleAddViewButtonClick = () => {
@ -104,7 +108,7 @@ export const ViewsDropdownButton = ({
viewId: string, viewId: string,
) => { ) => {
event.stopPropagation(); event.stopPropagation();
changeView(viewId); changeViewInUrl(viewId);
setViewEditMode('edit'); setViewEditMode('edit');
onViewEditModeChange?.(); onViewEditModeChange?.();
closeViewsDropdown(); closeViewsDropdown();

View File

@ -8,9 +8,10 @@ import { viewObjectIdScopeState } from '@/views/states/viewObjectIdScopeState';
import { ViewField } from '@/views/types/ViewField'; import { ViewField } from '@/views/types/ViewField';
export const useViewFields = (viewScopeId: string) => { export const useViewFields = (viewScopeId: string) => {
const { updateOneMutation, createOneMutation } = useFindOneMetadataObject({ const { updateOneMutation, createOneMutation, findManyQuery } =
objectNameSingular: 'viewFieldV2', useFindOneMetadataObject({
}); objectNameSingular: 'viewFieldV2',
});
const apolloClient = useApolloClient(); const apolloClient = useApolloClient();
const persistViewFields = useRecoilCallback( const persistViewFields = useRecoilCallback(
@ -49,12 +50,13 @@ export const useViewFields = (viewScopeId: string) => {
variables: { variables: {
input: { input: {
fieldId: viewField.fieldId, fieldId: viewField.fieldId,
viewId: currentViewId, viewId: viewId,
isVisible: viewField.isVisible, isVisible: viewField.isVisible,
size: viewField.size, size: viewField.size,
position: viewField.position, position: viewField.position,
}, },
}, },
refetchQueries: [findManyQuery],
}), }),
), ),
); );

View File

@ -1,14 +1,26 @@
import { useApolloClient } from '@apollo/client';
import { produce } from 'immer';
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { useFindOneMetadataObject } from '@/metadata/hooks/useFindOneMetadataObject';
import { Filter } from '@/ui/data/filter/types/Filter'; import { Filter } from '@/ui/data/filter/types/Filter';
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
import { availableFiltersScopedState } from '@/views/states/availableFiltersScopedState';
import { currentViewFiltersScopedFamilyState } from '@/views/states/currentViewFiltersScopedFamilyState'; import { currentViewFiltersScopedFamilyState } from '@/views/states/currentViewFiltersScopedFamilyState';
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState'; import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
import { onViewFiltersChangeScopedState } from '@/views/states/onViewFiltersChangeScopedState';
import { savedViewFiltersScopedFamilyState } from '@/views/states/savedViewFiltersScopedFamilyState'; import { savedViewFiltersScopedFamilyState } from '@/views/states/savedViewFiltersScopedFamilyState';
import { savedViewFiltersByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewFiltersByKeyScopedFamilySelector'; import { savedViewFiltersByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewFiltersByKeyScopedFamilySelector';
import { ViewFilter } from '@/views/types/ViewFilter';
import { useViewSetStates } from '../useViewSetStates';
export const useViewFilters = (viewScopeId: string) => { export const useViewFilters = (viewScopeId: string) => {
const { updateOneMutation, createOneMutation, findManyQuery } =
useFindOneMetadataObject({
objectNameSingular: 'viewFilterV2',
});
const apolloClient = useApolloClient();
const { setCurrentViewFilters } = useViewSetStates(viewScopeId);
const persistViewFilters = useRecoilCallback( const persistViewFilters = useRecoilCallback(
({ snapshot, set }) => ({ snapshot, set }) =>
async (viewId?: string) => { async (viewId?: string) => {
@ -19,69 +31,52 @@ export const useViewFilters = (viewScopeId: string) => {
return; return;
} }
const _createViewFilters = ( const createViewFilters = (viewFiltersToCreate: ViewFilter[]) => {
filters: Filter[], if (!viewFiltersToCreate.length) return;
availableFilters: FilterDefinition[] = [],
) => {
if (!currentViewId || !filters.length) {
return;
}
if (!availableFilters) { return Promise.all(
return; viewFiltersToCreate.map((viewFilter) =>
} apolloClient.mutate({
mutation: createOneMutation,
// return createViewFiltersMutation({ variables: {
// variables: { input: {
// data: filters.map((filter) => ({ fieldId: viewFilter.fieldId,
// displayValue: filter.displayValue ?? filter.value, viewId: viewId ?? currentViewId,
// key: filter.key, value: viewFilter.value,
// name: displayValue: viewFilter.displayValue,
// availableFilters.find(({ key }) => key === filter.key) operand: viewFilter.operand,
// ?.label ?? '', },
// operand: filter.operand, },
// value: filter.value, refetchQueries: [findManyQuery],
// viewId: viewId ?? currentViewId, }),
// })), ),
// }, );
// });
}; };
const _updateViewFilters = (filters: Filter[]) => { const updateViewFilters = (viewFiltersToUpdate: ViewFilter[]) => {
if (!currentViewId || !filters.length) return; if (!viewFiltersToUpdate.length) return;
// return Promise.all( return Promise.all(
// filters.map((filter) => viewFiltersToUpdate.map((viewFilter) =>
// updateViewFilterMutation({ apolloClient.mutate({
// variables: { mutation: updateOneMutation,
// data: { variables: {
// displayValue: filter.displayValue ?? filter.value, idToUpdate: viewFilter.id,
// operand: filter.operand, input: {
// value: filter.value, value: viewFilter.value,
// }, displayValue: viewFilter.displayValue,
// where: { operand: viewFilter.operand,
// viewId_key: { },
// key: filter.key, },
// viewId: viewId ?? currentViewId, }),
// }, ),
// }, );
// },
// }),
// ),
// );
}; };
const _deleteViewFilters = (filterKeys: string[]) => { const deleteViewFilters = (viewFilterIdsToDelete: string[]) => {
if (!currentViewId || !filterKeys.length) return; if (!viewFilterIdsToDelete.length) return;
// return deleteViewFiltersMutation({ // Todo
// variables: {
// where: {
// key: { in: filterKeys },
// viewId: { equals: viewId ?? currentViewId },
// },
// },
// });
}; };
const currentViewFilters = snapshot const currentViewFilters = snapshot
@ -97,7 +92,7 @@ export const useViewFilters = (viewScopeId: string) => {
.getLoadable( .getLoadable(
savedViewFiltersByKeyScopedFamilySelector({ savedViewFiltersByKeyScopedFamilySelector({
scopeId: viewScopeId, scopeId: viewScopeId,
viewId: currentViewId, viewId: viewId ?? currentViewId,
}), }),
) )
.getValue(); .getValue();
@ -109,32 +104,24 @@ export const useViewFilters = (viewScopeId: string) => {
return; return;
} }
const availableFilters = snapshot
.getLoadable(
availableFiltersScopedState({
scopeId: viewScopeId,
}),
)
.getValue();
const filtersToCreate = currentViewFilters.filter( const filtersToCreate = currentViewFilters.filter(
(filter) => !savedViewFiltersByKey[filter.key], (filter) => !savedViewFiltersByKey[filter.fieldId],
); );
await _createViewFilters(filtersToCreate, availableFilters); await createViewFilters(filtersToCreate);
const filtersToUpdate = currentViewFilters.filter( const filtersToUpdate = currentViewFilters.filter(
(filter) => (filter) =>
savedViewFiltersByKey[filter.key] && savedViewFiltersByKey[filter.fieldId] &&
(savedViewFiltersByKey[filter.key].operand !== filter.operand || (savedViewFiltersByKey[filter.fieldId].operand !== filter.operand ||
savedViewFiltersByKey[filter.key].value !== filter.value), savedViewFiltersByKey[filter.fieldId].value !== filter.value),
); );
await _updateViewFilters(filtersToUpdate); await updateViewFilters(filtersToUpdate);
const filterKeys = currentViewFilters.map((filter) => filter.key); const filterKeys = currentViewFilters.map((filter) => filter.fieldId);
const filterKeysToDelete = Object.keys(savedViewFiltersByKey).filter( const filterKeysToDelete = Object.keys(savedViewFiltersByKey).filter(
(previousFilterKey) => !filterKeys.includes(previousFilterKey), (previousFilterKey) => !filterKeys.includes(previousFilterKey),
); );
await _deleteViewFilters(filterKeysToDelete); await deleteViewFilters(filterKeysToDelete);
set( set(
savedViewFiltersScopedFamilyState({ savedViewFiltersScopedFamilyState({
scopeId: viewScopeId, scopeId: viewScopeId,
@ -143,8 +130,102 @@ export const useViewFilters = (viewScopeId: string) => {
currentViewFilters, currentViewFilters,
); );
}, },
[viewScopeId], [
apolloClient,
createOneMutation,
findManyQuery,
updateOneMutation,
viewScopeId,
],
); );
return { persistViewFilters }; const upsertViewFilter = useRecoilCallback(
({ snapshot }) =>
(filterToUpsert: Filter) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
if (!currentViewId) {
return;
}
const savedViewFiltersByKey = snapshot
.getLoadable(
savedViewFiltersByKeyScopedFamilySelector({
scopeId: viewScopeId,
viewId: currentViewId,
}),
)
.getValue();
if (!savedViewFiltersByKey) {
return;
}
const onViewFiltersChange = snapshot
.getLoadable(onViewFiltersChangeScopedState({ scopeId: viewScopeId }))
.getValue();
const existingSavedFilterId =
savedViewFiltersByKey[filterToUpsert.fieldId]?.id;
setCurrentViewFilters?.((filters) => {
const newViewFilters = produce(filters, (filtersDraft) => {
const existingFilterIndex = filtersDraft.findIndex(
(filter) => filter.fieldId === filterToUpsert.fieldId,
);
if (existingFilterIndex === -1) {
filtersDraft.push({
...filterToUpsert,
id: existingSavedFilterId,
});
return filtersDraft;
}
filtersDraft[existingFilterIndex] = {
...filterToUpsert,
id: existingSavedFilterId,
};
});
onViewFiltersChange?.(newViewFilters);
return newViewFilters;
});
},
);
const removeViewFilter = useRecoilCallback(
({ snapshot }) =>
(fieldId: string) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
if (!currentViewId) {
return;
}
const onViewFiltersChange = snapshot
.getLoadable(onViewFiltersChangeScopedState({ scopeId: viewScopeId }))
.getValue();
const currentViewFilters = snapshot
.getLoadable(
currentViewFiltersScopedFamilyState({
scopeId: viewScopeId,
familyKey: currentViewId,
}),
)
.getValue();
const newViewFilters = currentViewFilters.filter((filter) => {
return filter.fieldId !== fieldId;
});
setCurrentViewFilters?.(newViewFilters);
onViewFiltersChange?.(newViewFilters);
},
);
return { persistViewFilters, removeViewFilter, upsertViewFilter };
}; };

View File

@ -1,16 +1,25 @@
import { useApolloClient } from '@apollo/client';
import { produce } from 'immer'; import { produce } from 'immer';
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { useFindOneMetadataObject } from '@/metadata/hooks/useFindOneMetadataObject';
import { Sort } from '@/ui/data/sort/types/Sort'; import { Sort } from '@/ui/data/sort/types/Sort';
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState'; import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
import { currentViewSortsScopedFamilyState } from '@/views/states/currentViewSortsScopedFamilyState'; import { currentViewSortsScopedFamilyState } from '@/views/states/currentViewSortsScopedFamilyState';
import { onViewSortsChangeScopedState } from '@/views/states/onViewSortsChangeScopedState';
import { savedViewSortsScopedFamilyState } from '@/views/states/savedViewSortsScopedFamilyState'; import { savedViewSortsScopedFamilyState } from '@/views/states/savedViewSortsScopedFamilyState';
import { savedViewSortsByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewSortsByKeyScopedFamilySelector'; import { savedViewSortsByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewSortsByKeyScopedFamilySelector';
import { ViewSort } from '@/views/types/ViewSort';
import { useViewStates } from '../useViewStates'; import { useViewSetStates } from '../useViewSetStates';
export const useViewSorts = (viewScopeId: string) => { export const useViewSorts = (viewScopeId: string) => {
const { setCurrentViewSorts } = useViewStates(viewScopeId); const { updateOneMutation, createOneMutation, findManyQuery } =
useFindOneMetadataObject({
objectNameSingular: 'viewSortV2',
});
const apolloClient = useApolloClient();
const { setCurrentViewSorts } = useViewSetStates(viewScopeId);
const persistViewSorts = useRecoilCallback( const persistViewSorts = useRecoilCallback(
({ snapshot, set }) => ({ snapshot, set }) =>
@ -22,54 +31,48 @@ export const useViewSorts = (viewScopeId: string) => {
return; return;
} }
const _createViewSorts = (sorts: Sort[]) => { const createViewSorts = (viewSortsToCreate: ViewSort[]) => {
if (!currentViewId || !sorts.length) return; if (!viewSortsToCreate.length) return;
// return createViewSortsMutation({ return Promise.all(
// variables: { viewSortsToCreate.map((viewSort) =>
// data: sorts.map((sort) => ({ apolloClient.mutate({
// key: sort.key, mutation: createOneMutation,
// direction: sort.direction as ViewSortDirection, variables: {
// name: sort.definition.label, input: {
// viewId: viewId ?? currentViewId, fieldId: viewSort.fieldId,
// })), viewId: viewId ?? currentViewId,
// }, direction: viewSort.direction,
// }); },
},
refetchQueries: [findManyQuery],
}),
),
);
}; };
const _updateViewSorts = (sorts: Sort[]) => { const updateViewSorts = (viewSortsToUpdate: ViewSort[]) => {
if (!currentViewId || !sorts.length) return; if (!viewSortsToUpdate.length) return;
// return Promise.all( return Promise.all(
// sorts.map((sort) => viewSortsToUpdate.map((viewSort) =>
// updateViewSortMutation({ apolloClient.mutate({
// variables: { mutation: updateOneMutation,
// data: { variables: {
// direction: sort.direction as ViewSortDirection, idToUpdate: viewSort.id,
// }, input: {
// where: { direction: viewSort.direction,
// viewId_key: { },
// key: sort.key, },
// viewId: viewId ?? currentViewId, }),
// }, ),
// }, );
// },
// }),
// ),
// );
}; };
const _deleteViewSorts = (sortKeys: string[]) => { const deleteViewSorts = (viewSortIdsToDelete: string[]) => {
if (!currentViewId || !sortKeys.length) return; if (!viewSortIdsToDelete.length) return;
// return deleteViewSortsMutation({ // Todo
// variables: {
// where: {
// key: { in: sortKeys },
// viewId: { equals: viewId ?? currentViewId },
// },
// },
// });
}; };
const currentViewSorts = snapshot const currentViewSorts = snapshot
@ -85,7 +88,7 @@ export const useViewSorts = (viewScopeId: string) => {
.getLoadable( .getLoadable(
savedViewSortsByKeyScopedFamilySelector({ savedViewSortsByKeyScopedFamilySelector({
scopeId: viewScopeId, scopeId: viewScopeId,
viewId: currentViewId, viewId: viewId ?? currentViewId,
}), }),
) )
.getValue(); .getValue();
@ -98,22 +101,23 @@ export const useViewSorts = (viewScopeId: string) => {
} }
const sortsToCreate = currentViewSorts.filter( const sortsToCreate = currentViewSorts.filter(
(sort) => !savedViewSortsByKey[sort.key], (sort) => !savedViewSortsByKey[sort.fieldId],
); );
await _createViewSorts(sortsToCreate);
await createViewSorts(sortsToCreate);
const sortsToUpdate = currentViewSorts.filter( const sortsToUpdate = currentViewSorts.filter(
(sort) => (sort) =>
savedViewSortsByKey[sort.key] && savedViewSortsByKey[sort.fieldId] &&
savedViewSortsByKey[sort.key].direction !== sort.direction, savedViewSortsByKey[sort.fieldId].direction !== sort.direction,
); );
await _updateViewSorts(sortsToUpdate); await updateViewSorts(sortsToUpdate);
const sortKeys = currentViewSorts.map((sort) => sort.key); const sortKeys = currentViewSorts.map((sort) => sort.fieldId);
const sortKeysToDelete = Object.keys(savedViewSortsByKey).filter( const sortKeysToDelete = Object.keys(savedViewSortsByKey).filter(
(previousSortKey) => !sortKeys.includes(previousSortKey), (previousSortKey) => !sortKeys.includes(previousSortKey),
); );
await _deleteViewSorts(sortKeysToDelete); await deleteViewSorts(sortKeysToDelete);
set( set(
savedViewSortsScopedFamilyState({ savedViewSortsScopedFamilyState({
scopeId: viewScopeId, scopeId: viewScopeId,
@ -122,24 +126,99 @@ export const useViewSorts = (viewScopeId: string) => {
currentViewSorts, currentViewSorts,
); );
}, },
[viewScopeId], [
apolloClient,
createOneMutation,
findManyQuery,
updateOneMutation,
viewScopeId,
],
); );
const upsertViewSort = (sortToUpsert: Sort) => { const upsertViewSort = useRecoilCallback(
setCurrentViewSorts?.((sorts) => { ({ snapshot }) =>
return produce(sorts, (sortsDraft) => { (sortToUpsert: Sort) => {
const index = sortsDraft.findIndex( const currentViewId = snapshot
(sort) => sort.key === sortToUpsert.key, .getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
); .getValue();
if (index === -1) { if (!currentViewId) {
sortsDraft.push(sortToUpsert); return;
} else {
sortsDraft[index] = sortToUpsert;
} }
});
});
};
return { persistViewSorts, upsertViewSort }; const savedViewSortsByKey = snapshot
.getLoadable(
savedViewSortsByKeyScopedFamilySelector({
scopeId: viewScopeId,
viewId: currentViewId,
}),
)
.getValue();
if (!savedViewSortsByKey) {
return;
}
const onViewSortsChange = snapshot
.getLoadable(onViewSortsChangeScopedState({ scopeId: viewScopeId }))
.getValue();
const existingSavedSortId =
savedViewSortsByKey[sortToUpsert.fieldId]?.id;
setCurrentViewSorts?.((sorts) => {
const newViewSorts = produce(sorts, (sortsDraft) => {
const existingSortIndex = sortsDraft.findIndex(
(sort) => sort.fieldId === sortToUpsert.fieldId,
);
if (existingSortIndex === -1) {
sortsDraft.push({ ...sortToUpsert, id: existingSavedSortId });
return sortsDraft;
}
sortsDraft[existingSortIndex] = {
...sortToUpsert,
id: existingSavedSortId,
};
});
onViewSortsChange?.(newViewSorts);
return newViewSorts;
});
},
);
const removeViewSort = useRecoilCallback(
({ snapshot }) =>
(fieldId: string) => {
const currentViewId = snapshot
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
.getValue();
if (!currentViewId) {
return;
}
const onViewSortsChange = snapshot
.getLoadable(onViewSortsChangeScopedState({ scopeId: viewScopeId }))
.getValue();
const currentViewSorts = snapshot
.getLoadable(
currentViewSortsScopedFamilyState({
scopeId: viewScopeId,
familyKey: currentViewId,
}),
)
.getValue();
const newViewSorts = currentViewSorts.filter((filter) => {
return filter.fieldId !== fieldId;
});
setCurrentViewSorts?.(newViewSorts);
onViewSortsChange?.(newViewSorts);
},
);
return { persistViewSorts, upsertViewSort, removeViewSort };
}; };

View File

@ -1,13 +1,21 @@
import { useApolloClient } from '@apollo/client';
import { useRecoilCallback } from 'recoil'; import { useRecoilCallback } from 'recoil';
import { useFindOneMetadataObject } from '@/metadata/hooks/useFindOneMetadataObject';
import { viewObjectIdScopeState } from '@/views/states/viewObjectIdScopeState'; import { viewObjectIdScopeState } from '@/views/states/viewObjectIdScopeState';
import { viewTypeScopedState } from '@/views/states/viewTypeScopedState'; import { viewTypeScopedState } from '@/views/states/viewTypeScopedState';
import { View } from '@/views/types/View'; import { View } from '@/views/types/View';
export const useViews = (scopeId: string) => { export const useViews = (scopeId: string) => {
const { updateOneMutation, createOneMutation, findManyQuery } =
useFindOneMetadataObject({
objectNameSingular: 'viewV2',
});
const apolloClient = useApolloClient();
const createView = useRecoilCallback( const createView = useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>
async (_view: Pick<View, 'id' | 'name'>) => { async (view: Pick<View, 'id' | 'name'>) => {
const viewObjectId = await snapshot const viewObjectId = await snapshot
.getLoadable(viewObjectIdScopeState({ scopeId })) .getLoadable(viewObjectIdScopeState({ scopeId }))
.getValue(); .getValue();
@ -19,27 +27,31 @@ export const useViews = (scopeId: string) => {
if (!viewObjectId || !viewType) { if (!viewObjectId || !viewType) {
return; return;
} }
// await createViewMutation({ await apolloClient.mutate({
// variables: { mutation: createOneMutation,
// data: { variables: {
// ...view, input: {
// objectId: viewObjectId, ...view,
// type: viewType, objectId: viewObjectId,
// }, type: viewType,
// }, },
// refetchQueries: [getOperationName(GET_VIEWS) ?? ''], },
// }); refetchQueries: [findManyQuery],
});
}, },
); );
const updateView = async (_view: View) => { const updateView = async (view: View) => {
// await updateViewMutation({ await apolloClient.mutate({
// variables: { mutation: updateOneMutation,
// data: { name: view.name }, variables: {
// where: { id: view.id }, idToUpdate: view.id,
// }, input: {
// refetchQueries: [getOperationName(GET_VIEWS) ?? ''], ...view,
// }); },
},
refetchQueries: [findManyQuery],
});
}; };
const deleteView = async (_viewId: string) => { const deleteView = async (_viewId: string) => {

View File

@ -1,15 +0,0 @@
import { useView } from '@/views/hooks/useView';
export const useRemoveFilter = () => {
const { setCurrentViewFilters } = useView();
const removeFilter = (filterKey: string) => {
setCurrentViewFilters?.((filters) => {
return filters.filter((filter) => {
return filter.key !== filterKey;
});
});
};
return removeFilter;
};

View File

@ -1,26 +0,0 @@
import { produce } from 'immer';
import { Filter } from '@/ui/data/filter/types/Filter';
import { useView } from '@/views/hooks/useView';
export const useUpsertFilter = () => {
const { setCurrentViewFilters } = useView();
const upsertFilter = (filterToUpsert: Filter) => {
setCurrentViewFilters?.((filters) => {
return produce(filters, (filtersDraft) => {
const index = filtersDraft.findIndex(
(filter) => filter.key === filterToUpsert.key,
);
if (index === -1) {
filtersDraft.push(filterToUpsert);
} else {
filtersDraft[index] = filterToUpsert;
}
});
});
};
return upsertFilter;
};

View File

@ -10,8 +10,12 @@ import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsS
import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState'; import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState';
import { currentViewIdScopedState } from '../states/currentViewIdScopedState'; import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState'; import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState';
import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState';
import { onViewFiltersChangeScopedState } from '../states/onViewFiltersChangeScopedState';
import { onViewSortsChangeScopedState } from '../states/onViewSortsChangeScopedState';
import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState'; import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState';
import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState'; import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState';
import { currentViewScopedSelector } from '../states/selectors/currentViewScopedSelector';
import { viewEditModeScopedState } from '../states/viewEditModeScopedState'; import { viewEditModeScopedState } from '../states/viewEditModeScopedState';
import { viewsScopedState } from '../states/viewsScopedState'; import { viewsScopedState } from '../states/viewsScopedState';
@ -19,7 +23,7 @@ import { useViewFields } from './internal/useViewFields';
import { useViewFilters } from './internal/useViewFilters'; import { useViewFilters } from './internal/useViewFilters';
import { useViews } from './internal/useViews'; import { useViews } from './internal/useViews';
import { useViewSorts } from './internal/useViewSorts'; import { useViewSorts } from './internal/useViewSorts';
import { useViewStates } from './useViewStates'; import { useViewSetStates } from './useViewSetStates';
type UseViewProps = { type UseViewProps = {
viewScopeId?: string; viewScopeId?: string;
@ -42,33 +46,81 @@ export const useView = (props?: UseViewProps) => {
setEntityCountInCurrentView, setEntityCountInCurrentView,
setIsViewBarExpanded, setIsViewBarExpanded,
setAvailableSorts, setAvailableSortDefinitions,
setCurrentViewSorts, setCurrentViewSorts,
setSavedViewSorts, setSavedViewSorts,
setAvailableFilters, setAvailableFilterDefinitions,
setCurrentViewFilters, setCurrentViewFilters,
setSavedViewFilters, setSavedViewFilters,
setAvailableFields, setAvailableFieldDefinitions,
setCurrentViewFields, setCurrentViewFields,
setSavedViewFields, setSavedViewFields,
} = useViewStates(scopeId);
const { persistViewSorts, upsertViewSort } = useViewSorts(scopeId); setOnViewFieldsChange,
const { persistViewFilters } = useViewFilters(scopeId); setOnViewFiltersChange,
setOnViewSortsChange,
} = useViewSetStates(scopeId);
const { persistViewSorts, upsertViewSort, removeViewSort } =
useViewSorts(scopeId);
const { persistViewFilters, upsertViewFilter, removeViewFilter } =
useViewFilters(scopeId);
const { persistViewFields } = useViewFields(scopeId); const { persistViewFields } = useViewFields(scopeId);
const { createView: internalCreateView, deleteView: internalDeleteView } = const {
useViews(scopeId); createView: internalCreateView,
updateView: internalUpdateView,
deleteView: internalDeleteView,
} = useViews(scopeId);
const [_, setSearchParams] = useSearchParams(); const [_, setSearchParams] = useSearchParams();
const changeView = useCallback( const changeViewInUrl = useCallback(
(viewId: string) => { (viewId: string) => {
setSearchParams({ view: viewId }); setSearchParams({ view: viewId });
}, },
[setSearchParams], [setSearchParams],
); );
const loadView = useRecoilCallback(({ snapshot }) => (viewId: string) => {
setCurrentViewId?.(viewId);
const currentViewFields = snapshot
.getLoadable(
currentViewFieldsScopedFamilyState({ scopeId, familyKey: viewId }),
)
.getValue();
const onViewFieldsChange = snapshot
.getLoadable(onViewFieldsChangeScopedState({ scopeId }))
.getValue();
onViewFieldsChange?.(currentViewFields);
const currentViewFilters = snapshot
.getLoadable(
currentViewFiltersScopedFamilyState({ scopeId, familyKey: viewId }),
)
.getValue();
const onViewFiltersChange = snapshot
.getLoadable(onViewFiltersChangeScopedState({ scopeId }))
.getValue();
onViewFiltersChange?.(currentViewFilters);
const currentViewSorts = snapshot
.getLoadable(
currentViewSortsScopedFamilyState({ scopeId, familyKey: viewId }),
)
.getValue();
const onViewSortsChange = snapshot
.getLoadable(onViewSortsChangeScopedState({ scopeId }))
.getValue();
onViewSortsChange?.(currentViewSorts);
});
const resetViewBar = useRecoilCallback(({ snapshot }) => () => { const resetViewBar = useRecoilCallback(({ snapshot }) => () => {
const savedViewFilters = snapshot const savedViewFilters = snapshot
.getLoadable( .getLoadable(
@ -95,7 +147,6 @@ export const useView = (props?: UseViewProps) => {
setCurrentViewSorts?.(savedViewSorts); setCurrentViewSorts?.(savedViewSorts);
} }
setViewEditMode?.('none'); setViewEditMode?.('none');
setIsViewBarExpanded?.(false);
}); });
const createView = useRecoilCallback( const createView = useRecoilCallback(
@ -156,10 +207,10 @@ export const useView = (props?: UseViewProps) => {
await persistViewFilters(newViewId); await persistViewFilters(newViewId);
await persistViewSorts(newViewId); await persistViewSorts(newViewId);
changeView(newViewId); changeViewInUrl(newViewId);
}, },
[ [
changeView, changeViewInUrl,
currentViewId, currentViewId,
internalCreateView, internalCreateView,
persistViewFields, persistViewFields,
@ -195,15 +246,32 @@ export const useView = (props?: UseViewProps) => {
const handleViewNameSubmit = useRecoilCallback( const handleViewNameSubmit = useRecoilCallback(
({ snapshot }) => ({ snapshot }) =>
async (name?: string) => { async (name?: string) => {
if (!name) {
return;
}
const viewEditMode = snapshot const viewEditMode = snapshot
.getLoadable(viewEditModeScopedState({ scopeId })) .getLoadable(viewEditModeScopedState({ scopeId }))
.getValue(); .getValue();
const currentView = snapshot
.getLoadable(currentViewScopedSelector(scopeId))
.getValue();
if (!currentView) {
return;
}
if (viewEditMode === 'create' && name) { if (viewEditMode === 'create' && name) {
await createView(name); await createView(name);
} else {
await internalUpdateView({
...currentView,
name,
});
} }
}, },
[createView, scopeId], [createView, internalUpdateView, scopeId],
); );
return { return {
@ -224,20 +292,28 @@ export const useView = (props?: UseViewProps) => {
setViewType, setViewType,
setEntityCountInCurrentView, setEntityCountInCurrentView,
setAvailableSorts, setAvailableSortDefinitions,
setCurrentViewSorts, setCurrentViewSorts,
setSavedViewSorts, setSavedViewSorts,
upsertViewSort, upsertViewSort,
removeViewSort,
setAvailableFilters, setAvailableFilterDefinitions,
setCurrentViewFilters, setCurrentViewFilters,
setSavedViewFilters, setSavedViewFilters,
upsertViewFilter,
removeViewFilter,
setAvailableFields, setAvailableFieldDefinitions,
setCurrentViewFields, setCurrentViewFields,
setSavedViewFields, setSavedViewFields,
persistViewFields, persistViewFields,
changeView, changeViewInUrl,
loadView,
setOnViewFieldsChange,
setOnViewFiltersChange,
setOnViewSortsChange,
}; };
}; };

View File

@ -5,9 +5,9 @@ import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRec
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext'; import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext';
import { availableFieldsScopedState } from '../states/availableFieldsScopedState'; import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState';
import { availableFiltersScopedState } from '../states/availableFiltersScopedState'; import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState';
import { availableSortsScopedState } from '../states/availableSortsScopedState'; import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState';
import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState'; import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState';
import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState'; import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState';
import { currentViewIdScopedState } from '../states/currentViewIdScopedState'; import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
@ -31,17 +31,14 @@ import { viewObjectIdScopeState } from '../states/viewObjectIdScopeState';
import { viewsScopedState } from '../states/viewsScopedState'; import { viewsScopedState } from '../states/viewsScopedState';
import { viewTypeScopedState } from '../states/viewTypeScopedState'; import { viewTypeScopedState } from '../states/viewTypeScopedState';
export const useViewInternalStates = ( export const useViewGetStates = (viewScopeId?: string, viewId?: string) => {
viewScopeId?: string,
viewId?: string,
) => {
const scopeId = useAvailableScopeIdOrThrow( const scopeId = useAvailableScopeIdOrThrow(
ViewScopeInternalContext, ViewScopeInternalContext,
viewScopeId, viewScopeId,
); );
// View // View
const [currentViewId, setCurrentViewId] = useRecoilScopedStateV2( const [currentViewId] = useRecoilScopedStateV2(
currentViewIdScopedState, currentViewIdScopedState,
scopeId, scopeId,
); );
@ -50,39 +47,38 @@ export const useViewInternalStates = (
const currentView = useRecoilValue(currentViewScopedSelector(scopeId)); const currentView = useRecoilValue(currentViewScopedSelector(scopeId));
const [viewEditMode, setViewEditMode] = useRecoilScopedStateV2( const [viewEditMode] = useRecoilScopedStateV2(
viewEditModeScopedState, viewEditModeScopedState,
scopeId, scopeId,
); );
const [views, setViews] = useRecoilScopedStateV2(viewsScopedState, scopeId); const [views] = useRecoilScopedStateV2(viewsScopedState, scopeId);
const [viewObjectId, setViewObjectId] = useRecoilScopedStateV2( const [viewObjectId] = useRecoilScopedStateV2(
viewObjectIdScopeState, viewObjectIdScopeState,
scopeId, scopeId,
); );
const [viewType, setViewType] = useRecoilScopedStateV2( const [viewType] = useRecoilScopedStateV2(viewTypeScopedState, scopeId);
viewTypeScopedState,
const [entityCountInCurrentView] = useRecoilScopedStateV2(
entityCountInCurrentViewScopedState,
scopeId, scopeId,
); );
const [entityCountInCurrentView, setEntityCountInCurrentView] = const [isViewBarExpanded] = useRecoilScopedStateV2(
useRecoilScopedStateV2(entityCountInCurrentViewScopedState, scopeId);
const [isViewBarExpanded, setIsViewBarExpanded] = useRecoilScopedStateV2(
isViewBarExpandedScopedState, isViewBarExpandedScopedState,
scopeId, scopeId,
); );
// ViewSorts // ViewSorts
const [currentViewSorts, setCurrentViewSorts] = useRecoilScopedFamilyState( const [currentViewSorts] = useRecoilScopedFamilyState(
currentViewSortsScopedFamilyState, currentViewSortsScopedFamilyState,
scopeId, scopeId,
familyItemId, familyItemId,
); );
const [savedViewSorts, setSavedViewSorts] = useRecoilScopedFamilyState( const [savedViewSorts] = useRecoilScopedFamilyState(
savedViewSortsScopedFamilyState, savedViewSortsScopedFamilyState,
scopeId, scopeId,
familyItemId, familyItemId,
@ -95,8 +91,8 @@ export const useViewInternalStates = (
}), }),
); );
const [availableSorts, setAvailableSorts] = useRecoilScopedStateV2( const [availableSortDefinitions] = useRecoilScopedStateV2(
availableSortsScopedState, availableSortDefinitionsScopedState,
scopeId, scopeId,
); );
@ -108,14 +104,13 @@ export const useViewInternalStates = (
); );
// ViewFilters // ViewFilters
const [currentViewFilters, setCurrentViewFilters] = const [currentViewFilters] = useRecoilScopedFamilyState(
useRecoilScopedFamilyState( currentViewFiltersScopedFamilyState,
currentViewFiltersScopedFamilyState, scopeId,
scopeId, familyItemId,
familyItemId, );
);
const [savedViewFilters, setSavedViewFilters] = useRecoilScopedFamilyState( const [savedViewFilters] = useRecoilScopedFamilyState(
savedViewFiltersScopedFamilyState, savedViewFiltersScopedFamilyState,
scopeId, scopeId,
familyItemId, familyItemId,
@ -128,8 +123,8 @@ export const useViewInternalStates = (
}), }),
); );
const [availableFilters, setAvailableFilters] = useRecoilScopedStateV2( const [availableFilterDefinitions] = useRecoilScopedStateV2(
availableFiltersScopedState, availableFilterDefinitionsScopedState,
scopeId, scopeId,
); );
@ -141,18 +136,18 @@ export const useViewInternalStates = (
); );
// ViewFields // ViewFields
const [availableFields, setAvailableFields] = useRecoilScopedStateV2( const [availableFieldDefinitions] = useRecoilScopedStateV2(
availableFieldsScopedState, availableFieldDefinitionsScopedState,
scopeId, scopeId,
); );
const [currentViewFields, setCurrentViewFields] = useRecoilScopedFamilyState( const [currentViewFields] = useRecoilScopedFamilyState(
currentViewFieldsScopedFamilyState, currentViewFieldsScopedFamilyState,
scopeId, scopeId,
familyItemId, familyItemId,
); );
const [savedViewFields, setSavedViewFields] = useRecoilScopedFamilyState( const [savedViewFields] = useRecoilScopedFamilyState(
savedViewFieldsScopedFamilyState, savedViewFieldsScopedFamilyState,
scopeId, scopeId,
familyItemId, familyItemId,
@ -166,17 +161,17 @@ export const useViewInternalStates = (
); );
// ViewChangeHandlers // ViewChangeHandlers
const [onViewSortsChange, setOnViewSortsChange] = useRecoilScopedStateV2( const [onViewSortsChange] = useRecoilScopedStateV2(
onViewSortsChangeScopedState, onViewSortsChangeScopedState,
scopeId, scopeId,
); );
const [onViewFiltersChange, setOnViewFiltersChange] = useRecoilScopedStateV2( const [onViewFiltersChange] = useRecoilScopedStateV2(
onViewFiltersChangeScopedState, onViewFiltersChangeScopedState,
scopeId, scopeId,
); );
const [onViewFieldsChange, setOnViewFieldsChange] = useRecoilScopedStateV2( const [onViewFieldsChange] = useRecoilScopedStateV2(
onViewFieldsChangeScopedState, onViewFieldsChangeScopedState,
scopeId, scopeId,
); );
@ -184,52 +179,33 @@ export const useViewInternalStates = (
return { return {
currentViewId, currentViewId,
currentView, currentView,
setCurrentViewId,
isViewBarExpanded, isViewBarExpanded,
setIsViewBarExpanded,
views, views,
setViews,
viewEditMode, viewEditMode,
setViewEditMode,
viewObjectId, viewObjectId,
setViewObjectId,
viewType, viewType,
setViewType,
entityCountInCurrentView, entityCountInCurrentView,
setEntityCountInCurrentView,
availableSorts, availableSortDefinitions,
setAvailableSorts,
currentViewSorts, currentViewSorts,
setCurrentViewSorts,
savedViewSorts, savedViewSorts,
savedViewSortsByKey, savedViewSortsByKey,
setSavedViewSorts,
canPersistSorts, canPersistSorts,
availableFilters, availableFilterDefinitions,
setAvailableFilters,
currentViewFilters, currentViewFilters,
setCurrentViewFilters,
savedViewFilters, savedViewFilters,
savedViewFiltersByKey, savedViewFiltersByKey,
setSavedViewFilters,
canPersistFilters, canPersistFilters,
availableFields, availableFieldDefinitions,
setAvailableFields,
currentViewFields, currentViewFields,
savedViewFieldsByKey, savedViewFieldsByKey,
setCurrentViewFields,
savedViewFields, savedViewFields,
setSavedViewFields,
onViewSortsChange, onViewSortsChange,
setOnViewSortsChange,
onViewFiltersChange, onViewFiltersChange,
setOnViewFiltersChange,
onViewFieldsChange, onViewFieldsChange,
setOnViewFieldsChange,
}; };
}; };

View File

@ -4,15 +4,18 @@ import { useSetRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/use
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext'; import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext';
import { availableFieldsScopedState } from '../states/availableFieldsScopedState'; import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState';
import { availableFiltersScopedState } from '../states/availableFiltersScopedState'; import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState';
import { availableSortsScopedState } from '../states/availableSortsScopedState'; import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState';
import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState'; import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState';
import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState'; import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState';
import { currentViewIdScopedState } from '../states/currentViewIdScopedState'; import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState'; import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState';
import { entityCountInCurrentViewScopedState } from '../states/entityCountInCurrentViewScopedState'; import { entityCountInCurrentViewScopedState } from '../states/entityCountInCurrentViewScopedState';
import { isViewBarExpandedScopedState } from '../states/isViewBarExpandedScopedState'; 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 { savedViewFieldsScopedFamilyState } from '../states/savedViewFieldsScopedFamilyState';
import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState'; import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState';
import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState'; import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState';
@ -21,7 +24,7 @@ import { viewObjectIdScopeState } from '../states/viewObjectIdScopeState';
import { viewsScopedState } from '../states/viewsScopedState'; import { viewsScopedState } from '../states/viewsScopedState';
import { viewTypeScopedState } from '../states/viewTypeScopedState'; import { viewTypeScopedState } from '../states/viewTypeScopedState';
export const useViewStates = (viewScopeId?: string, viewId?: string) => { export const useViewSetStates = (viewScopeId?: string, viewId?: string) => {
const scopeId = useAvailableScopeIdOrThrow( const scopeId = useAvailableScopeIdOrThrow(
ViewScopeInternalContext, ViewScopeInternalContext,
viewScopeId, viewScopeId,
@ -71,8 +74,8 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => {
familyItemId, familyItemId,
); );
const setAvailableSorts = useSetRecoilScopedStateV2( const setAvailableSortDefinitions = useSetRecoilScopedStateV2(
availableSortsScopedState, availableSortDefinitionsScopedState,
scopeId, scopeId,
); );
@ -89,14 +92,14 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => {
familyItemId, familyItemId,
); );
const setAvailableFilters = useSetRecoilScopedStateV2( const setAvailableFilterDefinitions = useSetRecoilScopedStateV2(
availableFiltersScopedState, availableFilterDefinitionsScopedState,
scopeId, scopeId,
); );
// ViewFields // ViewFields
const setAvailableFields = useSetRecoilScopedStateV2( const setAvailableFieldDefinitions = useSetRecoilScopedStateV2(
availableFieldsScopedState, availableFieldDefinitionsScopedState,
scopeId, scopeId,
); );
@ -112,6 +115,21 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => {
familyItemId, familyItemId,
); );
const setOnViewFieldsChange = useSetRecoilScopedStateV2(
onViewFieldsChangeScopedState,
scopeId,
);
const setOnViewFiltersChange = useSetRecoilScopedStateV2(
onViewFiltersChangeScopedState,
scopeId,
);
const setOnViewSortsChange = useSetRecoilScopedStateV2(
onViewSortsChangeScopedState,
scopeId,
);
return { return {
currentViewId, currentViewId,
setCurrentViewId, setCurrentViewId,
@ -123,16 +141,20 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => {
setViewEditMode, setViewEditMode,
setEntityCountInCurrentView, setEntityCountInCurrentView,
setAvailableSorts, setAvailableSortDefinitions,
setCurrentViewSorts, setCurrentViewSorts,
setSavedViewSorts, setSavedViewSorts,
setAvailableFilters, setAvailableFilterDefinitions,
setCurrentViewFilters, setCurrentViewFilters,
setSavedViewFilters, setSavedViewFilters,
setAvailableFields, setAvailableFieldDefinitions,
setCurrentViewFields, setCurrentViewFields,
setSavedViewFields, setSavedViewFields,
setOnViewFieldsChange,
setOnViewFiltersChange,
setOnViewSortsChange,
}; };
}; };

View File

@ -3,7 +3,6 @@ import { useEffect } from 'react';
import { Filter } from '@/ui/data/filter/types/Filter'; import { Filter } from '@/ui/data/filter/types/Filter';
import { Sort } from '@/ui/data/sort/types/Sort'; import { Sort } from '@/ui/data/sort/types/Sort';
import { useView } from '@/views/hooks/useView'; import { useView } from '@/views/hooks/useView';
import { useViewInternalStates } from '@/views/hooks/useViewInternalStates';
import { ViewField } from '@/views/types/ViewField'; import { ViewField } from '@/views/types/ViewField';
type ViewScopeInitEffectProps = { type ViewScopeInitEffectProps = {
@ -14,17 +13,15 @@ type ViewScopeInitEffectProps = {
}; };
export const ViewScopeInitEffect = ({ export const ViewScopeInitEffect = ({
viewScopeId,
onViewSortsChange, onViewSortsChange,
onViewFiltersChange, onViewFiltersChange,
onViewFieldsChange, onViewFieldsChange,
}: ViewScopeInitEffectProps) => { }: ViewScopeInitEffectProps) => {
const { currentViewId } = useView();
const { const {
setOnViewSortsChange, setOnViewSortsChange,
setOnViewFieldsChange, setOnViewFieldsChange,
setOnViewFiltersChange, setOnViewFiltersChange,
} = useViewInternalStates(viewScopeId, currentViewId); } = useView();
useEffect(() => { useEffect(() => {
setOnViewSortsChange(() => onViewSortsChange); setOnViewSortsChange(() => onViewSortsChange);

View File

@ -2,9 +2,9 @@ import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition';
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata'; import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState'; import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
export const availableFieldsScopedState = createScopedState< export const availableFieldDefinitionsScopedState = createScopedState<
ColumnDefinition<FieldMetadata>[] ColumnDefinition<FieldMetadata>[]
>({ >({
key: 'availableFieldsScopedState', key: 'availableFieldDefinitionsScopedState',
defaultValue: [], defaultValue: [],
}); });

View File

@ -1,9 +1,9 @@
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition'; import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState'; import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
export const availableFiltersScopedState = createScopedState< export const availableFilterDefinitionsScopedState = createScopedState<
FilterDefinition[] FilterDefinition[]
>({ >({
key: 'availableFiltersScopedState', key: 'availableFilterDefinitionsScopedState',
defaultValue: [], defaultValue: [],
}); });

View File

@ -1,7 +1,9 @@
import { SortDefinition } from '@/ui/data/sort/types/SortDefinition'; import { SortDefinition } from '@/ui/data/sort/types/SortDefinition';
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState'; import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
export const availableSortsScopedState = createScopedState<SortDefinition[]>({ export const availableSortDefinitionsScopedState = createScopedState<
key: 'availableSortsScopedState', SortDefinition[]
>({
key: 'availableSortDefinitionsScopedState',
defaultValue: [], defaultValue: [],
}); });

View File

@ -1,8 +1,9 @@
import { Filter } from '@/ui/data/filter/types/Filter';
import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState'; import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState';
import { ViewFilter } from '../types/ViewFilter';
export const currentViewFiltersScopedFamilyState = createScopedFamilyState< export const currentViewFiltersScopedFamilyState = createScopedFamilyState<
Filter[], ViewFilter[],
string string
>({ >({
key: 'currentViewFiltersScopedFamilyState', key: 'currentViewFiltersScopedFamilyState',

View File

@ -1,8 +1,9 @@
import { Sort } from '@/ui/data/sort/types/Sort';
import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState'; import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState';
import { ViewSort } from '../types/ViewSort';
export const currentViewSortsScopedFamilyState = createScopedFamilyState< export const currentViewSortsScopedFamilyState = createScopedFamilyState<
Sort[], ViewSort[],
string string
>({ >({
key: 'currentViewSortsScopedFamilyState', key: 'currentViewSortsScopedFamilyState',

View File

@ -1,8 +1,9 @@
import { Filter } from '@/ui/data/filter/types/Filter';
import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState'; import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState';
import { ViewFilter } from '../types/ViewFilter';
export const savedViewFiltersScopedFamilyState = createScopedFamilyState< export const savedViewFiltersScopedFamilyState = createScopedFamilyState<
Filter[], ViewFilter[],
string string
>({ >({
key: 'savedViewFiltersScopedFamilyState', key: 'savedViewFiltersScopedFamilyState',

View File

@ -1,8 +1,9 @@
import { Sort } from '@/ui/data/sort/types/Sort';
import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState'; import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState';
import { ViewSort } from '../types/ViewSort';
export const savedViewSortsScopedFamilyState = createScopedFamilyState< export const savedViewSortsScopedFamilyState = createScopedFamilyState<
Sort[], ViewSort[],
string string
>({ >({
key: 'savedViewSortsScopedFamilyState', key: 'savedViewSortsScopedFamilyState',

View File

@ -1,6 +1,6 @@
import { selectorFamily } from 'recoil'; import { selectorFamily } from 'recoil';
import { Filter } from '@/ui/data/filter/types/Filter'; import { ViewFilter } from '@/views/types/ViewFilter';
import { savedViewFiltersScopedFamilyState } from '../savedViewFiltersScopedFamilyState'; import { savedViewFiltersScopedFamilyState } from '../savedViewFiltersScopedFamilyState';
@ -17,8 +17,8 @@ export const savedViewFiltersByKeyScopedFamilySelector = selectorFamily({
scopeId: scopeId, scopeId: scopeId,
familyKey: viewId, familyKey: viewId,
}), }),
).reduce<Record<string, Filter>>( ).reduce<Record<string, ViewFilter>>(
(result, filter) => ({ ...result, [filter.key]: filter }), (result, filter) => ({ ...result, [filter.fieldId]: filter }),
{}, {},
); );
}, },

View File

@ -1,6 +1,6 @@
import { selectorFamily } from 'recoil'; import { selectorFamily } from 'recoil';
import { Sort } from '@/ui/data/sort/types/Sort'; import { ViewSort } from '@/views/types/ViewSort';
import { savedViewSortsScopedFamilyState } from '../savedViewSortsScopedFamilyState'; import { savedViewSortsScopedFamilyState } from '../savedViewSortsScopedFamilyState';
@ -17,8 +17,8 @@ export const savedViewSortsByKeyScopedFamilySelector = selectorFamily({
scopeId: scopeId, scopeId: scopeId,
familyKey: viewId, familyKey: viewId,
}), }),
).reduce<Record<string, Sort>>( ).reduce<Record<string, ViewSort>>(
(result, sort) => ({ ...result, [sort.key]: sort }), (result, sort) => ({ ...result, [sort.fieldId]: sort }),
{}, {},
); );
}, },

View File

@ -1,7 +1,11 @@
import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition';
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
export type ViewField = { export type ViewField = {
id: string; id: string;
fieldId: string; fieldId: string;
position: number; position: number;
isVisible: boolean; isVisible: boolean;
size: number; size: number;
definition: ColumnDefinition<FieldMetadata>;
}; };

View File

@ -0,0 +1,12 @@
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
import { ViewFilterOperand } from './ViewFilterOperand';
export type ViewFilter = {
id?: string;
fieldId: string;
operand: ViewFilterOperand;
value: string;
displayValue: string;
definition: FilterDefinition;
};

View File

@ -0,0 +1,9 @@
import { SortDefinition } from '@/ui/data/sort/types/SortDefinition';
import { SortDirection } from '@/ui/data/sort/types/SortDirection';
export type ViewSort = {
id?: string;
fieldId: string;
direction: SortDirection;
definition: SortDefinition;
};

View File

@ -12,5 +12,6 @@ export const columnDefinitionsToViewFields = (
position: columnDefinition.position, position: columnDefinition.position,
size: columnDefinition.size, size: columnDefinition.size,
isVisible: columnDefinition.isVisible ?? true, isVisible: columnDefinition.isVisible ?? true,
definition: columnDefinition,
})); }));
}; };

View File

@ -0,0 +1,35 @@
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
import { BoardFieldDefinition } from '@/ui/layout/board/types/BoardFieldDefinition';
import { assertNotNull } from '~/utils/assert';
import { ViewField } from '../types/ViewField';
export const viewFieldsToBoardFieldDefinitions = (
viewFields: ViewField[],
fieldsMetadata: BoardFieldDefinition<FieldMetadata>[],
): BoardFieldDefinition<FieldMetadata>[] => {
return viewFields
.map((viewField) => {
const correspondingFieldMetadata = fieldsMetadata.find(
({ fieldId }) => viewField.fieldId === fieldId,
);
return correspondingFieldMetadata
? {
fieldId: viewField.fieldId,
label: correspondingFieldMetadata.label,
metadata: correspondingFieldMetadata.metadata,
entityChipDisplayMapper:
correspondingFieldMetadata.entityChipDisplayMapper,
infoTooltipContent: correspondingFieldMetadata.infoTooltipContent,
basePathToShowPage: correspondingFieldMetadata.basePathToShowPage,
Icon: correspondingFieldMetadata.Icon,
type: correspondingFieldMetadata.type,
position: viewField.position,
isVisible: viewField.isVisible,
viewFieldId: viewField.id,
}
: null;
})
.filter(assertNotNull);
};

View File

@ -0,0 +1,15 @@
import { Filter } from '@/ui/data/filter/types/Filter';
import { ViewFilter } from '../types/ViewFilter';
export const viewFiltersToFilters = (viewFilters: ViewFilter[]): Filter[] => {
return viewFilters.map((viewFilter) => {
return {
fieldId: viewFilter.fieldId,
value: viewFilter.value,
displayValue: viewFilter.displayValue,
operand: viewFilter.operand,
definition: viewFilter.definition,
};
});
};

View File

@ -0,0 +1,13 @@
import { Sort } from '@/ui/data/sort/types/Sort';
import { ViewSort } from '../types/ViewSort';
export const viewSortsToSorts = (viewSorts: ViewSort[]): Sort[] => {
return viewSorts.map((viewSort) => {
return {
fieldId: viewSort.fieldId,
direction: viewSort.direction,
definition: viewSort.definition,
};
});
};

View File

@ -28,7 +28,7 @@ import { getLogoUrlFromDomainName } from '~/utils';
import { CompanyNameEditableField } from '../../modules/companies/editable-field/components/CompanyNameEditableField'; import { CompanyNameEditableField } from '../../modules/companies/editable-field/components/CompanyNameEditableField';
import { ShowPageContainer } from '../../modules/ui/layout/page/ShowPageContainer'; import { ShowPageContainer } from '../../modules/ui/layout/page/ShowPageContainer';
import { companyShowFieldDefinition } from './constants/companyShowFieldDefinition'; import { companyShowFieldDefinitions } from './constants/companyShowFieldDefinitions';
export const CompanyShow = () => { export const CompanyShow = () => {
const companyId = useParams().companyId ?? ''; const companyId = useParams().companyId ?? '';
@ -90,7 +90,7 @@ export const CompanyShow = () => {
avatarType="squared" avatarType="squared"
/> />
<PropertyBox extraPadding={true}> <PropertyBox extraPadding={true}>
{companyShowFieldDefinition.map((fieldDefinition) => { {companyShowFieldDefinitions.map((fieldDefinition) => {
return ( return (
<FieldContext.Provider <FieldContext.Provider
key={company.id + fieldDefinition.fieldId} key={company.id + fieldDefinition.fieldId}

View File

@ -1,51 +0,0 @@
import { FilterDefinitionByEntity } from '@/ui/data/filter/types/FilterDefinitionByEntity';
import {
IconBuildingSkyscraper,
IconCalendarEvent,
IconLink,
IconMap,
IconUser,
IconUsers,
} from '@/ui/display/icon/index';
import { FilterDropdownUserSearchSelect } from '@/users/components/FilterDropdownUserSearchSelect';
import { Company } from '~/generated/graphql';
export const companyAvailableFilters: FilterDefinitionByEntity<Company>[] = [
{
key: 'name',
label: 'Name',
Icon: IconBuildingSkyscraper,
type: 'text',
},
{
key: 'employees',
label: 'Employees',
Icon: IconUsers,
type: 'number',
},
{
key: 'domainName',
label: 'URL',
Icon: IconLink,
type: 'text',
},
{
key: 'address',
label: 'Address',
Icon: IconMap,
type: 'text',
},
{
key: 'createdAt',
label: 'Created at',
Icon: IconCalendarEvent,
type: 'date',
},
{
key: 'accountOwnerId',
label: 'Account owner',
Icon: IconUser,
type: 'entity',
entitySelectComponent: <FilterDropdownUserSearchSelect />,
},
];

View File

@ -20,7 +20,7 @@ import {
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
import { User } from '~/generated/graphql'; import { User } from '~/generated/graphql';
export const companyShowFieldDefinition: FieldDefinition<FieldMetadata>[] = [ export const companyShowFieldDefinitions: FieldDefinition<FieldMetadata>[] = [
{ {
fieldId: 'domainName', fieldId: 'domainName',
label: 'Domain name', label: 'Domain name',

View File

@ -0,0 +1,52 @@
import { FilterDefinitionByEntity } from '@/ui/data/filter/types/FilterDefinitionByEntity';
import {
IconBuildingSkyscraper,
IconCalendarEvent,
IconLink,
IconMap,
IconUser,
IconUsers,
} from '@/ui/display/icon/index';
import { FilterDropdownUserSearchSelect } from '@/users/components/FilterDropdownUserSearchSelect';
import { Company } from '~/generated/graphql';
export const companyTableFilterDefinitions: FilterDefinitionByEntity<Company>[] =
[
{
fieldId: 'name',
label: 'Name',
Icon: IconBuildingSkyscraper,
type: 'text',
},
{
fieldId: 'employees',
label: 'Employees',
Icon: IconUsers,
type: 'number',
},
{
fieldId: 'domainName',
label: 'URL',
Icon: IconLink,
type: 'text',
},
{
fieldId: 'address',
label: 'Address',
Icon: IconMap,
type: 'text',
},
{
fieldId: 'createdAt',
label: 'Created at',
Icon: IconCalendarEvent,
type: 'date',
},
{
fieldId: 'accountOwnerId',
label: 'Account owner',
Icon: IconUser,
type: 'entity',
entitySelectComponent: <FilterDropdownUserSearchSelect />,
},
];

View File

@ -7,29 +7,29 @@ import {
IconUsers, IconUsers,
} from '@/ui/display/icon/index'; } from '@/ui/display/icon/index';
export const companyAvailableSorts: SortDefinition[] = [ export const companyTableSortDefinitions: SortDefinition[] = [
{ {
key: 'name', fieldId: 'name',
label: 'Name', label: 'Name',
Icon: IconBuildingSkyscraper, Icon: IconBuildingSkyscraper,
}, },
{ {
key: 'employees', fieldId: 'employees',
label: 'Employees', label: 'Employees',
Icon: IconUsers, Icon: IconUsers,
}, },
{ {
key: 'domainName', fieldId: 'domainName',
label: 'Url', label: 'Url',
Icon: IconLink, Icon: IconLink,
}, },
{ {
key: 'address', fieldId: 'address',
label: 'Address', label: 'Address',
Icon: IconMap, Icon: IconMap,
}, },
{ {
key: 'createdAt', fieldId: 'createdAt',
label: 'Creation', label: 'Creation',
Icon: IconCalendarEvent, Icon: IconCalendarEvent,
}, },

View File

@ -1,3 +1,5 @@
import styled from '@emotion/styled';
import { CompanyBoard } from '@/companies/board/components/CompanyBoard'; import { CompanyBoard } from '@/companies/board/components/CompanyBoard';
import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-contexts/CompanyBoardRecoilScopeContext'; import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
import { PipelineAddButton } from '@/pipeline/components/PipelineAddButton'; import { PipelineAddButton } from '@/pipeline/components/PipelineAddButton';
@ -11,6 +13,11 @@ import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'
import { useUpdatePipelineStageMutation } from '~/generated/graphql'; import { useUpdatePipelineStageMutation } from '~/generated/graphql';
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions'; import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
const StyledBoardContainer = styled.div`
display: flex;
width: 100%;
`;
export const Opportunities = () => { export const Opportunities = () => {
const { handlePipelineStageAdd, handlePipelineStageDelete } = const { handlePipelineStageAdd, handlePipelineStageDelete } =
usePipelineStages(); usePipelineStages();
@ -51,11 +58,13 @@ export const Opportunities = () => {
scopeId="opportunities" scopeId="opportunities"
CustomRecoilScopeContext={CompanyBoardRecoilScopeContext} CustomRecoilScopeContext={CompanyBoardRecoilScopeContext}
> >
<CompanyBoard <StyledBoardContainer>
onColumnAdd={handlePipelineStageAdd} <CompanyBoard
onColumnDelete={handlePipelineStageDelete} onColumnAdd={handlePipelineStageAdd}
onEditColumnTitle={handleEditColumnTitle} onColumnDelete={handlePipelineStageDelete}
/> onEditColumnTitle={handleEditColumnTitle}
/>
</StyledBoardContainer>
</RecoilScope> </RecoilScope>
</BoardOptionsContext.Provider> </BoardOptionsContext.Provider>
</PageBody> </PageBody>

View File

@ -8,31 +8,31 @@ import {
} from '@/ui/display/icon/index'; } from '@/ui/display/icon/index';
import { PipelineProgress } from '~/generated/graphql'; import { PipelineProgress } from '~/generated/graphql';
import { FilterDropdownPeopleSearchSelect } from '../../modules/people/components/FilterDropdownPeopleSearchSelect'; import { FilterDropdownPeopleSearchSelect } from '../../../modules/people/components/FilterDropdownPeopleSearchSelect';
export const opportunitiesFilters: FilterDefinitionByEntity<PipelineProgress>[] = export const opportunityBoardFilterDefinitions: FilterDefinitionByEntity<PipelineProgress>[] =
[ [
{ {
key: 'amount', fieldId: 'amount',
label: 'Amount', label: 'Amount',
Icon: IconCurrencyDollar, Icon: IconCurrencyDollar,
type: 'number', type: 'number',
}, },
{ {
key: 'closeDate', fieldId: 'closeDate',
label: 'Close date', label: 'Close date',
Icon: IconCalendarEvent, Icon: IconCalendarEvent,
type: 'date', type: 'date',
}, },
{ {
key: 'companyId', fieldId: 'companyId',
label: 'Company', label: 'Company',
Icon: IconBuildingSkyscraper, Icon: IconBuildingSkyscraper,
type: 'entity', type: 'entity',
entitySelectComponent: <FilterDropdownCompanySearchSelect />, entitySelectComponent: <FilterDropdownCompanySearchSelect />,
}, },
{ {
key: 'pointOfContactId', fieldId: 'pointOfContactId',
label: 'Point of contact', label: 'Point of contact',
Icon: IconUser, Icon: IconUser,
type: 'entity', type: 'entity',

View File

@ -1,19 +1,19 @@
import { SortDefinition } from '@/ui/data/sort/types/SortDefinition'; import { SortDefinition } from '@/ui/data/sort/types/SortDefinition';
import { IconCalendarEvent, IconCurrencyDollar } from '@/ui/display/icon/index'; import { IconCalendarEvent, IconCurrencyDollar } from '@/ui/display/icon/index';
export const opportunitiesSorts: SortDefinition[] = [ export const opportunityBoardSortDefinitions: SortDefinition[] = [
{ {
key: 'createdAt', fieldId: 'createdAt',
label: 'Creation', label: 'Creation',
Icon: IconCalendarEvent, Icon: IconCalendarEvent,
}, },
{ {
key: 'amount', fieldId: 'amount',
label: 'Amount', label: 'Amount',
Icon: IconCurrencyDollar, Icon: IconCurrencyDollar,
}, },
{ {
key: 'closeDate', fieldId: 'closeDate',
label: 'Expected close date', label: 'Expected close date',
Icon: IconCalendarEvent, Icon: IconCalendarEvent,
}, },

View File

@ -2,12 +2,12 @@ import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
import { NewCompanyProgressButton } from '@/companies/components/NewCompanyProgressButton'; import { NewCompanyProgressButton } from '@/companies/components/NewCompanyProgressButton';
import { BoardOptions } from '@/ui/layout/board/types/BoardOptions'; import { BoardOptions } from '@/ui/layout/board/types/BoardOptions';
import { opportunitiesFilters } from './opportunities-filters'; import { opportunityBoardFilterDefinitions } from './constants/opportunityBoardFilterDefinitions';
import { opportunitiesSorts } from './opportunities-sorts'; import { opportunityBoardSortDefinitions } from './constants/opportunityBoardSortDefinitions';
export const opportunitiesBoardOptions: BoardOptions = { export const opportunitiesBoardOptions: BoardOptions = {
newCardComponent: <NewCompanyProgressButton />, newCardComponent: <NewCompanyProgressButton />,
CardComponent: CompanyBoardCard, CardComponent: CompanyBoardCard,
filters: opportunitiesFilters, filterDefinitions: opportunityBoardFilterDefinitions,
sorts: opportunitiesSorts, sortDefinitions: opportunityBoardSortDefinitions,
}; };

View File

@ -2,7 +2,7 @@ import styled from '@emotion/styled';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect'; import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
import { PeopleTable } from '@/people/table/components/PeopleTable'; import { PersonTable } from '@/people/table/components/PersonTable';
import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider'; import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider';
import { DataTableActionBar } from '@/ui/data/data-table/action-bar/components/DataTableActionBar'; import { DataTableActionBar } from '@/ui/data/data-table/action-bar/components/DataTableActionBar';
import { DataTableContextMenu } from '@/ui/data/data-table/context-menu/components/DataTableContextMenu'; import { DataTableContextMenu } from '@/ui/data/data-table/context-menu/components/DataTableContextMenu';
@ -62,7 +62,7 @@ export const People = () => {
CustomRecoilScopeContext={TableRecoilScopeContext} CustomRecoilScopeContext={TableRecoilScopeContext}
> >
<StyledTableContainer> <StyledTableContainer>
<PeopleTable /> <PersonTable />
</StyledTableContainer> </StyledTableContainer>
<DataTableActionBar /> <DataTableActionBar />
<DataTableContextMenu /> <DataTableContextMenu />

View File

@ -31,7 +31,7 @@ import {
import { PeopleFullNameEditableField } from '../../modules/people/editable-field/components/PeopleFullNameEditableField'; import { PeopleFullNameEditableField } from '../../modules/people/editable-field/components/PeopleFullNameEditableField';
import { ShowPageContainer } from '../../modules/ui/layout/page/ShowPageContainer'; import { ShowPageContainer } from '../../modules/ui/layout/page/ShowPageContainer';
import { personShowFieldDefinition } from './constants/personShowFieldDefinition'; import { personShowFieldDefinition } from './constants/personShowFieldDefinitions';
export const PersonShow = () => { export const PersonShow = () => {
const personId = useParams().personId ?? ''; const personId = useParams().personId ?? '';

Some files were not shown because too many files have changed in this diff Show More