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:
@ -4,17 +4,13 @@ import { PageAddButton } from '@/ui/layout/page/PageAddButton';
|
||||
import { ActivityType } from '~/generated/graphql';
|
||||
|
||||
export const PageAddTaskButton = () => {
|
||||
const { selectedFilters } = useFilter();
|
||||
const { selectedFilter } = useFilter();
|
||||
const openCreateActivity = useOpenCreateActivityDrawer();
|
||||
|
||||
const assigneeIdFilter = selectedFilters.find(
|
||||
(filter) => filter.key === 'assigneeId',
|
||||
);
|
||||
|
||||
const handleClick = () => {
|
||||
openCreateActivity({
|
||||
type: ActivityType.Task,
|
||||
assigneeId: assigneeIdFilter?.value,
|
||||
assigneeId: selectedFilter?.value,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -17,12 +17,14 @@ export const useCurrentUserTaskCount = () => {
|
||||
completedAt: { equals: null },
|
||||
...(currentUser
|
||||
? turnFilterIntoWhereClause({
|
||||
key: 'assigneeId',
|
||||
type: 'entity',
|
||||
fieldId: 'assigneeId',
|
||||
value: currentUser.id,
|
||||
operand: ViewFilterOperand.Is,
|
||||
displayValue: currentUser.displayName,
|
||||
displayAvatarUrl: currentUser.avatarUrl ?? undefined,
|
||||
definition: {
|
||||
type: 'entity',
|
||||
},
|
||||
})
|
||||
: {}),
|
||||
},
|
||||
|
||||
@ -7,7 +7,7 @@ import { ActivityType, useGetActivitiesQuery } from '~/generated/graphql';
|
||||
import { parseDate } from '~/utils/date-utils';
|
||||
|
||||
export const useTasks = (entity?: ActivityTargetableEntity) => {
|
||||
const { selectedFilters } = useFilter();
|
||||
const { selectedFilter } = useFilter();
|
||||
|
||||
const whereFilters = entity
|
||||
? {
|
||||
@ -20,12 +20,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
|
||||
},
|
||||
},
|
||||
}
|
||||
: Object.assign(
|
||||
{},
|
||||
...selectedFilters.map((filter) => {
|
||||
return turnFilterIntoWhereClause(filter);
|
||||
}),
|
||||
);
|
||||
: Object.assign({}, turnFilterIntoWhereClause(selectedFilter));
|
||||
|
||||
const { data: completeTasksData } = useGetActivitiesQuery({
|
||||
variables: {
|
||||
@ -35,7 +30,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
|
||||
...whereFilters,
|
||||
},
|
||||
},
|
||||
skip: !entity && selectedFilters.length === 0,
|
||||
skip: !entity && !selectedFilter,
|
||||
});
|
||||
|
||||
const { data: incompleteTaskData } = useGetActivitiesQuery({
|
||||
@ -46,7 +41,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
|
||||
...whereFilters,
|
||||
},
|
||||
},
|
||||
skip: !entity && selectedFilters.length === 0,
|
||||
skip: !entity && !selectedFilter,
|
||||
});
|
||||
|
||||
const todayOrPreviousTasks = incompleteTaskData?.findManyActivities.filter(
|
||||
|
||||
@ -1,15 +1,29 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
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 {
|
||||
EntityBoard,
|
||||
EntityBoardProps,
|
||||
} from '@/ui/layout/board/components/EntityBoard';
|
||||
import { EntityBoardActionBar } from '@/ui/layout/board/components/EntityBoardActionBar';
|
||||
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 { HooksCompanyBoardEffect } from '../../components/HooksCompanyBoardEffect';
|
||||
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<
|
||||
EntityBoardProps,
|
||||
'onColumnAdd' | 'onColumnDelete' | 'onEditColumnTitle'
|
||||
@ -20,24 +34,30 @@ export const CompanyBoard = ({
|
||||
onColumnDelete,
|
||||
onEditColumnTitle,
|
||||
}: CompanyBoardProps) => {
|
||||
const viewScopeId = 'company-board-view';
|
||||
return (
|
||||
<>
|
||||
<BoardContext.Provider
|
||||
value={{
|
||||
BoardRecoilScopeContext: CompanyBoardRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<HooksCompanyBoardEffect />
|
||||
|
||||
<EntityBoard
|
||||
boardOptions={opportunitiesBoardOptions}
|
||||
onColumnAdd={onColumnAdd}
|
||||
onColumnDelete={onColumnDelete}
|
||||
onEditColumnTitle={onEditColumnTitle}
|
||||
/>
|
||||
<EntityBoardActionBar />
|
||||
<EntityBoardContextMenu />
|
||||
</BoardContext.Provider>
|
||||
</>
|
||||
<ViewScope viewScopeId={viewScopeId}>
|
||||
<StyledContainer>
|
||||
<BoardContext.Provider
|
||||
value={{
|
||||
BoardRecoilScopeContext: CompanyBoardRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<ViewBar
|
||||
optionsDropdownButton={<BoardOptionsDropdown />}
|
||||
optionsDropdownScopeId={BoardOptionsDropdownId}
|
||||
/>
|
||||
<HooksCompanyBoardEffect />
|
||||
<EntityBoard
|
||||
boardOptions={opportunitiesBoardOptions}
|
||||
onColumnAdd={onColumnAdd}
|
||||
onColumnDelete={onColumnDelete}
|
||||
onEditColumnTitle={onEditColumnTitle}
|
||||
/>
|
||||
<EntityBoardActionBar />
|
||||
<EntityBoardContextMenu />
|
||||
</BoardContext.Provider>
|
||||
</StyledContainer>
|
||||
</ViewScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -2,12 +2,19 @@ import { useEffect, useMemo } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
|
||||
import { turnFilterIntoWhereClause } from '@/ui/data/filter/utils/turnFilterIntoWhereClause';
|
||||
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 { availableBoardCardFieldsScopedState } from '@/ui/layout/board/states/availableBoardCardFieldsScopedState';
|
||||
import { boardCardFieldsScopedState } from '@/ui/layout/board/states/boardCardFieldsScopedState';
|
||||
import { isBoardLoadedState } from '@/ui/layout/board/states/isBoardLoadedState';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
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 {
|
||||
Pipeline,
|
||||
PipelineProgressableType,
|
||||
@ -22,20 +29,30 @@ import { useUpdateCompanyBoard } from '../hooks/useUpdateCompanyBoardColumns';
|
||||
|
||||
export const HooksCompanyBoardEffect = () => {
|
||||
const {
|
||||
setAvailableFilters,
|
||||
setAvailableSorts,
|
||||
setAvailableFilterDefinitions,
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableFieldDefinitions,
|
||||
setEntityCountInCurrentView,
|
||||
setViewObjectId,
|
||||
setViewType,
|
||||
} = useView();
|
||||
|
||||
const { currentViewFilters } = useViewInternalStates();
|
||||
|
||||
useEffect(() => {
|
||||
setAvailableFilters(opportunitiesBoardOptions.filters);
|
||||
setAvailableSorts?.(opportunitiesBoardOptions.sorts);
|
||||
}, [setAvailableFilters, setAvailableSorts]);
|
||||
const { currentViewFilters, currentViewFields } = useViewGetStates();
|
||||
|
||||
const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState);
|
||||
|
||||
const { BoardRecoilScopeContext } = useBoardContext();
|
||||
|
||||
const [, setBoardCardFields] = useRecoilScopedState(
|
||||
boardCardFieldsScopedState,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setAvailableBoardCardFields] = useRecoilScopedState(
|
||||
availableBoardCardFieldsScopedState,
|
||||
BoardRecoilScopeContext,
|
||||
);
|
||||
|
||||
const updateCompanyBoard = useUpdateCompanyBoard();
|
||||
|
||||
const { data: pipelineData, loading: loadingGetPipelines } =
|
||||
@ -51,6 +68,21 @@ export const HooksCompanyBoardEffect = () => {
|
||||
|
||||
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
|
||||
?.map((pipelineStage) => pipelineStage.id)
|
||||
.flat();
|
||||
@ -107,6 +139,7 @@ export const HooksCompanyBoardEffect = () => {
|
||||
if (!loading && pipeline && pipelineProgresses && companiesData) {
|
||||
setActionBarEntries();
|
||||
setContextMenuEntries();
|
||||
setAvailableBoardCardFields(pipelineAvailableFieldDefinitions);
|
||||
updateCompanyBoard(pipeline, pipelineProgresses, companiesData.companies);
|
||||
setEntityCountInCurrentView(companiesData.companies.length);
|
||||
}
|
||||
@ -120,7 +153,19 @@ export const HooksCompanyBoardEffect = () => {
|
||||
setContextMenuEntries,
|
||||
searchParams,
|
||||
setEntityCountInCurrentView,
|
||||
setAvailableBoardCardFields,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentViewFields) {
|
||||
setBoardCardFields(
|
||||
viewFieldsToBoardFieldDefinitions(
|
||||
currentViewFields,
|
||||
pipelineAvailableFieldDefinitions,
|
||||
),
|
||||
);
|
||||
}
|
||||
}, [currentViewFields, setBoardCardFields]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
@ -8,40 +8,59 @@ import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyT
|
||||
import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport';
|
||||
import { DataTable } from '@/ui/data/data-table/components/DataTable';
|
||||
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 { 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 {
|
||||
UpdateOneCompanyMutationVariables,
|
||||
useGetCompaniesQuery,
|
||||
useGetWorkspaceMembersLazyQuery,
|
||||
useUpdateOneCompanyMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { companyAvailableFilters } from '~/pages/companies/companies-filters';
|
||||
import { companyAvailableSorts } from '~/pages/companies/companies-sorts';
|
||||
import { companyTableFilterDefinitions } from '~/pages/companies/constants/companyTableFilterDefinitions';
|
||||
import { companyTableSortDefinitions } from '~/pages/companies/constants/companyTableSortDefinitions';
|
||||
|
||||
import CompanyTableEffect from './CompanyTableEffect';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
export const CompanyTable = () => {
|
||||
const tableViewScopeId = 'company-table';
|
||||
const viewScopeId = 'company-table-view';
|
||||
const tableScopeId = 'companies';
|
||||
const setTableColumns = useSetRecoilState(
|
||||
tableColumnsScopedState('companies'),
|
||||
tableColumnsScopedState(tableScopeId),
|
||||
);
|
||||
|
||||
const setTableFilters = useSetRecoilState(
|
||||
tableFiltersScopedState(tableScopeId),
|
||||
);
|
||||
|
||||
const setTableSorts = useSetRecoilState(tableSortsScopedState(tableScopeId));
|
||||
|
||||
const [updateEntityMutation] = useUpdateOneCompanyMutation();
|
||||
const upsertDataTableItem = useUpsertDataTableItem();
|
||||
|
||||
const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery();
|
||||
const { persistViewFields } = useViewFields(tableViewScopeId);
|
||||
const { setCurrentViewFields, currentViewId } = useView({
|
||||
viewScopeId: tableViewScopeId,
|
||||
const { persistViewFields } = useViewFields(viewScopeId);
|
||||
const { setCurrentViewFields } = useView({
|
||||
viewScopeId,
|
||||
});
|
||||
|
||||
const { setContextMenuEntries } = useCompanyTableContextMenuEntries();
|
||||
@ -79,16 +98,9 @@ export const CompanyTable = () => {
|
||||
const { openCompanySpreadsheetImport: onImport } =
|
||||
useSpreadsheetCompanyImport();
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
return (
|
||||
<ViewScope
|
||||
viewScopeId={tableViewScopeId}
|
||||
viewScopeId={viewScopeId}
|
||||
onViewFieldsChange={(viewFields) => {
|
||||
setTableColumns(
|
||||
viewFieldsToColumnDefinitions(
|
||||
@ -97,7 +109,12 @@ export const CompanyTable = () => {
|
||||
),
|
||||
);
|
||||
}}
|
||||
onViewFiltersChange={() => {}}
|
||||
onViewFiltersChange={(viewFilters) => {
|
||||
setTableFilters(viewFiltersToFilters(viewFilters));
|
||||
}}
|
||||
onViewSortsChange={(viewSorts) => {
|
||||
setTableSorts(viewSortsToSorts(viewSorts));
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<TableContext.Provider
|
||||
@ -110,7 +127,7 @@ export const CompanyTable = () => {
|
||||
>
|
||||
<ViewBar
|
||||
optionsDropdownButton={<TableOptionsDropdown onImport={onImport} />}
|
||||
optionsDropdownScopeId="table-dropdown-option"
|
||||
optionsDropdownScopeId={TableOptionsDropdownId}
|
||||
/>
|
||||
<CompanyTableEffect />
|
||||
<DataTableEffect
|
||||
@ -119,8 +136,8 @@ export const CompanyTable = () => {
|
||||
getRequestOptimisticEffectDefinition={
|
||||
getCompaniesOptimisticEffectDefinition
|
||||
}
|
||||
filterDefinitionArray={companyAvailableFilters}
|
||||
sortDefinitionArray={companyAvailableSorts}
|
||||
filterDefinitionArray={companyTableFilterDefinitions}
|
||||
sortDefinitionArray={companyTableSortDefinitions}
|
||||
setContextMenuEntries={setContextMenuEntries}
|
||||
setActionBarEntries={setActionBarEntries}
|
||||
/>
|
||||
|
||||
@ -6,14 +6,14 @@ import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scop
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { companyAvailableFilters } from '~/pages/companies/companies-filters';
|
||||
import { companyAvailableSorts } from '~/pages/companies/companies-sorts';
|
||||
import { companyTableFilterDefinitions } from '~/pages/companies/constants/companyTableFilterDefinitions';
|
||||
import { companyTableSortDefinitions } from '~/pages/companies/constants/companyTableSortDefinitions';
|
||||
|
||||
const CompanyTableEffect = () => {
|
||||
const {
|
||||
setAvailableSorts,
|
||||
setAvailableFilters,
|
||||
setAvailableFields,
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
setAvailableFieldDefinitions,
|
||||
setViewType,
|
||||
setViewObjectId,
|
||||
} = useView();
|
||||
@ -24,26 +24,21 @@ const CompanyTableEffect = () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setAvailableSorts?.(companyAvailableSorts);
|
||||
setAvailableFilters?.(companyAvailableFilters);
|
||||
setAvailableFields?.(companiesAvailableFieldDefinitions);
|
||||
setAvailableSortDefinitions?.(companyTableSortDefinitions);
|
||||
setAvailableFilterDefinitions?.(companyTableFilterDefinitions);
|
||||
setAvailableFieldDefinitions?.(companiesAvailableFieldDefinitions);
|
||||
setViewObjectId?.('company');
|
||||
setViewType?.(ViewType.Table);
|
||||
|
||||
setAvailableTableColumns(companiesAvailableFieldDefinitions);
|
||||
}, [
|
||||
setAvailableFields,
|
||||
setAvailableFilters,
|
||||
setAvailableSorts,
|
||||
setAvailableFieldDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableTableColumns,
|
||||
setViewObjectId,
|
||||
setViewType,
|
||||
]);
|
||||
// useEffect(() => {
|
||||
// if (currentViewFields) {
|
||||
// setTableColumns([...currentViewFields].sort((a, b) => a.index - b.index));
|
||||
// }
|
||||
// }, [currentViewFields, setTableColumns]);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (currentViewSorts) {
|
||||
|
||||
@ -17,7 +17,6 @@ export const CompanyTableMockDataEffect = () => {
|
||||
|
||||
useEffect(() => {
|
||||
setDataTableData(mockedCompaniesData, [], []);
|
||||
|
||||
setTableColumns(companiesAvailableFieldDefinitions);
|
||||
}, [setDataTableData, setTableColumns]);
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
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 { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
@ -10,22 +11,26 @@ export const MetadataObjectNavItems = () => {
|
||||
const { metadataObjects } = useFindManyMetadataObjects();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { icons } = useLazyLoadIcons();
|
||||
|
||||
return (
|
||||
<>
|
||||
{metadataObjects
|
||||
.filter((metadataObject) => !!metadataObject.isActive)
|
||||
.map((metadataObject) => (
|
||||
<NavItem
|
||||
key={metadataObject.id}
|
||||
label={capitalize(metadataObject.namePlural)}
|
||||
to={`/objects/${metadataObject.namePlural}`}
|
||||
Icon={IconBuildingSkyscraper}
|
||||
onClick={() => {
|
||||
navigate(`/objects/${metadataObject.namePlural}`);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
.filter((metadataObjects) => !metadataObjects.namePlural.endsWith('V2'))
|
||||
.map((metadataObject) => {
|
||||
return (
|
||||
<NavItem
|
||||
key={metadataObject.id}
|
||||
label={capitalize(metadataObject.namePlural)}
|
||||
to={`/objects/${metadataObject.namePlural}`}
|
||||
Icon={metadataObject.icon ? icons[metadataObject.icon] : Icon123}
|
||||
onClick={() => {
|
||||
navigate(`/objects/${metadataObject.namePlural}`);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -9,9 +9,9 @@ import { useMetadataObjectInContext } from '../hooks/useMetadataObjectInContext'
|
||||
|
||||
export const ObjectTableEffect = () => {
|
||||
const {
|
||||
setAvailableSorts,
|
||||
setAvailableFilters,
|
||||
setAvailableFields,
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
setAvailableFieldDefinitions,
|
||||
setViewType,
|
||||
setViewObjectId,
|
||||
} = useView();
|
||||
@ -38,22 +38,22 @@ export const ObjectTableEffect = () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setAvailableSorts?.([]); // TODO: extract from metadata fields
|
||||
setAvailableFilters?.([]); // TODO: extract from metadata fields
|
||||
setAvailableFields?.(columnDefinitions);
|
||||
setAvailableSortDefinitions?.([]); // TODO: extract from metadata fields
|
||||
setAvailableFilterDefinitions?.([]); // TODO: extract from metadata fields
|
||||
setAvailableFieldDefinitions?.(columnDefinitions);
|
||||
setViewObjectId?.(objectNamePlural);
|
||||
setViewType?.(ViewType.Table);
|
||||
|
||||
setAvailableTableColumns(columnDefinitions);
|
||||
}, [
|
||||
setAvailableFields,
|
||||
setAvailableFilters,
|
||||
setAvailableSorts,
|
||||
setAvailableTableColumns,
|
||||
setViewObjectId,
|
||||
setViewType,
|
||||
columnDefinitions,
|
||||
objectNamePlural,
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
setAvailableFieldDefinitions,
|
||||
]);
|
||||
|
||||
// useEffect(() => {
|
||||
|
||||
@ -6,6 +6,7 @@ import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
|
||||
import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier';
|
||||
import { formatMetadataFieldAsColumnDefinition } from '../utils/formatMetadataFieldAsColumnDefinition';
|
||||
import { generateCreateOneObjectMutation } from '../utils/generateCreateOneObjectMutation';
|
||||
import { generateDeleteOneObjectMutation } from '../utils/generateDeleteOneObjectMutation';
|
||||
import { generateFindManyCustomObjectsQuery } from '../utils/generateFindManyCustomObjectsQuery';
|
||||
import { generateFindOneCustomObjectQuery } from '../utils/generateFindOneCustomObjectQuery';
|
||||
import { generateUpdateOneObjectMutation } from '../utils/generateUpdateOneObjectMutation';
|
||||
@ -79,7 +80,7 @@ export const useFindOneMetadataObject = ({
|
||||
|
||||
// TODO: implement backend delete
|
||||
const deleteOneMutation = foundMetadataObject
|
||||
? generateCreateOneObjectMutation({
|
||||
? generateDeleteOneObjectMutation({
|
||||
metadataObject: foundMetadataObject,
|
||||
})
|
||||
: gql`
|
||||
|
||||
@ -7,7 +7,7 @@ import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scop
|
||||
import { tableRowIdsState } from '@/ui/data/data-table/states/tableRowIdsState';
|
||||
import { entityFieldsFamilyState } from '@/ui/data/field/states/entityFieldsFamilyState';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
import { availableSortsScopedState } from '@/views/states/availableSortsScopedState';
|
||||
import { availableSortDefinitionsScopedState } from '@/views/states/availableSortDefinitionsScopedState';
|
||||
|
||||
export const useSetObjectDataTableData = () => {
|
||||
const resetTableRowSelection = useResetTableRowSelection();
|
||||
@ -41,7 +41,10 @@ export const useSetObjectDataTableData = () => {
|
||||
|
||||
set(numberOfTableRowsState, entityIds.length);
|
||||
|
||||
set(availableSortsScopedState({ scopeId: tableContextScopeId }), []);
|
||||
set(
|
||||
availableSortDefinitionsScopedState({ scopeId: tableContextScopeId }),
|
||||
[],
|
||||
);
|
||||
|
||||
set(isFetchingDataTableDataState, false);
|
||||
},
|
||||
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
135
front/src/modules/people/table/components/PersonTable.tsx
Normal file
135
front/src/modules/people/table/components/PersonTable.tsx
Normal 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>
|
||||
);
|
||||
};
|
||||
@ -7,14 +7,14 @@ import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumn
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { peopleAvailableFilters } from '~/pages/people/people-filters';
|
||||
import { peopleAvailableSorts } from '~/pages/people/people-sorts';
|
||||
import { personTableFilterDefinitions } from '~/pages/people/constants/personTableFilterDefinitions';
|
||||
import { personTableSortDefinitions } from '~/pages/people/constants/personTableSortDefinitions';
|
||||
|
||||
const PeopleTableEffect = () => {
|
||||
const {
|
||||
setAvailableSorts,
|
||||
setAvailableFilters,
|
||||
setAvailableFields,
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
setAvailableFieldDefinitions,
|
||||
setViewType,
|
||||
setViewObjectId,
|
||||
} = useView();
|
||||
@ -30,17 +30,17 @@ const PeopleTableEffect = () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setAvailableSorts?.(peopleAvailableSorts);
|
||||
setAvailableFilters?.(peopleAvailableFilters);
|
||||
setAvailableFields?.(peopleAvailableFieldDefinitions);
|
||||
setAvailableSortDefinitions?.(personTableSortDefinitions);
|
||||
setAvailableFilterDefinitions?.(personTableFilterDefinitions);
|
||||
setAvailableFieldDefinitions?.(peopleAvailableFieldDefinitions);
|
||||
setViewObjectId?.('person');
|
||||
setViewType?.(ViewType.Table);
|
||||
|
||||
setAvailableTableColumns(peopleAvailableFieldDefinitions);
|
||||
}, [
|
||||
setAvailableFields,
|
||||
setAvailableFilters,
|
||||
setAvailableSorts,
|
||||
setAvailableFieldDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableTableColumns,
|
||||
setTableColumns,
|
||||
setViewObjectId,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition';
|
||||
import {
|
||||
FieldDateMetadata,
|
||||
FieldMetadata,
|
||||
@ -12,10 +13,9 @@ import {
|
||||
IconUser,
|
||||
} from '@/ui/display/icon';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { BoardFieldDefinition } from '@/ui/layout/board/types/BoardFieldDefinition';
|
||||
import { Person } from '~/generated/graphql';
|
||||
|
||||
export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetadata>[] =
|
||||
export const pipelineAvailableFieldDefinitions: ColumnDefinition<FieldMetadata>[] =
|
||||
[
|
||||
{
|
||||
fieldId: 'closeDate',
|
||||
@ -26,10 +26,11 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
|
||||
metadata: {
|
||||
fieldName: 'closeDate',
|
||||
},
|
||||
size: 0,
|
||||
isVisible: true,
|
||||
infoTooltipContent:
|
||||
'Specified date by which an opportunity must be completed.',
|
||||
} satisfies BoardFieldDefinition<FieldDateMetadata>,
|
||||
} satisfies ColumnDefinition<FieldDateMetadata>,
|
||||
{
|
||||
fieldId: 'amount',
|
||||
label: 'Amount',
|
||||
@ -40,9 +41,10 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
|
||||
fieldName: 'amount',
|
||||
placeHolder: '0',
|
||||
},
|
||||
size: 0,
|
||||
isVisible: true,
|
||||
infoTooltipContent: 'Potential monetary value of a business opportunity.',
|
||||
} satisfies BoardFieldDefinition<FieldNumberMetadata>,
|
||||
} satisfies ColumnDefinition<FieldNumberMetadata>,
|
||||
{
|
||||
fieldId: 'probability',
|
||||
label: 'Probability',
|
||||
@ -52,10 +54,11 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
|
||||
metadata: {
|
||||
fieldName: 'probability',
|
||||
},
|
||||
size: 0,
|
||||
isVisible: true,
|
||||
infoTooltipContent:
|
||||
"Level of certainty in the lead's potential to convert into a success.",
|
||||
} satisfies BoardFieldDefinition<FieldProbabilityMetadata>,
|
||||
} satisfies ColumnDefinition<FieldProbabilityMetadata>,
|
||||
{
|
||||
fieldId: 'pointOfContact',
|
||||
label: 'Point of Contact',
|
||||
@ -67,6 +70,7 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
|
||||
relationType: Entity.Person,
|
||||
useEditButton: true,
|
||||
},
|
||||
size: 0,
|
||||
isVisible: true,
|
||||
infoTooltipContent: 'Primary contact within the company.',
|
||||
entityChipDisplayMapper: (dataObject: Person) => {
|
||||
@ -76,5 +80,5 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
|
||||
avatarType: 'rounded',
|
||||
};
|
||||
},
|
||||
} satisfies BoardFieldDefinition<FieldRelationMetadata>,
|
||||
} satisfies ColumnDefinition<FieldRelationMetadata>,
|
||||
];
|
||||
|
||||
@ -55,7 +55,12 @@ export const SettingsObjectIconSection = ({
|
||||
<StyledArrowContainer>
|
||||
<img src={ArrowRight} alt="Arrow right" width={32} height={16} />
|
||||
</StyledArrowContainer>
|
||||
<SettingsObjectIconWithLabel Icon={Icon} label={label || 'Investors'} />
|
||||
{Icon && (
|
||||
<SettingsObjectIconWithLabel
|
||||
Icon={Icon}
|
||||
label={label || 'Investors'}
|
||||
/>
|
||||
)}
|
||||
</StyledContainer>
|
||||
</Section>
|
||||
);
|
||||
|
||||
@ -168,37 +168,35 @@ export const DataTableHeader = () => {
|
||||
>
|
||||
<SelectAllCheckbox />
|
||||
</th>
|
||||
{[...visibleTableColumns]
|
||||
.sort((columnA, columnB) => columnA.position - columnB.position)
|
||||
.map((column) => (
|
||||
<StyledColumnHeaderCell
|
||||
key={column.fieldId}
|
||||
isResizing={resizedFieldKey === column.fieldId}
|
||||
columnWidth={Math.max(
|
||||
tableColumnsByKey[column.fieldId].size +
|
||||
(resizedFieldKey === column.fieldId ? resizeFieldOffset : 0),
|
||||
COLUMN_MIN_WIDTH,
|
||||
)}
|
||||
>
|
||||
<StyledColumnHeadContainer>
|
||||
<ColumnHeadWithDropdown
|
||||
column={column}
|
||||
isFirstColumn={column.position === 1}
|
||||
isLastColumn={
|
||||
column.position === visibleTableColumns.length - 1
|
||||
}
|
||||
primaryColumnKey={primaryColumn?.fieldId || ''}
|
||||
/>
|
||||
</StyledColumnHeadContainer>
|
||||
<StyledResizeHandler
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
onPointerDown={() => {
|
||||
setResizedFieldKey(column.fieldId);
|
||||
}}
|
||||
{visibleTableColumns.map((column) => (
|
||||
<StyledColumnHeaderCell
|
||||
key={column.fieldId}
|
||||
isResizing={resizedFieldKey === column.fieldId}
|
||||
columnWidth={Math.max(
|
||||
tableColumnsByKey[column.fieldId].size +
|
||||
(resizedFieldKey === column.fieldId ? resizeFieldOffset : 0),
|
||||
COLUMN_MIN_WIDTH,
|
||||
)}
|
||||
>
|
||||
<StyledColumnHeadContainer>
|
||||
<ColumnHeadWithDropdown
|
||||
column={column}
|
||||
isFirstColumn={column.position === 1}
|
||||
isLastColumn={
|
||||
column.position === visibleTableColumns.length - 1
|
||||
}
|
||||
primaryColumnKey={primaryColumn?.fieldId || ''}
|
||||
/>
|
||||
</StyledColumnHeaderCell>
|
||||
))}
|
||||
</StyledColumnHeadContainer>
|
||||
<StyledResizeHandler
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
onPointerDown={() => {
|
||||
setResizedFieldKey(column.fieldId);
|
||||
}}
|
||||
/>
|
||||
</StyledColumnHeaderCell>
|
||||
))}
|
||||
<th>
|
||||
{hiddenTableColumns.length > 0 && (
|
||||
<StyledColumnHeadContainer>
|
||||
|
||||
@ -4,7 +4,7 @@ import { entityFieldsFamilyState } from '@/ui/data/field/states/entityFieldsFami
|
||||
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
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 { isFetchingDataTableDataState } from '../states/isFetchingDataTableDataState';
|
||||
@ -54,7 +54,7 @@ export const useSetDataTableData = () => {
|
||||
setEntityCountInCurrentView(entityIds.length);
|
||||
|
||||
set(
|
||||
availableSortsScopedState({ scopeId: tableContextScopeId }),
|
||||
availableSortDefinitionsScopedState({ scopeId: tableContextScopeId }),
|
||||
sortDefinitionArray,
|
||||
);
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { useViewInternalStates } from '@/views/hooks/useViewInternalStates';
|
||||
import { useViewGetStates } from '@/views/hooks/useViewGetStates';
|
||||
|
||||
import { useTableColumns } from '../../hooks/useTableColumns';
|
||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
@ -29,7 +29,7 @@ export const TableOptionsDropdownContent = ({
|
||||
onImport?: () => void;
|
||||
}) => {
|
||||
const { setViewEditMode, handleViewNameSubmit } = useView();
|
||||
const { viewEditMode, currentView } = useViewInternalStates();
|
||||
const { viewEditMode, currentView } = useViewGetStates();
|
||||
|
||||
const { closeDropdown } = useDropdown();
|
||||
|
||||
|
||||
@ -12,12 +12,13 @@ type FilterDropdownButtonProps = {
|
||||
export const FilterDropdownButton = ({
|
||||
hotkeyScope,
|
||||
}: FilterDropdownButtonProps) => {
|
||||
const { availableFilters } = useFilter();
|
||||
const { availableFilterDefinitions } = useFilter();
|
||||
|
||||
const hasOnlyOneEntityFilter =
|
||||
availableFilters.length === 1 && availableFilters[0].type === 'entity';
|
||||
availableFilterDefinitions.length === 1 &&
|
||||
availableFilterDefinitions[0].type === 'entity';
|
||||
|
||||
if (!availableFilters.length) {
|
||||
if (!availableFilterDefinitions.length) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { InternalDatePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
|
||||
import { useUpsertFilter } from '@/views/hooks/useUpsertFilter';
|
||||
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
|
||||
@ -8,19 +7,18 @@ export const FilterDropdownDateSearchInput = () => {
|
||||
filterDefinitionUsedInDropdown,
|
||||
selectedOperandInDropdown,
|
||||
setIsFilterDropdownUnfolded,
|
||||
selectFilter,
|
||||
} = useFilter();
|
||||
|
||||
const upsertFilter = useUpsertFilter();
|
||||
|
||||
const handleChange = (date: Date) => {
|
||||
if (!filterDefinitionUsedInDropdown || !selectedOperandInDropdown) return;
|
||||
|
||||
upsertFilter({
|
||||
key: filterDefinitionUsedInDropdown.key,
|
||||
type: filterDefinitionUsedInDropdown.type,
|
||||
selectFilter?.({
|
||||
fieldId: filterDefinitionUsedInDropdown.fieldId,
|
||||
value: date.toISOString(),
|
||||
operand: selectedOperandInDropdown,
|
||||
displayValue: date.toLocaleDateString(),
|
||||
definition: filterDefinitionUsedInDropdown,
|
||||
});
|
||||
|
||||
setIsFilterDropdownUnfolded(false);
|
||||
|
||||
@ -1,11 +1,8 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useFilterCurrentlyEdited } from '@/ui/data/filter/hooks/useFilterCurrentlyEdited';
|
||||
import { EntitiesForMultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect';
|
||||
import { SingleEntitySelectBase } from '@/ui/input/relation-picker/components/SingleEntitySelectBase';
|
||||
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 { useFilter } from '../hooks/useFilter';
|
||||
@ -16,20 +13,16 @@ export const FilterDropdownEntitySearchSelect = ({
|
||||
entitiesForSelect: EntitiesForMultipleEntitySelect<EntityForSelect>;
|
||||
}) => {
|
||||
const {
|
||||
filterDropdownSelectedEntityId,
|
||||
setFilterDropdownSelectedEntityId,
|
||||
filterDefinitionUsedInDropdown,
|
||||
selectedOperandInDropdown,
|
||||
filterDropdownSearchInput,
|
||||
selectedFilter,
|
||||
selectFilter,
|
||||
} = useFilter();
|
||||
|
||||
const [isAllEntitySelected, setIsAllEntitySelected] = useState(false);
|
||||
|
||||
const upsertFilter = useUpsertFilter();
|
||||
const removeFilter = useRemoveFilter();
|
||||
|
||||
const filterCurrentlyEdited = useFilterCurrentlyEdited();
|
||||
|
||||
const handleUserSelected = (
|
||||
selectedEntity: EntityForSelect | null | undefined,
|
||||
) => {
|
||||
@ -45,24 +38,16 @@ export const FilterDropdownEntitySearchSelect = ({
|
||||
setIsAllEntitySelected(false);
|
||||
}
|
||||
|
||||
const clickedOnAlreadySelectedEntity =
|
||||
selectedEntity.id === filterDropdownSelectedEntityId;
|
||||
setFilterDropdownSelectedEntityId(selectedEntity.id);
|
||||
|
||||
if (clickedOnAlreadySelectedEntity) {
|
||||
removeFilter(filterDefinitionUsedInDropdown.key);
|
||||
setFilterDropdownSelectedEntityId(null);
|
||||
} else {
|
||||
setFilterDropdownSelectedEntityId(selectedEntity.id);
|
||||
|
||||
upsertFilter({
|
||||
displayValue: selectedEntity.name,
|
||||
key: filterDefinitionUsedInDropdown.key,
|
||||
operand: selectedOperandInDropdown,
|
||||
type: filterDefinitionUsedInDropdown.type,
|
||||
value: selectedEntity.id,
|
||||
displayAvatarUrl: selectedEntity.avatarUrl,
|
||||
});
|
||||
}
|
||||
selectFilter?.({
|
||||
displayValue: selectedEntity.name,
|
||||
fieldId: filterDefinitionUsedInDropdown.fieldId,
|
||||
operand: selectedOperandInDropdown,
|
||||
value: selectedEntity.id,
|
||||
displayAvatarUrl: selectedEntity.avatarUrl,
|
||||
definition: filterDefinitionUsedInDropdown,
|
||||
});
|
||||
};
|
||||
|
||||
const isAllEntitySelectShown =
|
||||
@ -81,36 +66,30 @@ export const FilterDropdownEntitySearchSelect = ({
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (isAllEntitySelected) {
|
||||
setIsAllEntitySelected(false);
|
||||
|
||||
removeFilter(filterDefinitionUsedInDropdown.key);
|
||||
} else {
|
||||
setIsAllEntitySelected(true);
|
||||
setIsAllEntitySelected(true);
|
||||
setFilterDropdownSelectedEntityId(null);
|
||||
|
||||
setFilterDropdownSelectedEntityId(null);
|
||||
|
||||
upsertFilter({
|
||||
displayValue: filterDefinitionUsedInDropdown.selectAllLabel,
|
||||
key: filterDefinitionUsedInDropdown.key,
|
||||
operand: ViewFilterOperand.IsNotNull,
|
||||
type: filterDefinitionUsedInDropdown.type,
|
||||
value: '',
|
||||
});
|
||||
}
|
||||
selectFilter?.({
|
||||
displayValue: filterDefinitionUsedInDropdown.selectAllLabel,
|
||||
fieldId: filterDefinitionUsedInDropdown.fieldId,
|
||||
operand: ViewFilterOperand.IsNotNull,
|
||||
value: '',
|
||||
definition: filterDefinitionUsedInDropdown,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!filterCurrentlyEdited) {
|
||||
if (!selectedFilter) {
|
||||
setFilterDropdownSelectedEntityId(null);
|
||||
} else {
|
||||
setFilterDropdownSelectedEntityId(filterCurrentlyEdited.value);
|
||||
setFilterDropdownSelectedEntityId(selectedFilter.value);
|
||||
setIsAllEntitySelected(
|
||||
filterCurrentlyEdited.operand === ViewFilterOperand.IsNotNull,
|
||||
selectedFilter.operand === ViewFilterOperand.IsNotNull,
|
||||
);
|
||||
}
|
||||
}, [
|
||||
filterCurrentlyEdited,
|
||||
selectedFilter,
|
||||
setFilterDropdownSelectedEntityId,
|
||||
entitiesForSelect.selectedEntities,
|
||||
]);
|
||||
|
||||
@ -11,32 +11,32 @@ export const FilterDropdownFilterSelect = () => {
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
setFilterDropdownSearchInput,
|
||||
availableFilters,
|
||||
availableFilterDefinitions,
|
||||
} = useFilter();
|
||||
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
{availableFilters.map((availableFilter, index) => (
|
||||
{availableFilterDefinitions.map((availableFilterDefinition, index) => (
|
||||
<MenuItem
|
||||
key={`select-filter-${index}`}
|
||||
testId={`select-filter-${index}`}
|
||||
onClick={() => {
|
||||
setFilterDefinitionUsedInDropdown(availableFilter);
|
||||
setFilterDefinitionUsedInDropdown(availableFilterDefinition);
|
||||
|
||||
if (availableFilter.type === 'entity') {
|
||||
if (availableFilterDefinition.type === 'entity') {
|
||||
setHotkeyScope(RelationPickerHotkeyScope.RelationPicker);
|
||||
}
|
||||
|
||||
setSelectedOperandInDropdown(
|
||||
getOperandsForFilterType(availableFilter.type)?.[0],
|
||||
getOperandsForFilterType(availableFilterDefinition.type)?.[0],
|
||||
);
|
||||
|
||||
setFilterDropdownSearchInput('');
|
||||
}}
|
||||
LeftIcon={availableFilter.Icon}
|
||||
text={availableFilter.label}
|
||||
LeftIcon={availableFilterDefinition.Icon}
|
||||
text={availableFilterDefinition.label}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
|
||||
@ -2,16 +2,14 @@ import { ChangeEvent } from 'react';
|
||||
|
||||
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';
|
||||
|
||||
export const FilterDropdownNumberSearchInput = () => {
|
||||
const { selectedOperandInDropdown, filterDefinitionUsedInDropdown } =
|
||||
useFilter();
|
||||
|
||||
const upsertFilter = useUpsertFilter();
|
||||
const removeFilter = useRemoveFilter();
|
||||
const {
|
||||
selectedOperandInDropdown,
|
||||
filterDefinitionUsedInDropdown,
|
||||
selectFilter,
|
||||
} = useFilter();
|
||||
|
||||
return (
|
||||
filterDefinitionUsedInDropdown &&
|
||||
@ -21,17 +19,13 @@ export const FilterDropdownNumberSearchInput = () => {
|
||||
type="number"
|
||||
placeholder={filterDefinitionUsedInDropdown.label}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
if (event.target.value === '') {
|
||||
removeFilter(filterDefinitionUsedInDropdown.key);
|
||||
} else {
|
||||
upsertFilter({
|
||||
key: filterDefinitionUsedInDropdown.key,
|
||||
type: filterDefinitionUsedInDropdown.type,
|
||||
value: event.target.value,
|
||||
operand: selectedOperandInDropdown,
|
||||
displayValue: event.target.value,
|
||||
});
|
||||
}
|
||||
selectFilter?.({
|
||||
fieldId: filterDefinitionUsedInDropdown.fieldId,
|
||||
value: event.target.value,
|
||||
operand: selectedOperandInDropdown,
|
||||
displayValue: event.target.value,
|
||||
definition: filterDefinitionUsedInDropdown,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -2,9 +2,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
|
||||
import { useUpsertFilter } from '../../../../views/hooks/useUpsertFilter';
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
import { useFilterCurrentlyEdited } from '../hooks/useFilterCurrentlyEdited';
|
||||
import { getOperandLabel } from '../utils/getOperandLabel';
|
||||
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
|
||||
|
||||
@ -14,27 +12,25 @@ export const FilterDropdownOperandSelect = () => {
|
||||
setSelectedOperandInDropdown,
|
||||
isFilterDropdownOperandSelectUnfolded,
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
selectedFilter,
|
||||
selectFilter,
|
||||
} = useFilter();
|
||||
|
||||
const operandsForFilterType = getOperandsForFilterType(
|
||||
filterDefinitionUsedInDropdown?.type,
|
||||
);
|
||||
|
||||
const filterCurrentlyEdited = useFilterCurrentlyEdited();
|
||||
|
||||
const upsertFilter = useUpsertFilter();
|
||||
|
||||
const handleOperangeChange = (newOperand: ViewFilterOperand) => {
|
||||
setSelectedOperandInDropdown(newOperand);
|
||||
setIsFilterDropdownOperandSelectUnfolded(false);
|
||||
|
||||
if (filterDefinitionUsedInDropdown && filterCurrentlyEdited) {
|
||||
upsertFilter({
|
||||
key: filterCurrentlyEdited.key,
|
||||
displayValue: filterCurrentlyEdited.displayValue,
|
||||
if (filterDefinitionUsedInDropdown && selectedFilter) {
|
||||
selectFilter?.({
|
||||
fieldId: selectedFilter.fieldId,
|
||||
displayValue: selectedFilter.displayValue,
|
||||
operand: newOperand,
|
||||
type: filterCurrentlyEdited.type,
|
||||
value: filterCurrentlyEdited.value,
|
||||
value: selectedFilter.value,
|
||||
definition: filterDefinitionUsedInDropdown,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -2,10 +2,7 @@ import { ChangeEvent } from 'react';
|
||||
|
||||
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 { useFilterCurrentlyEdited } from '../hooks/useFilterCurrentlyEdited';
|
||||
|
||||
export const FilterDropdownTextSearchInput = () => {
|
||||
const {
|
||||
@ -13,13 +10,10 @@ export const FilterDropdownTextSearchInput = () => {
|
||||
selectedOperandInDropdown,
|
||||
filterDropdownSearchInput,
|
||||
setFilterDropdownSearchInput,
|
||||
selectedFilter,
|
||||
selectFilter,
|
||||
} = useFilter();
|
||||
|
||||
const upsertFilter = useUpsertFilter();
|
||||
const removeFilter = useRemoveFilter();
|
||||
|
||||
const filterCurrentlyEdited = useFilterCurrentlyEdited();
|
||||
|
||||
return (
|
||||
filterDefinitionUsedInDropdown &&
|
||||
selectedOperandInDropdown && (
|
||||
@ -27,21 +21,17 @@ export const FilterDropdownTextSearchInput = () => {
|
||||
autoFocus
|
||||
type="text"
|
||||
placeholder={filterDefinitionUsedInDropdown.label}
|
||||
value={filterCurrentlyEdited?.value ?? filterDropdownSearchInput}
|
||||
value={selectedFilter?.value ?? filterDropdownSearchInput}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setFilterDropdownSearchInput(event.target.value);
|
||||
|
||||
if (event.target.value === '') {
|
||||
removeFilter(filterDefinitionUsedInDropdown.key);
|
||||
} else {
|
||||
upsertFilter({
|
||||
key: filterDefinitionUsedInDropdown.key,
|
||||
type: filterDefinitionUsedInDropdown.type,
|
||||
value: event.target.value,
|
||||
operand: selectedOperandInDropdown,
|
||||
displayValue: event.target.value,
|
||||
});
|
||||
}
|
||||
selectFilter?.({
|
||||
fieldId: filterDefinitionUsedInDropdown.fieldId,
|
||||
value: event.target.value,
|
||||
operand: selectedOperandInDropdown,
|
||||
displayValue: event.target.value,
|
||||
definition: filterDefinitionUsedInDropdown,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -21,13 +21,13 @@ export const SingleEntityFilterDropdownButton = ({
|
||||
hotkeyScope: HotkeyScope;
|
||||
}) => {
|
||||
const {
|
||||
availableFilters,
|
||||
selectedFilters,
|
||||
availableFilterDefinitions,
|
||||
selectedFilter,
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
} = useFilter();
|
||||
|
||||
const availableFilter = availableFilters[0];
|
||||
const availableFilter = availableFilterDefinitions[0];
|
||||
|
||||
React.useEffect(() => {
|
||||
setFilterDefinitionUsedInDropdown(availableFilter);
|
||||
@ -48,11 +48,11 @@ export const SingleEntityFilterDropdownButton = ({
|
||||
dropdownOffset={{ x: 0, y: -28 }}
|
||||
clickableComponent={
|
||||
<StyledHeaderDropdownButton>
|
||||
{selectedFilters[0] ? (
|
||||
{selectedFilter ? (
|
||||
<GenericEntityFilterChip
|
||||
filter={selectedFilters[0]}
|
||||
filter={selectedFilter}
|
||||
Icon={
|
||||
selectedFilters[0].operand === ViewFilterOperand.IsNotNull
|
||||
selectedFilter.operand === ViewFilterOperand.IsNotNull
|
||||
? availableFilter.SelectAllIcon
|
||||
: undefined
|
||||
}
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
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 { Filter } from '../types/Filter';
|
||||
|
||||
import { useFilterStates } from './useFilterStates';
|
||||
|
||||
@ -14,8 +18,8 @@ export const useFilter = (props?: UseFilterProps) => {
|
||||
props?.filterScopeId,
|
||||
);
|
||||
const {
|
||||
availableFilters,
|
||||
setAvailableFilters,
|
||||
availableFilterDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
filterDefinitionUsedInDropdown,
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
filterDropdownSearchInput,
|
||||
@ -26,16 +30,28 @@ export const useFilter = (props?: UseFilterProps) => {
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
isFilterDropdownUnfolded,
|
||||
setIsFilterDropdownUnfolded,
|
||||
selectedFilters,
|
||||
setSelectedFilters,
|
||||
selectedFilter,
|
||||
setSelectedFilter,
|
||||
selectedOperandInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
} = useFilterStates(scopeId);
|
||||
|
||||
const { onFilterSelect } = useScopeInternalContextOrThrow(
|
||||
FilterScopeInternalContext,
|
||||
);
|
||||
|
||||
const selectFilter = useCallback(
|
||||
(filter: Filter) => {
|
||||
setSelectedFilter(filter);
|
||||
onFilterSelect?.(filter);
|
||||
},
|
||||
[setSelectedFilter, onFilterSelect],
|
||||
);
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
availableFilters,
|
||||
setAvailableFilters,
|
||||
availableFilterDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
filterDefinitionUsedInDropdown,
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
filterDropdownSearchInput,
|
||||
@ -46,9 +62,10 @@ export const useFilter = (props?: UseFilterProps) => {
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
isFilterDropdownUnfolded,
|
||||
setIsFilterDropdownUnfolded,
|
||||
selectedFilters,
|
||||
setSelectedFilters,
|
||||
selectedFilter,
|
||||
setSelectedFilter,
|
||||
selectedOperandInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
selectFilter,
|
||||
};
|
||||
};
|
||||
|
||||
@ -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]);
|
||||
};
|
||||
@ -1,19 +1,17 @@
|
||||
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 { filterDropdownSearchInputScopedState } from '../states/filterDropdownSearchInputScopedState';
|
||||
import { filterDropdownSelectedEntityIdScopedState } from '../states/filterDropdownSelectedEntityIdScopedState';
|
||||
import { isFilterDropdownOperandSelectUnfoldedScopedState } from '../states/isFilterDropdownOperandSelectUnfoldedScopedState';
|
||||
import { isFilterDropdownUnfoldedScopedState } from '../states/isFilterDropdownUnfoldedScopedState';
|
||||
import { selectedFiltersScopedState } from '../states/selectedFiltersScopedState';
|
||||
import { selectedFilterScopedState } from '../states/selectedFilterScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
||||
|
||||
export const useFilterStates = (scopeId: string) => {
|
||||
const [availableFilters, setAvailableFilters] = useRecoilScopedStateV2(
|
||||
availableFiltersScopedState,
|
||||
scopeId,
|
||||
);
|
||||
const [availableFilterDefinitions, setAvailableFilterDefinitions] =
|
||||
useRecoilScopedStateV2(availableFilterDefinitionsScopedState, scopeId);
|
||||
|
||||
const [filterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown] =
|
||||
useRecoilScopedStateV2(filterDefinitionUsedInDropdownScopedState, scopeId);
|
||||
@ -35,8 +33,8 @@ export const useFilterStates = (scopeId: string) => {
|
||||
const [isFilterDropdownUnfolded, setIsFilterDropdownUnfolded] =
|
||||
useRecoilScopedStateV2(isFilterDropdownUnfoldedScopedState, scopeId);
|
||||
|
||||
const [selectedFilters, setSelectedFilters] = useRecoilScopedStateV2(
|
||||
selectedFiltersScopedState,
|
||||
const [selectedFilter, setSelectedFilter] = useRecoilScopedStateV2(
|
||||
selectedFilterScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
@ -44,8 +42,8 @@ export const useFilterStates = (scopeId: string) => {
|
||||
useRecoilScopedStateV2(selectedOperandInDropdownScopedState, scopeId);
|
||||
|
||||
return {
|
||||
availableFilters,
|
||||
setAvailableFilters,
|
||||
availableFilterDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
filterDefinitionUsedInDropdown,
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
filterDropdownSearchInput,
|
||||
@ -56,8 +54,8 @@ export const useFilterStates = (scopeId: string) => {
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
isFilterDropdownUnfolded,
|
||||
setIsFilterDropdownUnfolded,
|
||||
selectedFilters,
|
||||
setSelectedFilters,
|
||||
selectedFilter,
|
||||
setSelectedFilter,
|
||||
selectedOperandInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
};
|
||||
|
||||
@ -2,25 +2,31 @@ import { ReactNode } from 'react';
|
||||
|
||||
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
|
||||
|
||||
import { Filter } from '../types/Filter';
|
||||
|
||||
import { FilterScopeInitEffect } from './init-effect/FilterScopeInitEffect';
|
||||
import { FilterScopeInternalContext } from './scope-internal-context/FilterScopeInternalContext';
|
||||
|
||||
type FilterScopeProps = {
|
||||
children: ReactNode;
|
||||
filterScopeId: string;
|
||||
availableFilters?: FilterDefinition[];
|
||||
availableFilterDefinitions?: FilterDefinition[];
|
||||
onFilterSelect?: (filter: Filter) => void;
|
||||
};
|
||||
|
||||
export const FilterScope = ({
|
||||
children,
|
||||
filterScopeId,
|
||||
availableFilters,
|
||||
availableFilterDefinitions,
|
||||
onFilterSelect,
|
||||
}: FilterScopeProps) => {
|
||||
return (
|
||||
<FilterScopeInternalContext.Provider value={{ scopeId: filterScopeId }}>
|
||||
<FilterScopeInternalContext.Provider
|
||||
value={{ scopeId: filterScopeId, onFilterSelect }}
|
||||
>
|
||||
<FilterScopeInitEffect
|
||||
filterScopeId={filterScopeId}
|
||||
availableFilters={availableFilters}
|
||||
availableFilterDefinitions={availableFilterDefinitions}
|
||||
/>
|
||||
{children}
|
||||
</FilterScopeInternalContext.Provider>
|
||||
|
||||
@ -6,20 +6,20 @@ import { useFilterStates } from '../../hooks/useFilterStates';
|
||||
|
||||
type FilterScopeInitEffectProps = {
|
||||
filterScopeId: string;
|
||||
availableFilters?: FilterDefinition[];
|
||||
availableFilterDefinitions?: FilterDefinition[];
|
||||
};
|
||||
|
||||
export const FilterScopeInitEffect = ({
|
||||
filterScopeId,
|
||||
availableFilters,
|
||||
availableFilterDefinitions,
|
||||
}: FilterScopeInitEffectProps) => {
|
||||
const { setAvailableFilters } = useFilterStates(filterScopeId);
|
||||
const { setAvailableFilterDefinitions } = useFilterStates(filterScopeId);
|
||||
|
||||
useEffect(() => {
|
||||
if (availableFilters) {
|
||||
setAvailableFilters(availableFilters);
|
||||
if (availableFilterDefinitions) {
|
||||
setAvailableFilterDefinitions(availableFilterDefinitions);
|
||||
}
|
||||
}, [availableFilters, setAvailableFilters]);
|
||||
}, [availableFilterDefinitions, setAvailableFilterDefinitions]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey';
|
||||
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
|
||||
|
||||
import { Filter } from '../../types/Filter';
|
||||
|
||||
type FilterScopeInternalContextProps = ScopedStateKey & {
|
||||
test?: string;
|
||||
onFilterSelect?: (sort: Filter) => void;
|
||||
};
|
||||
|
||||
export const FilterScopeInternalContext =
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const availableFiltersScopedState = createScopedState<
|
||||
export const availableFilterDefinitionsScopedState = createScopedState<
|
||||
FilterDefinition[]
|
||||
>({
|
||||
key: 'availableFiltersScopedState',
|
||||
key: 'availableFilterDefinitionsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -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,
|
||||
});
|
||||
@ -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: [],
|
||||
});
|
||||
@ -1,12 +1,12 @@
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
|
||||
import { FilterType } from './FilterType';
|
||||
import { FilterDefinition } from './FilterDefinition';
|
||||
|
||||
export type Filter = {
|
||||
key: string;
|
||||
type: FilterType;
|
||||
fieldId: string;
|
||||
value: string;
|
||||
displayValue: string;
|
||||
displayAvatarUrl?: string;
|
||||
operand: ViewFilterOperand;
|
||||
definition: FilterDefinition;
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@ import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { FilterType } from './FilterType';
|
||||
|
||||
export type FilterDefinition = {
|
||||
key: string;
|
||||
fieldId: string;
|
||||
label: string;
|
||||
Icon: IconComponent;
|
||||
type: FilterType;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { FilterDefinition } from './FilterDefinition';
|
||||
|
||||
export type FilterDefinitionByEntity<T> = FilterDefinition & {
|
||||
key: keyof T;
|
||||
fieldId: keyof T;
|
||||
};
|
||||
|
||||
@ -3,28 +3,39 @@ import { QueryMode } from '~/generated/graphql';
|
||||
|
||||
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) {
|
||||
case ViewFilterOperand.IsNotNull:
|
||||
return {
|
||||
[filter.key]: {
|
||||
[filter.fieldId]: {
|
||||
not: null,
|
||||
},
|
||||
};
|
||||
default:
|
||||
switch (filter.type) {
|
||||
switch (filter.definition.type) {
|
||||
case 'text':
|
||||
switch (filter.operand) {
|
||||
case ViewFilterOperand.Contains:
|
||||
return {
|
||||
[filter.key]: {
|
||||
[filter.fieldId]: {
|
||||
contains: filter.value,
|
||||
mode: QueryMode.Insensitive,
|
||||
},
|
||||
};
|
||||
case ViewFilterOperand.DoesNotContain:
|
||||
return {
|
||||
[filter.key]: {
|
||||
[filter.fieldId]: {
|
||||
not: {
|
||||
contains: filter.value,
|
||||
mode: QueryMode.Insensitive,
|
||||
@ -33,64 +44,64 @@ export const turnFilterIntoWhereClause = (filter: Filter) => {
|
||||
};
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${filter.operand} for ${filter.type} filter`,
|
||||
`Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
|
||||
);
|
||||
}
|
||||
case 'number':
|
||||
switch (filter.operand) {
|
||||
case ViewFilterOperand.GreaterThan:
|
||||
return {
|
||||
[filter.key]: {
|
||||
[filter.fieldId]: {
|
||||
gte: parseFloat(filter.value),
|
||||
},
|
||||
};
|
||||
case ViewFilterOperand.LessThan:
|
||||
return {
|
||||
[filter.key]: {
|
||||
[filter.fieldId]: {
|
||||
lte: parseFloat(filter.value),
|
||||
},
|
||||
};
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${filter.operand} for ${filter.type} filter`,
|
||||
`Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
|
||||
);
|
||||
}
|
||||
case 'date':
|
||||
switch (filter.operand) {
|
||||
case ViewFilterOperand.GreaterThan:
|
||||
return {
|
||||
[filter.key]: {
|
||||
[filter.fieldId]: {
|
||||
gte: filter.value,
|
||||
},
|
||||
};
|
||||
case ViewFilterOperand.LessThan:
|
||||
return {
|
||||
[filter.key]: {
|
||||
[filter.fieldId]: {
|
||||
lte: filter.value,
|
||||
},
|
||||
};
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${filter.operand} for ${filter.type} filter`,
|
||||
`Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
|
||||
);
|
||||
}
|
||||
case 'entity':
|
||||
switch (filter.operand) {
|
||||
case ViewFilterOperand.Is:
|
||||
return {
|
||||
[filter.key]: {
|
||||
[filter.fieldId]: {
|
||||
equals: filter.value,
|
||||
},
|
||||
};
|
||||
case ViewFilterOperand.IsNot:
|
||||
return {
|
||||
[filter.key]: {
|
||||
[filter.fieldId]: {
|
||||
not: { equals: filter.value },
|
||||
},
|
||||
};
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${filter.operand} for ${filter.type} filter`,
|
||||
`Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
|
||||
);
|
||||
}
|
||||
default:
|
||||
|
||||
@ -35,7 +35,7 @@ export const SortDropdownButton = ({
|
||||
setSelectedSortDirection('asc');
|
||||
}, []);
|
||||
|
||||
const { availableSorts, onSortAdd, isSortSelected } = useSort();
|
||||
const { availableSortDefinitions, onSortSelect, isSortSelected } = useSort();
|
||||
|
||||
const { toggleDropdown } = useDropdown({
|
||||
dropdownScopeId: SortDropdownId,
|
||||
@ -48,8 +48,8 @@ export const SortDropdownButton = ({
|
||||
|
||||
const handleAddSort = (selectedSortDefinition: SortDefinition) => {
|
||||
toggleDropdown();
|
||||
onSortAdd?.({
|
||||
key: selectedSortDefinition.key,
|
||||
onSortSelect?.({
|
||||
fieldId: selectedSortDefinition.fieldId,
|
||||
direction: selectedSortDirection,
|
||||
definition: selectedSortDefinition,
|
||||
});
|
||||
@ -96,7 +96,7 @@ export const SortDropdownButton = ({
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
{availableSorts.map((availableSort, index) => (
|
||||
{availableSortDefinitions.map((availableSort, index) => (
|
||||
<MenuItem
|
||||
testId={`select-sort-${index}`}
|
||||
key={index}
|
||||
|
||||
@ -15,22 +15,22 @@ export const useSort = (props?: UseSortProps) => {
|
||||
props?.sortScopeId,
|
||||
);
|
||||
const {
|
||||
availableSorts,
|
||||
setAvailableSorts,
|
||||
availableSortDefinitions,
|
||||
setAvailableSortDefinitions,
|
||||
isSortSelected,
|
||||
setIsSortSelected,
|
||||
} = useSortStates(scopeId);
|
||||
|
||||
const { onSortAdd } = useScopeInternalContextOrThrow(
|
||||
const { onSortSelect } = useScopeInternalContextOrThrow(
|
||||
SortScopeInternalContext,
|
||||
);
|
||||
|
||||
return {
|
||||
onSortAdd,
|
||||
onSortSelect,
|
||||
scopeId,
|
||||
availableSorts,
|
||||
availableSortDefinitions,
|
||||
isSortSelected,
|
||||
setIsSortSelected,
|
||||
setAvailableSorts,
|
||||
setAvailableSortDefinitions,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
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';
|
||||
|
||||
export const useSortStates = (scopeId: string) => {
|
||||
const [availableSorts, setAvailableSorts] = useRecoilScopedStateV2(
|
||||
availableSortsScopedState,
|
||||
scopeId,
|
||||
);
|
||||
const [availableSortDefinitions, setAvailableSortDefinitions] =
|
||||
useRecoilScopedStateV2(availableSortDefinitionsScopedState, scopeId);
|
||||
|
||||
const [isSortSelected, setIsSortSelected] = useRecoilScopedStateV2(
|
||||
isSortSelectedScopedState,
|
||||
@ -15,8 +13,8 @@ export const useSortStates = (scopeId: string) => {
|
||||
);
|
||||
|
||||
return {
|
||||
availableSorts,
|
||||
setAvailableSorts,
|
||||
availableSortDefinitions,
|
||||
setAvailableSortDefinitions,
|
||||
isSortSelected,
|
||||
setIsSortSelected,
|
||||
};
|
||||
|
||||
@ -9,23 +9,23 @@ import { SortScopeInternalContext } from './scope-internal-context/SortScopeInte
|
||||
type SortScopeProps = {
|
||||
children: ReactNode;
|
||||
sortScopeId: string;
|
||||
availableSorts?: SortDefinition[];
|
||||
onSortAdd?: (sort: Sort) => void | Promise<void>;
|
||||
availableSortDefinitions?: SortDefinition[];
|
||||
onSortSelect?: (sort: Sort) => void | Promise<void>;
|
||||
};
|
||||
|
||||
export const SortScope = ({
|
||||
children,
|
||||
sortScopeId,
|
||||
availableSorts,
|
||||
onSortAdd,
|
||||
availableSortDefinitions,
|
||||
onSortSelect,
|
||||
}: SortScopeProps) => {
|
||||
return (
|
||||
<SortScopeInternalContext.Provider
|
||||
value={{ scopeId: sortScopeId, onSortAdd }}
|
||||
value={{ scopeId: sortScopeId, onSortSelect }}
|
||||
>
|
||||
<SortScopeInitEffect
|
||||
sortScopeId={sortScopeId}
|
||||
availableSorts={availableSorts}
|
||||
availableSortDefinitions={availableSortDefinitions}
|
||||
/>
|
||||
{children}
|
||||
</SortScopeInternalContext.Provider>
|
||||
|
||||
@ -6,20 +6,20 @@ import { useSortStates } from '../../hooks/useSortStates';
|
||||
|
||||
type SortScopeInitEffectProps = {
|
||||
sortScopeId: string;
|
||||
availableSorts?: SortDefinition[];
|
||||
availableSortDefinitions?: SortDefinition[];
|
||||
};
|
||||
|
||||
export const SortScopeInitEffect = ({
|
||||
sortScopeId,
|
||||
availableSorts,
|
||||
availableSortDefinitions,
|
||||
}: SortScopeInitEffectProps) => {
|
||||
const { setAvailableSorts } = useSortStates(sortScopeId);
|
||||
const { setAvailableSortDefinitions } = useSortStates(sortScopeId);
|
||||
|
||||
useEffect(() => {
|
||||
if (availableSorts) {
|
||||
setAvailableSorts(availableSorts);
|
||||
if (availableSortDefinitions) {
|
||||
setAvailableSortDefinitions(availableSortDefinitions);
|
||||
}
|
||||
}, [availableSorts, setAvailableSorts]);
|
||||
}, [availableSortDefinitions, setAvailableSortDefinitions]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
@ -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 { Sort } from '../../types/Sort';
|
||||
import { SortDefinition } from '../../types/SortDefinition';
|
||||
|
||||
type SortScopeInternalContextProps = ScopedStateKey & {
|
||||
onSortAdd?: (sort: Sort) => void;
|
||||
availableSorts?: SortDefinition[];
|
||||
onSortSelect?: (sort: Sort) => void;
|
||||
};
|
||||
|
||||
export const SortScopeInternalContext =
|
||||
|
||||
@ -2,7 +2,7 @@ import { SortDefinition } from './SortDefinition';
|
||||
import { SortDirection } from './SortDirection';
|
||||
|
||||
export type Sort = {
|
||||
key: string;
|
||||
fieldId: string;
|
||||
direction: SortDirection;
|
||||
definition: SortDefinition;
|
||||
};
|
||||
|
||||
@ -3,7 +3,7 @@ import { IconComponent } from '@/ui/display/icon/types/IconComponent';
|
||||
import { SortDirection } from './SortDirection';
|
||||
|
||||
export type SortDefinition = {
|
||||
key: string;
|
||||
fieldId: string;
|
||||
label: string;
|
||||
Icon?: IconComponent;
|
||||
getOrderByTemplate?: (direction: SortDirection) => any[];
|
||||
|
||||
@ -10,7 +10,7 @@ export const reduceSortsToOrderBy = (sorts: Sort[]): any[] =>
|
||||
if (sort.definition.getOrderByTemplate) {
|
||||
return sort.definition.getOrderByTemplate(direction);
|
||||
} else {
|
||||
return [{ [sort.definition.key]: direction }];
|
||||
return [{ [sort.definition.fieldId]: direction }];
|
||||
}
|
||||
})
|
||||
.flat();
|
||||
|
||||
@ -2,8 +2,9 @@ import { useView } from '@/views/hooks/useView';
|
||||
|
||||
import { Dropdown } from '../../dropdown/components/Dropdown';
|
||||
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 {
|
||||
BoardOptionsDropdownContent,
|
||||
@ -12,26 +13,22 @@ import {
|
||||
|
||||
type BoardOptionsDropdownProps = Pick<
|
||||
BoardOptionsDropdownContentProps,
|
||||
'customHotkeyScope' | 'onStageAdd'
|
||||
'onStageAdd'
|
||||
>;
|
||||
|
||||
export const BoardOptionsDropdown = ({
|
||||
customHotkeyScope,
|
||||
onStageAdd,
|
||||
}: BoardOptionsDropdownProps) => {
|
||||
const { setViewEditMode } = useView();
|
||||
|
||||
return (
|
||||
<DropdownScope dropdownScopeId={BoardScopeIds.OptionsDropdown}>
|
||||
<DropdownScope dropdownScopeId={BoardOptionsDropdownId}>
|
||||
<Dropdown
|
||||
clickableComponent={<BoardOptionsDropdownButton />}
|
||||
dropdownComponents={
|
||||
<BoardOptionsDropdownContent
|
||||
customHotkeyScope={customHotkeyScope}
|
||||
onStageAdd={onStageAdd}
|
||||
/>
|
||||
<BoardOptionsDropdownContent onStageAdd={onStageAdd} />
|
||||
}
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
dropdownHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||
onClickOutside={() => setViewEditMode('none')}
|
||||
dropdownMenuWidth={170}
|
||||
/>
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
|
||||
import { BoardScopeIds } from '../types/enums/BoardScopeIds';
|
||||
|
||||
export const BoardOptionsDropdownButton = () => {
|
||||
const { isDropdownOpen, toggleDropdown } = useDropdown({
|
||||
dropdownScopeId: BoardScopeIds.OptionsDropdown,
|
||||
});
|
||||
const { isDropdownOpen, toggleDropdown } = useDropdown();
|
||||
|
||||
const handleClick = () => {
|
||||
toggleDropdown();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
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 { ThemeColor } from '@/ui/theme/constants/colors';
|
||||
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 { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { useViewInternalStates } from '@/views/hooks/useViewInternalStates';
|
||||
import { viewEditModeScopedState } from '@/views/states/viewEditModeScopedState';
|
||||
import { useViewGetStates } from '@/views/hooks/useViewGetStates';
|
||||
|
||||
import { useBoardCardFields } from '../hooks/useBoardCardFields';
|
||||
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
||||
import { boardColumnsState } from '../states/boardColumnsState';
|
||||
import { isCompactViewEnabledState } from '../states/isCompactViewEnabledState';
|
||||
import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsFamilyState';
|
||||
import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector';
|
||||
import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector';
|
||||
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||
import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope';
|
||||
|
||||
export type BoardOptionsDropdownContentProps = {
|
||||
customHotkeyScope: HotkeyScope;
|
||||
onStageAdd?: (boardColumn: BoardColumnDefinition) => void;
|
||||
};
|
||||
|
||||
@ -54,15 +49,12 @@ type ColumnForCreate = {
|
||||
};
|
||||
|
||||
export const BoardOptionsDropdownContent = ({
|
||||
customHotkeyScope,
|
||||
onStageAdd,
|
||||
}: BoardOptionsDropdownContentProps) => {
|
||||
const { setViewEditMode, createView, currentViewId } = useView();
|
||||
const { viewEditMode, currentView } = useViewInternalStates();
|
||||
const { setViewEditMode, handleViewNameSubmit } = useView();
|
||||
const { viewEditMode, currentView } = useViewGetStates();
|
||||
const { BoardRecoilScopeContext } = useContext(BoardContext);
|
||||
|
||||
const boardRecoilScopeId = useRecoilScopeId(BoardRecoilScopeContext);
|
||||
|
||||
const stageInputRef = useRef<HTMLInputElement>(null);
|
||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
@ -104,31 +96,6 @@ export const BoardOptionsDropdownContent = ({
|
||||
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 handleMenuNavigate = (menu: BoardOptionsMenu) => {
|
||||
@ -146,43 +113,39 @@ export const BoardOptionsDropdownContent = ({
|
||||
setViewEditMode('none');
|
||||
closeDropdown();
|
||||
},
|
||||
customHotkeyScope.scope,
|
||||
BoardOptionsHotkeyScope.Dropdown,
|
||||
);
|
||||
|
||||
useScopedHotkeys(
|
||||
Key.Enter,
|
||||
() => {
|
||||
const name = viewEditInputRef.current?.value;
|
||||
resetMenu();
|
||||
setViewEditMode('none');
|
||||
closeDropdown();
|
||||
handleStageSubmit();
|
||||
handleViewNameSubmit();
|
||||
handleViewNameSubmit(name);
|
||||
closeDropdown();
|
||||
},
|
||||
customHotkeyScope.scope,
|
||||
BoardOptionsHotkeyScope.Dropdown,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!currentMenu && (
|
||||
<>
|
||||
{viewEditMode && (
|
||||
<DropdownMenuInput
|
||||
ref={viewEditInputRef}
|
||||
autoFocus={viewEditMode !== 'none'}
|
||||
placeholder={
|
||||
viewEditMode === 'create'
|
||||
? 'New view'
|
||||
: viewEditMode === 'edit'
|
||||
? 'View name'
|
||||
: ''
|
||||
}
|
||||
defaultValue={
|
||||
viewEditMode === 'create'
|
||||
? ''
|
||||
: viewEditMode === 'edit'
|
||||
? currentView?.name
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<DropdownMenuInput
|
||||
ref={viewEditInputRef}
|
||||
autoFocus={viewEditMode !== 'none'}
|
||||
placeholder={
|
||||
viewEditMode === 'create'
|
||||
? 'New view'
|
||||
: viewEditMode === 'edit'
|
||||
? 'View name'
|
||||
: ''
|
||||
}
|
||||
defaultValue={viewEditMode === 'create' ? '' : currentView?.name}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItemNavigate
|
||||
|
||||
@ -40,6 +40,8 @@ export type EntityBoardProps = {
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
|
||||
@ -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';
|
||||
@ -7,6 +7,6 @@ import { PipelineProgress } from '~/generated/graphql';
|
||||
export type BoardOptions = {
|
||||
newCardComponent: React.ReactNode;
|
||||
CardComponent: ComponentType;
|
||||
filters: FilterDefinitionByEntity<PipelineProgress>[];
|
||||
sorts: SortDefinition[];
|
||||
filterDefinitions: FilterDefinitionByEntity<PipelineProgress>[];
|
||||
sortDefinitions: SortDefinition[];
|
||||
};
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
export enum BoardScopeIds {
|
||||
OptionsDropdown = 'board-options',
|
||||
}
|
||||
@ -10,7 +10,7 @@ import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
|
||||
import { useViewInternalStates } from '../hooks/useViewInternalStates';
|
||||
import { useViewGetStates } from '../hooks/useViewGetStates';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: inline-flex;
|
||||
@ -28,7 +28,7 @@ export const UpdateViewButtonGroup = ({
|
||||
}: UpdateViewButtonGroupProps) => {
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const { updateCurrentView, setViewEditMode } = useView();
|
||||
const { canPersistFilters, canPersistSorts } = useViewInternalStates();
|
||||
const { canPersistFilters, canPersistSorts } = useViewGetStates();
|
||||
|
||||
const canPersistView = canPersistFilters || canPersistSorts;
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { TopBar } from '@/ui/layout/top-bar/TopBar';
|
||||
|
||||
import { useView } from '../hooks/useView';
|
||||
import { useViewInternalStates } from '../hooks/useViewInternalStates';
|
||||
import { useViewGetStates } from '../hooks/useViewGetStates';
|
||||
import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
|
||||
|
||||
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
|
||||
@ -31,18 +31,20 @@ export const ViewBar = ({
|
||||
const { openDropdown: openOptionsDropdownButton } = useDropdown({
|
||||
dropdownScopeId: optionsDropdownScopeId,
|
||||
});
|
||||
const { upsertViewSort } = useView();
|
||||
const { availableFilters, availableSorts } = useViewInternalStates();
|
||||
const { upsertViewSort, upsertViewFilter } = useView();
|
||||
const { availableFilterDefinitions, availableSortDefinitions } =
|
||||
useViewGetStates();
|
||||
|
||||
return (
|
||||
<FilterScope
|
||||
filterScopeId="view-filter"
|
||||
availableFilters={availableFilters}
|
||||
availableFilterDefinitions={availableFilterDefinitions}
|
||||
onFilterSelect={upsertViewFilter}
|
||||
>
|
||||
<SortScope
|
||||
sortScopeId="view-sort"
|
||||
availableSorts={availableSorts}
|
||||
onSortAdd={upsertViewSort}
|
||||
availableSortDefinitions={availableSortDefinitions}
|
||||
onSortSelect={upsertViewSort}
|
||||
>
|
||||
<ViewBarEffect />
|
||||
<TopBar
|
||||
@ -51,6 +53,7 @@ export const ViewBar = ({
|
||||
<ViewsDropdownButton
|
||||
onViewEditModeChange={openOptionsDropdownButton}
|
||||
hotkeyScope={{ scope: ViewsHotkeyScope.ListDropdown }}
|
||||
optionsDropdownScopeId={optionsDropdownScopeId}
|
||||
/>
|
||||
}
|
||||
displayBottomBorder={false}
|
||||
|
||||
@ -5,9 +5,8 @@ import { AddFilterFromDropdownButton } from '@/ui/data/filter/components/AddFilt
|
||||
import { getOperandLabelShort } from '@/ui/data/filter/utils/getOperandLabel';
|
||||
import { IconArrowDown, IconArrowUp } from '@/ui/display/icon/index';
|
||||
|
||||
import { useRemoveFilter } from '../hooks/useRemoveFilter';
|
||||
import { useView } from '../hooks/useView';
|
||||
import { useViewInternalStates } from '../hooks/useViewInternalStates';
|
||||
import { useViewGetStates } from '../hooks/useViewGetStates';
|
||||
|
||||
import SortOrFilterChip from './SortOrFilterChip';
|
||||
|
||||
@ -90,43 +89,23 @@ export const ViewBarDetails = ({
|
||||
}: ViewBarDetailsProps) => {
|
||||
const {
|
||||
currentViewSorts,
|
||||
setCurrentViewSorts,
|
||||
availableFilters,
|
||||
currentViewFilters,
|
||||
canPersistFilters,
|
||||
canPersistSorts,
|
||||
isViewBarExpanded,
|
||||
} = useViewInternalStates();
|
||||
} = useViewGetStates();
|
||||
|
||||
const { resetViewBar } = useView();
|
||||
const { resetViewBar, removeViewSort, removeViewFilter } = useView();
|
||||
|
||||
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 = () => {
|
||||
resetViewBar();
|
||||
};
|
||||
|
||||
const handleSortRemove = (sortKey: string) =>
|
||||
setCurrentViewSorts?.((previousSorts) =>
|
||||
previousSorts.filter((sort) => sort.key !== sortKey),
|
||||
);
|
||||
|
||||
const shouldExpandViewBar =
|
||||
canPersistView ||
|
||||
((filtersWithDefinition?.length || currentViewSorts?.length) &&
|
||||
((currentViewSorts?.length || currentViewFilters?.length) &&
|
||||
isViewBarExpanded);
|
||||
|
||||
if (!shouldExpandViewBar) {
|
||||
@ -140,32 +119,32 @@ export const ViewBarDetails = ({
|
||||
{currentViewSorts?.map((sort) => {
|
||||
return (
|
||||
<SortOrFilterChip
|
||||
key={sort.key}
|
||||
testId={sort.key}
|
||||
key={sort.fieldId}
|
||||
testId={sort.fieldId}
|
||||
labelValue={sort.definition.label}
|
||||
Icon={sort.direction === 'desc' ? IconArrowDown : IconArrowUp}
|
||||
isSort
|
||||
onRemove={() => handleSortRemove(sort.key)}
|
||||
onRemove={() => removeViewSort(sort.fieldId)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{!!currentViewSorts?.length && !!filtersWithDefinition?.length && (
|
||||
{!!currentViewSorts?.length && !!currentViewFilters?.length && (
|
||||
<StyledSeperatorContainer>
|
||||
<StyledSeperator />
|
||||
</StyledSeperatorContainer>
|
||||
)}
|
||||
{filtersWithDefinition?.map((filter) => {
|
||||
{currentViewFilters?.map((filter) => {
|
||||
return (
|
||||
<SortOrFilterChip
|
||||
key={filter.key}
|
||||
testId={filter.key}
|
||||
labelKey={filter.label}
|
||||
key={filter.fieldId}
|
||||
testId={filter.fieldId}
|
||||
labelKey={filter.definition.label}
|
||||
labelValue={`${getOperandLabelShort(filter.operand)} ${
|
||||
filter.displayValue
|
||||
}`}
|
||||
Icon={filter.Icon}
|
||||
Icon={filter.definition.Icon}
|
||||
onRemove={() => {
|
||||
removeFilter(filter.key);
|
||||
removeViewFilter(filter.fieldId);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -8,28 +8,65 @@ import { assertNotNull } from '~/utils/assert';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
import { useView } from '../hooks/useView';
|
||||
import { useViewInternalStates } from '../hooks/useViewInternalStates';
|
||||
import { availableFieldsScopedState } from '../states/availableFieldsScopedState';
|
||||
import { useViewGetStates } from '../hooks/useViewGetStates';
|
||||
import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState';
|
||||
import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState';
|
||||
import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState';
|
||||
import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState';
|
||||
import { onViewFiltersChangeScopedState } from '../states/onViewFiltersChangeScopedState';
|
||||
import { onViewSortsChangeScopedState } from '../states/onViewSortsChangeScopedState';
|
||||
import { savedViewFieldsScopedFamilyState } from '../states/savedViewFieldsScopedFamilyState';
|
||||
import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState';
|
||||
import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState';
|
||||
import { viewsScopedState } from '../states/viewsScopedState';
|
||||
import { View } from '../types/View';
|
||||
import { ViewField } from '../types/ViewField';
|
||||
import { ViewFilter } from '../types/ViewFilter';
|
||||
import { ViewSort } from '../types/ViewSort';
|
||||
|
||||
export const ViewBarEffect = () => {
|
||||
const {
|
||||
scopeId: viewScopeId,
|
||||
setCurrentViewFields,
|
||||
setSavedViewFields,
|
||||
setCurrentViewFilters,
|
||||
setSavedViewFilters,
|
||||
setCurrentViewSorts,
|
||||
setSavedViewSorts,
|
||||
currentViewId,
|
||||
setViews,
|
||||
changeView,
|
||||
loadView,
|
||||
changeViewInUrl,
|
||||
setCurrentViewId,
|
||||
} = useView();
|
||||
|
||||
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({
|
||||
objectNamePlural: 'viewFieldsV2',
|
||||
@ -38,7 +75,9 @@ export const ViewBarEffect = () => {
|
||||
({ snapshot }) =>
|
||||
async (data: PaginatedObjectTypeResults<ViewField>) => {
|
||||
const availableFields = snapshot
|
||||
.getLoadable(availableFieldsScopedState({ scopeId: viewScopeId }))
|
||||
.getLoadable(
|
||||
availableFieldDefinitionsScopedState({ scopeId: viewScopeId }),
|
||||
)
|
||||
.getValue();
|
||||
|
||||
const onViewFieldsChange = snapshot
|
||||
@ -74,133 +113,122 @@ export const ViewBarEffect = () => {
|
||||
});
|
||||
|
||||
useFindManyObjects({
|
||||
objectNamePlural: 'viewsV2',
|
||||
filter: { type: { eq: viewType }, objectId: { eq: viewObjectId } },
|
||||
objectNamePlural: 'viewFiltersV2',
|
||||
filter: { viewId: { eq: currentViewId } },
|
||||
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 }))
|
||||
async (data: PaginatedObjectTypeResults<Required<ViewFilter>>) => {
|
||||
const availableFilterDefinitions = snapshot
|
||||
.getLoadable(
|
||||
availableFilterDefinitionsScopedState({ scopeId: viewScopeId }),
|
||||
)
|
||||
.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({
|
||||
// skip: !currentViewId,
|
||||
// variables: {
|
||||
// where: {
|
||||
// viewId: { equals: currentViewId },
|
||||
// },
|
||||
// },
|
||||
// onCompleted: useRecoilCallback(({ snapshot }) => async (data) => {
|
||||
// const availableSorts = snapshot
|
||||
// .getLoadable(availableSortsScopedState({ scopeId: viewScopeId }))
|
||||
// .getValue();
|
||||
useFindManyObjects({
|
||||
objectNamePlural: 'viewSortsV2',
|
||||
filter: { viewId: { eq: currentViewId } },
|
||||
onCompleted: useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (data: PaginatedObjectTypeResults<Required<ViewSort>>) => {
|
||||
const availableSortDefinitions = snapshot
|
||||
.getLoadable(
|
||||
availableSortDefinitionsScopedState({ scopeId: viewScopeId }),
|
||||
)
|
||||
.getValue();
|
||||
|
||||
// if (!availableSorts || !currentViewId) {
|
||||
// return;
|
||||
// }
|
||||
if (!availableSortDefinitions || !currentViewId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// const savedViewSorts = snapshot
|
||||
// .getLoadable(
|
||||
// savedViewSortsScopedFamilyState({
|
||||
// scopeId: viewScopeId,
|
||||
// familyKey: currentViewId,
|
||||
// }),
|
||||
// )
|
||||
// .getValue();
|
||||
const savedViewSorts = snapshot
|
||||
.getLoadable(
|
||||
savedViewSortsScopedFamilyState({
|
||||
scopeId: viewScopeId,
|
||||
familyKey: currentViewId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
|
||||
// const queriedViewSorts = data.viewSorts
|
||||
// .map((viewSort) => {
|
||||
// const foundCorrespondingSortDefinition = availableSorts.find(
|
||||
// (sort) => sort.key === viewSort.key,
|
||||
// );
|
||||
const onViewSortsChange = snapshot
|
||||
.getLoadable(onViewSortsChangeScopedState({ scopeId: viewScopeId }))
|
||||
.getValue();
|
||||
|
||||
// if (foundCorrespondingSortDefinition) {
|
||||
// return {
|
||||
// key: viewSort.key,
|
||||
// definition: foundCorrespondingSortDefinition,
|
||||
// direction: viewSort.direction.toLowerCase(),
|
||||
// } as Sort;
|
||||
// } else {
|
||||
// return undefined;
|
||||
// }
|
||||
// })
|
||||
// .filter((sort): sort is Sort => !!sort);
|
||||
const queriedViewSorts = data.edges
|
||||
.map(({ node }) => {
|
||||
const availableSortDefinition = availableSortDefinitions.find(
|
||||
(sort) => sort.fieldId === node.fieldId,
|
||||
);
|
||||
|
||||
// if (!isDeeplyEqual(savedViewSorts, queriedViewSorts)) {
|
||||
// setSavedViewSorts?.(queriedViewSorts);
|
||||
// setCurrentViewSorts?.(queriedViewSorts);
|
||||
// }
|
||||
// }),
|
||||
// });
|
||||
if (!availableSortDefinition) return null;
|
||||
|
||||
// useGetViewFiltersQuery({
|
||||
// skip: !currentViewId,
|
||||
// variables: {
|
||||
// where: {
|
||||
// viewId: { equals: currentViewId },
|
||||
// },
|
||||
// },
|
||||
// onCompleted: useRecoilCallback(({ snapshot }) => (data) => {
|
||||
// const availableFilters = snapshot
|
||||
// .getLoadable(availableFiltersScopedState({ scopeId: viewScopeId }))
|
||||
// .getValue();
|
||||
return {
|
||||
id: node.id,
|
||||
fieldId: node.fieldId,
|
||||
direction: node.direction,
|
||||
definition: availableSortDefinition,
|
||||
};
|
||||
})
|
||||
.filter(assertNotNull);
|
||||
|
||||
// if (!availableFilters || !currentViewId) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 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);
|
||||
// }
|
||||
// }),
|
||||
// });
|
||||
if (!isDeeplyEqual(savedViewSorts, queriedViewSorts)) {
|
||||
setSavedViewSorts?.(queriedViewSorts);
|
||||
setCurrentViewSorts?.(queriedViewSorts);
|
||||
onViewSortsChange?.(queriedViewSorts);
|
||||
}
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
const currentViewIdFromUrl = searchParams.get('view');
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentViewIdFromUrl) return;
|
||||
setCurrentViewId(currentViewIdFromUrl);
|
||||
}, [currentViewIdFromUrl, setCurrentViewId]);
|
||||
loadView(currentViewIdFromUrl);
|
||||
}, [currentViewIdFromUrl, loadView, setCurrentViewId]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
@ -3,7 +3,6 @@ import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { TableOptionsDropdownId } from '@/ui/data/data-table/constants/TableOptionsDropdownId';
|
||||
import {
|
||||
IconChevronDown,
|
||||
IconList,
|
||||
@ -24,7 +23,7 @@ import { assertNotNull } from '~/utils/assert';
|
||||
|
||||
import { ViewsDropdownId } from '../constants/ViewsDropdownId';
|
||||
import { useView } from '../hooks/useView';
|
||||
import { useViewInternalStates } from '../hooks/useViewInternalStates';
|
||||
import { useViewGetStates } from '../hooks/useViewGetStates';
|
||||
|
||||
const StyledBoldDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)`
|
||||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||||
@ -60,17 +59,22 @@ const StyledViewName = styled.span`
|
||||
export type ViewsDropdownButtonProps = {
|
||||
hotkeyScope: HotkeyScope;
|
||||
onViewEditModeChange?: () => void;
|
||||
optionsDropdownScopeId: string;
|
||||
};
|
||||
|
||||
export const ViewsDropdownButton = ({
|
||||
hotkeyScope,
|
||||
onViewEditModeChange,
|
||||
optionsDropdownScopeId,
|
||||
}: ViewsDropdownButtonProps) => {
|
||||
const theme = useTheme();
|
||||
const { scopeId, removeView, currentViewId, changeView } = useView();
|
||||
const { scopeId, removeView, currentViewId, changeViewInUrl } = useView();
|
||||
|
||||
const { views, currentView, setViewEditMode, entityCountInCurrentView } =
|
||||
useViewInternalStates(scopeId, currentViewId);
|
||||
const { views, currentView, entityCountInCurrentView } = useViewGetStates(
|
||||
scopeId,
|
||||
currentViewId,
|
||||
);
|
||||
const { setViewEditMode } = useView();
|
||||
|
||||
const {
|
||||
isDropdownOpen: isViewsDropdownOpen,
|
||||
@ -80,16 +84,16 @@ export const ViewsDropdownButton = ({
|
||||
});
|
||||
|
||||
const { openDropdown: openOptionsDropdown } = useDropdown({
|
||||
dropdownScopeId: TableOptionsDropdownId,
|
||||
dropdownScopeId: optionsDropdownScopeId,
|
||||
});
|
||||
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
() => async (viewId: string) => {
|
||||
changeView(viewId);
|
||||
changeViewInUrl(viewId);
|
||||
|
||||
closeViewsDropdown();
|
||||
},
|
||||
[changeView, closeViewsDropdown],
|
||||
[changeViewInUrl, closeViewsDropdown],
|
||||
);
|
||||
|
||||
const handleAddViewButtonClick = () => {
|
||||
@ -104,7 +108,7 @@ export const ViewsDropdownButton = ({
|
||||
viewId: string,
|
||||
) => {
|
||||
event.stopPropagation();
|
||||
changeView(viewId);
|
||||
changeViewInUrl(viewId);
|
||||
setViewEditMode('edit');
|
||||
onViewEditModeChange?.();
|
||||
closeViewsDropdown();
|
||||
|
||||
@ -8,9 +8,10 @@ import { viewObjectIdScopeState } from '@/views/states/viewObjectIdScopeState';
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
|
||||
export const useViewFields = (viewScopeId: string) => {
|
||||
const { updateOneMutation, createOneMutation } = useFindOneMetadataObject({
|
||||
objectNameSingular: 'viewFieldV2',
|
||||
});
|
||||
const { updateOneMutation, createOneMutation, findManyQuery } =
|
||||
useFindOneMetadataObject({
|
||||
objectNameSingular: 'viewFieldV2',
|
||||
});
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const persistViewFields = useRecoilCallback(
|
||||
@ -49,12 +50,13 @@ export const useViewFields = (viewScopeId: string) => {
|
||||
variables: {
|
||||
input: {
|
||||
fieldId: viewField.fieldId,
|
||||
viewId: currentViewId,
|
||||
viewId: viewId,
|
||||
isVisible: viewField.isVisible,
|
||||
size: viewField.size,
|
||||
position: viewField.position,
|
||||
},
|
||||
},
|
||||
refetchQueries: [findManyQuery],
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
@ -1,14 +1,26 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { produce } from 'immer';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useFindOneMetadataObject } from '@/metadata/hooks/useFindOneMetadataObject';
|
||||
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 { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
|
||||
import { onViewFiltersChangeScopedState } from '@/views/states/onViewFiltersChangeScopedState';
|
||||
import { savedViewFiltersScopedFamilyState } from '@/views/states/savedViewFiltersScopedFamilyState';
|
||||
import { savedViewFiltersByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewFiltersByKeyScopedFamilySelector';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
|
||||
import { useViewSetStates } from '../useViewSetStates';
|
||||
|
||||
export const useViewFilters = (viewScopeId: string) => {
|
||||
const { updateOneMutation, createOneMutation, findManyQuery } =
|
||||
useFindOneMetadataObject({
|
||||
objectNameSingular: 'viewFilterV2',
|
||||
});
|
||||
const apolloClient = useApolloClient();
|
||||
const { setCurrentViewFilters } = useViewSetStates(viewScopeId);
|
||||
|
||||
const persistViewFilters = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (viewId?: string) => {
|
||||
@ -19,69 +31,52 @@ export const useViewFilters = (viewScopeId: string) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const _createViewFilters = (
|
||||
filters: Filter[],
|
||||
availableFilters: FilterDefinition[] = [],
|
||||
) => {
|
||||
if (!currentViewId || !filters.length) {
|
||||
return;
|
||||
}
|
||||
const createViewFilters = (viewFiltersToCreate: ViewFilter[]) => {
|
||||
if (!viewFiltersToCreate.length) return;
|
||||
|
||||
if (!availableFilters) {
|
||||
return;
|
||||
}
|
||||
|
||||
// return createViewFiltersMutation({
|
||||
// variables: {
|
||||
// data: filters.map((filter) => ({
|
||||
// displayValue: filter.displayValue ?? filter.value,
|
||||
// key: filter.key,
|
||||
// name:
|
||||
// availableFilters.find(({ key }) => key === filter.key)
|
||||
// ?.label ?? '',
|
||||
// operand: filter.operand,
|
||||
// value: filter.value,
|
||||
// viewId: viewId ?? currentViewId,
|
||||
// })),
|
||||
// },
|
||||
// });
|
||||
return Promise.all(
|
||||
viewFiltersToCreate.map((viewFilter) =>
|
||||
apolloClient.mutate({
|
||||
mutation: createOneMutation,
|
||||
variables: {
|
||||
input: {
|
||||
fieldId: viewFilter.fieldId,
|
||||
viewId: viewId ?? currentViewId,
|
||||
value: viewFilter.value,
|
||||
displayValue: viewFilter.displayValue,
|
||||
operand: viewFilter.operand,
|
||||
},
|
||||
},
|
||||
refetchQueries: [findManyQuery],
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const _updateViewFilters = (filters: Filter[]) => {
|
||||
if (!currentViewId || !filters.length) return;
|
||||
const updateViewFilters = (viewFiltersToUpdate: ViewFilter[]) => {
|
||||
if (!viewFiltersToUpdate.length) return;
|
||||
|
||||
// return Promise.all(
|
||||
// filters.map((filter) =>
|
||||
// updateViewFilterMutation({
|
||||
// variables: {
|
||||
// data: {
|
||||
// displayValue: filter.displayValue ?? filter.value,
|
||||
// operand: filter.operand,
|
||||
// value: filter.value,
|
||||
// },
|
||||
// where: {
|
||||
// viewId_key: {
|
||||
// key: filter.key,
|
||||
// viewId: viewId ?? currentViewId,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
// ),
|
||||
// );
|
||||
return Promise.all(
|
||||
viewFiltersToUpdate.map((viewFilter) =>
|
||||
apolloClient.mutate({
|
||||
mutation: updateOneMutation,
|
||||
variables: {
|
||||
idToUpdate: viewFilter.id,
|
||||
input: {
|
||||
value: viewFilter.value,
|
||||
displayValue: viewFilter.displayValue,
|
||||
operand: viewFilter.operand,
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const _deleteViewFilters = (filterKeys: string[]) => {
|
||||
if (!currentViewId || !filterKeys.length) return;
|
||||
const deleteViewFilters = (viewFilterIdsToDelete: string[]) => {
|
||||
if (!viewFilterIdsToDelete.length) return;
|
||||
|
||||
// return deleteViewFiltersMutation({
|
||||
// variables: {
|
||||
// where: {
|
||||
// key: { in: filterKeys },
|
||||
// viewId: { equals: viewId ?? currentViewId },
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
// Todo
|
||||
};
|
||||
|
||||
const currentViewFilters = snapshot
|
||||
@ -97,7 +92,7 @@ export const useViewFilters = (viewScopeId: string) => {
|
||||
.getLoadable(
|
||||
savedViewFiltersByKeyScopedFamilySelector({
|
||||
scopeId: viewScopeId,
|
||||
viewId: currentViewId,
|
||||
viewId: viewId ?? currentViewId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
@ -109,32 +104,24 @@ export const useViewFilters = (viewScopeId: string) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const availableFilters = snapshot
|
||||
.getLoadable(
|
||||
availableFiltersScopedState({
|
||||
scopeId: viewScopeId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
|
||||
const filtersToCreate = currentViewFilters.filter(
|
||||
(filter) => !savedViewFiltersByKey[filter.key],
|
||||
(filter) => !savedViewFiltersByKey[filter.fieldId],
|
||||
);
|
||||
await _createViewFilters(filtersToCreate, availableFilters);
|
||||
await createViewFilters(filtersToCreate);
|
||||
|
||||
const filtersToUpdate = currentViewFilters.filter(
|
||||
(filter) =>
|
||||
savedViewFiltersByKey[filter.key] &&
|
||||
(savedViewFiltersByKey[filter.key].operand !== filter.operand ||
|
||||
savedViewFiltersByKey[filter.key].value !== filter.value),
|
||||
savedViewFiltersByKey[filter.fieldId] &&
|
||||
(savedViewFiltersByKey[filter.fieldId].operand !== filter.operand ||
|
||||
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(
|
||||
(previousFilterKey) => !filterKeys.includes(previousFilterKey),
|
||||
);
|
||||
await _deleteViewFilters(filterKeysToDelete);
|
||||
await deleteViewFilters(filterKeysToDelete);
|
||||
set(
|
||||
savedViewFiltersScopedFamilyState({
|
||||
scopeId: viewScopeId,
|
||||
@ -143,8 +130,102 @@ export const useViewFilters = (viewScopeId: string) => {
|
||||
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 };
|
||||
};
|
||||
|
||||
@ -1,16 +1,25 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { produce } from 'immer';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useFindOneMetadataObject } from '@/metadata/hooks/useFindOneMetadataObject';
|
||||
import { Sort } from '@/ui/data/sort/types/Sort';
|
||||
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
|
||||
import { currentViewSortsScopedFamilyState } from '@/views/states/currentViewSortsScopedFamilyState';
|
||||
import { onViewSortsChangeScopedState } from '@/views/states/onViewSortsChangeScopedState';
|
||||
import { savedViewSortsScopedFamilyState } from '@/views/states/savedViewSortsScopedFamilyState';
|
||||
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) => {
|
||||
const { setCurrentViewSorts } = useViewStates(viewScopeId);
|
||||
const { updateOneMutation, createOneMutation, findManyQuery } =
|
||||
useFindOneMetadataObject({
|
||||
objectNameSingular: 'viewSortV2',
|
||||
});
|
||||
const apolloClient = useApolloClient();
|
||||
const { setCurrentViewSorts } = useViewSetStates(viewScopeId);
|
||||
|
||||
const persistViewSorts = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
@ -22,54 +31,48 @@ export const useViewSorts = (viewScopeId: string) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const _createViewSorts = (sorts: Sort[]) => {
|
||||
if (!currentViewId || !sorts.length) return;
|
||||
const createViewSorts = (viewSortsToCreate: ViewSort[]) => {
|
||||
if (!viewSortsToCreate.length) return;
|
||||
|
||||
// return createViewSortsMutation({
|
||||
// variables: {
|
||||
// data: sorts.map((sort) => ({
|
||||
// key: sort.key,
|
||||
// direction: sort.direction as ViewSortDirection,
|
||||
// name: sort.definition.label,
|
||||
// viewId: viewId ?? currentViewId,
|
||||
// })),
|
||||
// },
|
||||
// });
|
||||
return Promise.all(
|
||||
viewSortsToCreate.map((viewSort) =>
|
||||
apolloClient.mutate({
|
||||
mutation: createOneMutation,
|
||||
variables: {
|
||||
input: {
|
||||
fieldId: viewSort.fieldId,
|
||||
viewId: viewId ?? currentViewId,
|
||||
direction: viewSort.direction,
|
||||
},
|
||||
},
|
||||
refetchQueries: [findManyQuery],
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const _updateViewSorts = (sorts: Sort[]) => {
|
||||
if (!currentViewId || !sorts.length) return;
|
||||
const updateViewSorts = (viewSortsToUpdate: ViewSort[]) => {
|
||||
if (!viewSortsToUpdate.length) return;
|
||||
|
||||
// return Promise.all(
|
||||
// sorts.map((sort) =>
|
||||
// updateViewSortMutation({
|
||||
// variables: {
|
||||
// data: {
|
||||
// direction: sort.direction as ViewSortDirection,
|
||||
// },
|
||||
// where: {
|
||||
// viewId_key: {
|
||||
// key: sort.key,
|
||||
// viewId: viewId ?? currentViewId,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// }),
|
||||
// ),
|
||||
// );
|
||||
return Promise.all(
|
||||
viewSortsToUpdate.map((viewSort) =>
|
||||
apolloClient.mutate({
|
||||
mutation: updateOneMutation,
|
||||
variables: {
|
||||
idToUpdate: viewSort.id,
|
||||
input: {
|
||||
direction: viewSort.direction,
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const _deleteViewSorts = (sortKeys: string[]) => {
|
||||
if (!currentViewId || !sortKeys.length) return;
|
||||
const deleteViewSorts = (viewSortIdsToDelete: string[]) => {
|
||||
if (!viewSortIdsToDelete.length) return;
|
||||
|
||||
// return deleteViewSortsMutation({
|
||||
// variables: {
|
||||
// where: {
|
||||
// key: { in: sortKeys },
|
||||
// viewId: { equals: viewId ?? currentViewId },
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
// Todo
|
||||
};
|
||||
|
||||
const currentViewSorts = snapshot
|
||||
@ -85,7 +88,7 @@ export const useViewSorts = (viewScopeId: string) => {
|
||||
.getLoadable(
|
||||
savedViewSortsByKeyScopedFamilySelector({
|
||||
scopeId: viewScopeId,
|
||||
viewId: currentViewId,
|
||||
viewId: viewId ?? currentViewId,
|
||||
}),
|
||||
)
|
||||
.getValue();
|
||||
@ -98,22 +101,23 @@ export const useViewSorts = (viewScopeId: string) => {
|
||||
}
|
||||
|
||||
const sortsToCreate = currentViewSorts.filter(
|
||||
(sort) => !savedViewSortsByKey[sort.key],
|
||||
(sort) => !savedViewSortsByKey[sort.fieldId],
|
||||
);
|
||||
await _createViewSorts(sortsToCreate);
|
||||
|
||||
await createViewSorts(sortsToCreate);
|
||||
|
||||
const sortsToUpdate = currentViewSorts.filter(
|
||||
(sort) =>
|
||||
savedViewSortsByKey[sort.key] &&
|
||||
savedViewSortsByKey[sort.key].direction !== sort.direction,
|
||||
savedViewSortsByKey[sort.fieldId] &&
|
||||
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(
|
||||
(previousSortKey) => !sortKeys.includes(previousSortKey),
|
||||
);
|
||||
await _deleteViewSorts(sortKeysToDelete);
|
||||
await deleteViewSorts(sortKeysToDelete);
|
||||
set(
|
||||
savedViewSortsScopedFamilyState({
|
||||
scopeId: viewScopeId,
|
||||
@ -122,24 +126,99 @@ export const useViewSorts = (viewScopeId: string) => {
|
||||
currentViewSorts,
|
||||
);
|
||||
},
|
||||
[viewScopeId],
|
||||
[
|
||||
apolloClient,
|
||||
createOneMutation,
|
||||
findManyQuery,
|
||||
updateOneMutation,
|
||||
viewScopeId,
|
||||
],
|
||||
);
|
||||
|
||||
const upsertViewSort = (sortToUpsert: Sort) => {
|
||||
setCurrentViewSorts?.((sorts) => {
|
||||
return produce(sorts, (sortsDraft) => {
|
||||
const index = sortsDraft.findIndex(
|
||||
(sort) => sort.key === sortToUpsert.key,
|
||||
);
|
||||
const upsertViewSort = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(sortToUpsert: Sort) => {
|
||||
const currentViewId = snapshot
|
||||
.getLoadable(currentViewIdScopedState({ scopeId: viewScopeId }))
|
||||
.getValue();
|
||||
|
||||
if (index === -1) {
|
||||
sortsDraft.push(sortToUpsert);
|
||||
} else {
|
||||
sortsDraft[index] = sortToUpsert;
|
||||
if (!currentViewId) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
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 };
|
||||
};
|
||||
|
||||
@ -1,13 +1,21 @@
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useFindOneMetadataObject } from '@/metadata/hooks/useFindOneMetadataObject';
|
||||
import { viewObjectIdScopeState } from '@/views/states/viewObjectIdScopeState';
|
||||
import { viewTypeScopedState } from '@/views/states/viewTypeScopedState';
|
||||
import { View } from '@/views/types/View';
|
||||
|
||||
export const useViews = (scopeId: string) => {
|
||||
const { updateOneMutation, createOneMutation, findManyQuery } =
|
||||
useFindOneMetadataObject({
|
||||
objectNameSingular: 'viewV2',
|
||||
});
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
const createView = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (_view: Pick<View, 'id' | 'name'>) => {
|
||||
async (view: Pick<View, 'id' | 'name'>) => {
|
||||
const viewObjectId = await snapshot
|
||||
.getLoadable(viewObjectIdScopeState({ scopeId }))
|
||||
.getValue();
|
||||
@ -19,27 +27,31 @@ export const useViews = (scopeId: string) => {
|
||||
if (!viewObjectId || !viewType) {
|
||||
return;
|
||||
}
|
||||
// await createViewMutation({
|
||||
// variables: {
|
||||
// data: {
|
||||
// ...view,
|
||||
// objectId: viewObjectId,
|
||||
// type: viewType,
|
||||
// },
|
||||
// },
|
||||
// refetchQueries: [getOperationName(GET_VIEWS) ?? ''],
|
||||
// });
|
||||
await apolloClient.mutate({
|
||||
mutation: createOneMutation,
|
||||
variables: {
|
||||
input: {
|
||||
...view,
|
||||
objectId: viewObjectId,
|
||||
type: viewType,
|
||||
},
|
||||
},
|
||||
refetchQueries: [findManyQuery],
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const updateView = async (_view: View) => {
|
||||
// await updateViewMutation({
|
||||
// variables: {
|
||||
// data: { name: view.name },
|
||||
// where: { id: view.id },
|
||||
// },
|
||||
// refetchQueries: [getOperationName(GET_VIEWS) ?? ''],
|
||||
// });
|
||||
const updateView = async (view: View) => {
|
||||
await apolloClient.mutate({
|
||||
mutation: updateOneMutation,
|
||||
variables: {
|
||||
idToUpdate: view.id,
|
||||
input: {
|
||||
...view,
|
||||
},
|
||||
},
|
||||
refetchQueries: [findManyQuery],
|
||||
});
|
||||
};
|
||||
|
||||
const deleteView = async (_viewId: string) => {
|
||||
|
||||
@ -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;
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
@ -10,8 +10,12 @@ import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsS
|
||||
import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
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 { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState';
|
||||
import { currentViewScopedSelector } from '../states/selectors/currentViewScopedSelector';
|
||||
import { viewEditModeScopedState } from '../states/viewEditModeScopedState';
|
||||
import { viewsScopedState } from '../states/viewsScopedState';
|
||||
|
||||
@ -19,7 +23,7 @@ import { useViewFields } from './internal/useViewFields';
|
||||
import { useViewFilters } from './internal/useViewFilters';
|
||||
import { useViews } from './internal/useViews';
|
||||
import { useViewSorts } from './internal/useViewSorts';
|
||||
import { useViewStates } from './useViewStates';
|
||||
import { useViewSetStates } from './useViewSetStates';
|
||||
|
||||
type UseViewProps = {
|
||||
viewScopeId?: string;
|
||||
@ -42,33 +46,81 @@ export const useView = (props?: UseViewProps) => {
|
||||
setEntityCountInCurrentView,
|
||||
setIsViewBarExpanded,
|
||||
|
||||
setAvailableSorts,
|
||||
setAvailableSortDefinitions,
|
||||
setCurrentViewSorts,
|
||||
setSavedViewSorts,
|
||||
|
||||
setAvailableFilters,
|
||||
setAvailableFilterDefinitions,
|
||||
setCurrentViewFilters,
|
||||
setSavedViewFilters,
|
||||
|
||||
setAvailableFields,
|
||||
setAvailableFieldDefinitions,
|
||||
setCurrentViewFields,
|
||||
setSavedViewFields,
|
||||
} = useViewStates(scopeId);
|
||||
|
||||
const { persistViewSorts, upsertViewSort } = useViewSorts(scopeId);
|
||||
const { persistViewFilters } = useViewFilters(scopeId);
|
||||
setOnViewFieldsChange,
|
||||
setOnViewFiltersChange,
|
||||
setOnViewSortsChange,
|
||||
} = useViewSetStates(scopeId);
|
||||
|
||||
const { persistViewSorts, upsertViewSort, removeViewSort } =
|
||||
useViewSorts(scopeId);
|
||||
const { persistViewFilters, upsertViewFilter, removeViewFilter } =
|
||||
useViewFilters(scopeId);
|
||||
const { persistViewFields } = useViewFields(scopeId);
|
||||
const { createView: internalCreateView, deleteView: internalDeleteView } =
|
||||
useViews(scopeId);
|
||||
const {
|
||||
createView: internalCreateView,
|
||||
updateView: internalUpdateView,
|
||||
deleteView: internalDeleteView,
|
||||
} = useViews(scopeId);
|
||||
const [_, setSearchParams] = useSearchParams();
|
||||
|
||||
const changeView = useCallback(
|
||||
const changeViewInUrl = useCallback(
|
||||
(viewId: string) => {
|
||||
setSearchParams({ view: viewId });
|
||||
},
|
||||
[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 savedViewFilters = snapshot
|
||||
.getLoadable(
|
||||
@ -95,7 +147,6 @@ export const useView = (props?: UseViewProps) => {
|
||||
setCurrentViewSorts?.(savedViewSorts);
|
||||
}
|
||||
setViewEditMode?.('none');
|
||||
setIsViewBarExpanded?.(false);
|
||||
});
|
||||
|
||||
const createView = useRecoilCallback(
|
||||
@ -156,10 +207,10 @@ export const useView = (props?: UseViewProps) => {
|
||||
await persistViewFilters(newViewId);
|
||||
await persistViewSorts(newViewId);
|
||||
|
||||
changeView(newViewId);
|
||||
changeViewInUrl(newViewId);
|
||||
},
|
||||
[
|
||||
changeView,
|
||||
changeViewInUrl,
|
||||
currentViewId,
|
||||
internalCreateView,
|
||||
persistViewFields,
|
||||
@ -195,15 +246,32 @@ export const useView = (props?: UseViewProps) => {
|
||||
const handleViewNameSubmit = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (name?: string) => {
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewEditMode = snapshot
|
||||
.getLoadable(viewEditModeScopedState({ scopeId }))
|
||||
.getValue();
|
||||
|
||||
const currentView = snapshot
|
||||
.getLoadable(currentViewScopedSelector(scopeId))
|
||||
.getValue();
|
||||
|
||||
if (!currentView) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (viewEditMode === 'create' && name) {
|
||||
await createView(name);
|
||||
} else {
|
||||
await internalUpdateView({
|
||||
...currentView,
|
||||
name,
|
||||
});
|
||||
}
|
||||
},
|
||||
[createView, scopeId],
|
||||
[createView, internalUpdateView, scopeId],
|
||||
);
|
||||
|
||||
return {
|
||||
@ -224,20 +292,28 @@ export const useView = (props?: UseViewProps) => {
|
||||
setViewType,
|
||||
setEntityCountInCurrentView,
|
||||
|
||||
setAvailableSorts,
|
||||
setAvailableSortDefinitions,
|
||||
setCurrentViewSorts,
|
||||
setSavedViewSorts,
|
||||
upsertViewSort,
|
||||
removeViewSort,
|
||||
|
||||
setAvailableFilters,
|
||||
setAvailableFilterDefinitions,
|
||||
setCurrentViewFilters,
|
||||
setSavedViewFilters,
|
||||
upsertViewFilter,
|
||||
removeViewFilter,
|
||||
|
||||
setAvailableFields,
|
||||
setAvailableFieldDefinitions,
|
||||
setCurrentViewFields,
|
||||
setSavedViewFields,
|
||||
|
||||
persistViewFields,
|
||||
changeView,
|
||||
changeViewInUrl,
|
||||
loadView,
|
||||
|
||||
setOnViewFieldsChange,
|
||||
setOnViewFiltersChange,
|
||||
setOnViewSortsChange,
|
||||
};
|
||||
};
|
||||
|
||||
@ -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 { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext';
|
||||
import { availableFieldsScopedState } from '../states/availableFieldsScopedState';
|
||||
import { availableFiltersScopedState } from '../states/availableFiltersScopedState';
|
||||
import { availableSortsScopedState } from '../states/availableSortsScopedState';
|
||||
import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState';
|
||||
import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState';
|
||||
import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState';
|
||||
import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState';
|
||||
import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
@ -31,17 +31,14 @@ import { viewObjectIdScopeState } from '../states/viewObjectIdScopeState';
|
||||
import { viewsScopedState } from '../states/viewsScopedState';
|
||||
import { viewTypeScopedState } from '../states/viewTypeScopedState';
|
||||
|
||||
export const useViewInternalStates = (
|
||||
viewScopeId?: string,
|
||||
viewId?: string,
|
||||
) => {
|
||||
export const useViewGetStates = (viewScopeId?: string, viewId?: string) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
ViewScopeInternalContext,
|
||||
viewScopeId,
|
||||
);
|
||||
|
||||
// View
|
||||
const [currentViewId, setCurrentViewId] = useRecoilScopedStateV2(
|
||||
const [currentViewId] = useRecoilScopedStateV2(
|
||||
currentViewIdScopedState,
|
||||
scopeId,
|
||||
);
|
||||
@ -50,39 +47,38 @@ export const useViewInternalStates = (
|
||||
|
||||
const currentView = useRecoilValue(currentViewScopedSelector(scopeId));
|
||||
|
||||
const [viewEditMode, setViewEditMode] = useRecoilScopedStateV2(
|
||||
const [viewEditMode] = useRecoilScopedStateV2(
|
||||
viewEditModeScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const [views, setViews] = useRecoilScopedStateV2(viewsScopedState, scopeId);
|
||||
const [views] = useRecoilScopedStateV2(viewsScopedState, scopeId);
|
||||
|
||||
const [viewObjectId, setViewObjectId] = useRecoilScopedStateV2(
|
||||
const [viewObjectId] = useRecoilScopedStateV2(
|
||||
viewObjectIdScopeState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const [viewType, setViewType] = useRecoilScopedStateV2(
|
||||
viewTypeScopedState,
|
||||
const [viewType] = useRecoilScopedStateV2(viewTypeScopedState, scopeId);
|
||||
|
||||
const [entityCountInCurrentView] = useRecoilScopedStateV2(
|
||||
entityCountInCurrentViewScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const [entityCountInCurrentView, setEntityCountInCurrentView] =
|
||||
useRecoilScopedStateV2(entityCountInCurrentViewScopedState, scopeId);
|
||||
|
||||
const [isViewBarExpanded, setIsViewBarExpanded] = useRecoilScopedStateV2(
|
||||
const [isViewBarExpanded] = useRecoilScopedStateV2(
|
||||
isViewBarExpandedScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
// ViewSorts
|
||||
const [currentViewSorts, setCurrentViewSorts] = useRecoilScopedFamilyState(
|
||||
const [currentViewSorts] = useRecoilScopedFamilyState(
|
||||
currentViewSortsScopedFamilyState,
|
||||
scopeId,
|
||||
familyItemId,
|
||||
);
|
||||
|
||||
const [savedViewSorts, setSavedViewSorts] = useRecoilScopedFamilyState(
|
||||
const [savedViewSorts] = useRecoilScopedFamilyState(
|
||||
savedViewSortsScopedFamilyState,
|
||||
scopeId,
|
||||
familyItemId,
|
||||
@ -95,8 +91,8 @@ export const useViewInternalStates = (
|
||||
}),
|
||||
);
|
||||
|
||||
const [availableSorts, setAvailableSorts] = useRecoilScopedStateV2(
|
||||
availableSortsScopedState,
|
||||
const [availableSortDefinitions] = useRecoilScopedStateV2(
|
||||
availableSortDefinitionsScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
@ -108,14 +104,13 @@ export const useViewInternalStates = (
|
||||
);
|
||||
|
||||
// ViewFilters
|
||||
const [currentViewFilters, setCurrentViewFilters] =
|
||||
useRecoilScopedFamilyState(
|
||||
currentViewFiltersScopedFamilyState,
|
||||
scopeId,
|
||||
familyItemId,
|
||||
);
|
||||
const [currentViewFilters] = useRecoilScopedFamilyState(
|
||||
currentViewFiltersScopedFamilyState,
|
||||
scopeId,
|
||||
familyItemId,
|
||||
);
|
||||
|
||||
const [savedViewFilters, setSavedViewFilters] = useRecoilScopedFamilyState(
|
||||
const [savedViewFilters] = useRecoilScopedFamilyState(
|
||||
savedViewFiltersScopedFamilyState,
|
||||
scopeId,
|
||||
familyItemId,
|
||||
@ -128,8 +123,8 @@ export const useViewInternalStates = (
|
||||
}),
|
||||
);
|
||||
|
||||
const [availableFilters, setAvailableFilters] = useRecoilScopedStateV2(
|
||||
availableFiltersScopedState,
|
||||
const [availableFilterDefinitions] = useRecoilScopedStateV2(
|
||||
availableFilterDefinitionsScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
@ -141,18 +136,18 @@ export const useViewInternalStates = (
|
||||
);
|
||||
|
||||
// ViewFields
|
||||
const [availableFields, setAvailableFields] = useRecoilScopedStateV2(
|
||||
availableFieldsScopedState,
|
||||
const [availableFieldDefinitions] = useRecoilScopedStateV2(
|
||||
availableFieldDefinitionsScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const [currentViewFields, setCurrentViewFields] = useRecoilScopedFamilyState(
|
||||
const [currentViewFields] = useRecoilScopedFamilyState(
|
||||
currentViewFieldsScopedFamilyState,
|
||||
scopeId,
|
||||
familyItemId,
|
||||
);
|
||||
|
||||
const [savedViewFields, setSavedViewFields] = useRecoilScopedFamilyState(
|
||||
const [savedViewFields] = useRecoilScopedFamilyState(
|
||||
savedViewFieldsScopedFamilyState,
|
||||
scopeId,
|
||||
familyItemId,
|
||||
@ -166,17 +161,17 @@ export const useViewInternalStates = (
|
||||
);
|
||||
|
||||
// ViewChangeHandlers
|
||||
const [onViewSortsChange, setOnViewSortsChange] = useRecoilScopedStateV2(
|
||||
const [onViewSortsChange] = useRecoilScopedStateV2(
|
||||
onViewSortsChangeScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const [onViewFiltersChange, setOnViewFiltersChange] = useRecoilScopedStateV2(
|
||||
const [onViewFiltersChange] = useRecoilScopedStateV2(
|
||||
onViewFiltersChangeScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const [onViewFieldsChange, setOnViewFieldsChange] = useRecoilScopedStateV2(
|
||||
const [onViewFieldsChange] = useRecoilScopedStateV2(
|
||||
onViewFieldsChangeScopedState,
|
||||
scopeId,
|
||||
);
|
||||
@ -184,52 +179,33 @@ export const useViewInternalStates = (
|
||||
return {
|
||||
currentViewId,
|
||||
currentView,
|
||||
setCurrentViewId,
|
||||
isViewBarExpanded,
|
||||
setIsViewBarExpanded,
|
||||
|
||||
views,
|
||||
setViews,
|
||||
viewEditMode,
|
||||
setViewEditMode,
|
||||
viewObjectId,
|
||||
setViewObjectId,
|
||||
viewType,
|
||||
setViewType,
|
||||
entityCountInCurrentView,
|
||||
setEntityCountInCurrentView,
|
||||
|
||||
availableSorts,
|
||||
setAvailableSorts,
|
||||
availableSortDefinitions,
|
||||
currentViewSorts,
|
||||
setCurrentViewSorts,
|
||||
savedViewSorts,
|
||||
savedViewSortsByKey,
|
||||
setSavedViewSorts,
|
||||
canPersistSorts,
|
||||
|
||||
availableFilters,
|
||||
setAvailableFilters,
|
||||
availableFilterDefinitions,
|
||||
currentViewFilters,
|
||||
setCurrentViewFilters,
|
||||
savedViewFilters,
|
||||
savedViewFiltersByKey,
|
||||
setSavedViewFilters,
|
||||
canPersistFilters,
|
||||
|
||||
availableFields,
|
||||
setAvailableFields,
|
||||
availableFieldDefinitions,
|
||||
currentViewFields,
|
||||
savedViewFieldsByKey,
|
||||
setCurrentViewFields,
|
||||
savedViewFields,
|
||||
setSavedViewFields,
|
||||
|
||||
onViewSortsChange,
|
||||
setOnViewSortsChange,
|
||||
onViewFiltersChange,
|
||||
setOnViewFiltersChange,
|
||||
onViewFieldsChange,
|
||||
setOnViewFieldsChange,
|
||||
};
|
||||
};
|
||||
@ -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 { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext';
|
||||
import { availableFieldsScopedState } from '../states/availableFieldsScopedState';
|
||||
import { availableFiltersScopedState } from '../states/availableFiltersScopedState';
|
||||
import { availableSortsScopedState } from '../states/availableSortsScopedState';
|
||||
import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState';
|
||||
import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState';
|
||||
import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState';
|
||||
import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState';
|
||||
import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState';
|
||||
import { entityCountInCurrentViewScopedState } from '../states/entityCountInCurrentViewScopedState';
|
||||
import { isViewBarExpandedScopedState } from '../states/isViewBarExpandedScopedState';
|
||||
import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState';
|
||||
import { onViewFiltersChangeScopedState } from '../states/onViewFiltersChangeScopedState';
|
||||
import { onViewSortsChangeScopedState } from '../states/onViewSortsChangeScopedState';
|
||||
import { savedViewFieldsScopedFamilyState } from '../states/savedViewFieldsScopedFamilyState';
|
||||
import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState';
|
||||
import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState';
|
||||
@ -21,7 +24,7 @@ import { viewObjectIdScopeState } from '../states/viewObjectIdScopeState';
|
||||
import { viewsScopedState } from '../states/viewsScopedState';
|
||||
import { viewTypeScopedState } from '../states/viewTypeScopedState';
|
||||
|
||||
export const useViewStates = (viewScopeId?: string, viewId?: string) => {
|
||||
export const useViewSetStates = (viewScopeId?: string, viewId?: string) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
ViewScopeInternalContext,
|
||||
viewScopeId,
|
||||
@ -71,8 +74,8 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => {
|
||||
familyItemId,
|
||||
);
|
||||
|
||||
const setAvailableSorts = useSetRecoilScopedStateV2(
|
||||
availableSortsScopedState,
|
||||
const setAvailableSortDefinitions = useSetRecoilScopedStateV2(
|
||||
availableSortDefinitionsScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
@ -89,14 +92,14 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => {
|
||||
familyItemId,
|
||||
);
|
||||
|
||||
const setAvailableFilters = useSetRecoilScopedStateV2(
|
||||
availableFiltersScopedState,
|
||||
const setAvailableFilterDefinitions = useSetRecoilScopedStateV2(
|
||||
availableFilterDefinitionsScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
// ViewFields
|
||||
const setAvailableFields = useSetRecoilScopedStateV2(
|
||||
availableFieldsScopedState,
|
||||
const setAvailableFieldDefinitions = useSetRecoilScopedStateV2(
|
||||
availableFieldDefinitionsScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
@ -112,6 +115,21 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => {
|
||||
familyItemId,
|
||||
);
|
||||
|
||||
const setOnViewFieldsChange = useSetRecoilScopedStateV2(
|
||||
onViewFieldsChangeScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const setOnViewFiltersChange = useSetRecoilScopedStateV2(
|
||||
onViewFiltersChangeScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const setOnViewSortsChange = useSetRecoilScopedStateV2(
|
||||
onViewSortsChangeScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
return {
|
||||
currentViewId,
|
||||
setCurrentViewId,
|
||||
@ -123,16 +141,20 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => {
|
||||
setViewEditMode,
|
||||
setEntityCountInCurrentView,
|
||||
|
||||
setAvailableSorts,
|
||||
setAvailableSortDefinitions,
|
||||
setCurrentViewSorts,
|
||||
setSavedViewSorts,
|
||||
|
||||
setAvailableFilters,
|
||||
setAvailableFilterDefinitions,
|
||||
setCurrentViewFilters,
|
||||
setSavedViewFilters,
|
||||
|
||||
setAvailableFields,
|
||||
setAvailableFieldDefinitions,
|
||||
setCurrentViewFields,
|
||||
setSavedViewFields,
|
||||
|
||||
setOnViewFieldsChange,
|
||||
setOnViewFiltersChange,
|
||||
setOnViewSortsChange,
|
||||
};
|
||||
};
|
||||
@ -3,7 +3,6 @@ import { useEffect } from 'react';
|
||||
import { Filter } from '@/ui/data/filter/types/Filter';
|
||||
import { Sort } from '@/ui/data/sort/types/Sort';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { useViewInternalStates } from '@/views/hooks/useViewInternalStates';
|
||||
import { ViewField } from '@/views/types/ViewField';
|
||||
|
||||
type ViewScopeInitEffectProps = {
|
||||
@ -14,17 +13,15 @@ type ViewScopeInitEffectProps = {
|
||||
};
|
||||
|
||||
export const ViewScopeInitEffect = ({
|
||||
viewScopeId,
|
||||
onViewSortsChange,
|
||||
onViewFiltersChange,
|
||||
onViewFieldsChange,
|
||||
}: ViewScopeInitEffectProps) => {
|
||||
const { currentViewId } = useView();
|
||||
const {
|
||||
setOnViewSortsChange,
|
||||
setOnViewFieldsChange,
|
||||
setOnViewFiltersChange,
|
||||
} = useViewInternalStates(viewScopeId, currentViewId);
|
||||
} = useView();
|
||||
|
||||
useEffect(() => {
|
||||
setOnViewSortsChange(() => onViewSortsChange);
|
||||
|
||||
@ -2,9 +2,9 @@ import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition';
|
||||
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const availableFieldsScopedState = createScopedState<
|
||||
export const availableFieldDefinitionsScopedState = createScopedState<
|
||||
ColumnDefinition<FieldMetadata>[]
|
||||
>({
|
||||
key: 'availableFieldsScopedState',
|
||||
key: 'availableFieldDefinitionsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,9 +1,9 @@
|
||||
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const availableFiltersScopedState = createScopedState<
|
||||
export const availableFilterDefinitionsScopedState = createScopedState<
|
||||
FilterDefinition[]
|
||||
>({
|
||||
key: 'availableFiltersScopedState',
|
||||
key: 'availableFilterDefinitionsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,7 +1,9 @@
|
||||
import { SortDefinition } from '@/ui/data/sort/types/SortDefinition';
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const availableSortsScopedState = createScopedState<SortDefinition[]>({
|
||||
key: 'availableSortsScopedState',
|
||||
export const availableSortDefinitionsScopedState = createScopedState<
|
||||
SortDefinition[]
|
||||
>({
|
||||
key: 'availableSortDefinitionsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -1,8 +1,9 @@
|
||||
import { Filter } from '@/ui/data/filter/types/Filter';
|
||||
import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState';
|
||||
|
||||
import { ViewFilter } from '../types/ViewFilter';
|
||||
|
||||
export const currentViewFiltersScopedFamilyState = createScopedFamilyState<
|
||||
Filter[],
|
||||
ViewFilter[],
|
||||
string
|
||||
>({
|
||||
key: 'currentViewFiltersScopedFamilyState',
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { Sort } from '@/ui/data/sort/types/Sort';
|
||||
import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState';
|
||||
|
||||
import { ViewSort } from '../types/ViewSort';
|
||||
|
||||
export const currentViewSortsScopedFamilyState = createScopedFamilyState<
|
||||
Sort[],
|
||||
ViewSort[],
|
||||
string
|
||||
>({
|
||||
key: 'currentViewSortsScopedFamilyState',
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { Filter } from '@/ui/data/filter/types/Filter';
|
||||
import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState';
|
||||
|
||||
import { ViewFilter } from '../types/ViewFilter';
|
||||
|
||||
export const savedViewFiltersScopedFamilyState = createScopedFamilyState<
|
||||
Filter[],
|
||||
ViewFilter[],
|
||||
string
|
||||
>({
|
||||
key: 'savedViewFiltersScopedFamilyState',
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { Sort } from '@/ui/data/sort/types/Sort';
|
||||
import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState';
|
||||
|
||||
import { ViewSort } from '../types/ViewSort';
|
||||
|
||||
export const savedViewSortsScopedFamilyState = createScopedFamilyState<
|
||||
Sort[],
|
||||
ViewSort[],
|
||||
string
|
||||
>({
|
||||
key: 'savedViewSortsScopedFamilyState',
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { Filter } from '@/ui/data/filter/types/Filter';
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
|
||||
import { savedViewFiltersScopedFamilyState } from '../savedViewFiltersScopedFamilyState';
|
||||
|
||||
@ -17,8 +17,8 @@ export const savedViewFiltersByKeyScopedFamilySelector = selectorFamily({
|
||||
scopeId: scopeId,
|
||||
familyKey: viewId,
|
||||
}),
|
||||
).reduce<Record<string, Filter>>(
|
||||
(result, filter) => ({ ...result, [filter.key]: filter }),
|
||||
).reduce<Record<string, ViewFilter>>(
|
||||
(result, filter) => ({ ...result, [filter.fieldId]: filter }),
|
||||
{},
|
||||
);
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { Sort } from '@/ui/data/sort/types/Sort';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
|
||||
import { savedViewSortsScopedFamilyState } from '../savedViewSortsScopedFamilyState';
|
||||
|
||||
@ -17,8 +17,8 @@ export const savedViewSortsByKeyScopedFamilySelector = selectorFamily({
|
||||
scopeId: scopeId,
|
||||
familyKey: viewId,
|
||||
}),
|
||||
).reduce<Record<string, Sort>>(
|
||||
(result, sort) => ({ ...result, [sort.key]: sort }),
|
||||
).reduce<Record<string, ViewSort>>(
|
||||
(result, sort) => ({ ...result, [sort.fieldId]: sort }),
|
||||
{},
|
||||
);
|
||||
},
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition';
|
||||
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
|
||||
|
||||
export type ViewField = {
|
||||
id: string;
|
||||
fieldId: string;
|
||||
position: number;
|
||||
isVisible: boolean;
|
||||
size: number;
|
||||
definition: ColumnDefinition<FieldMetadata>;
|
||||
};
|
||||
|
||||
12
front/src/modules/views/types/ViewFilter.ts
Normal file
12
front/src/modules/views/types/ViewFilter.ts
Normal 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;
|
||||
};
|
||||
9
front/src/modules/views/types/ViewSort.ts
Normal file
9
front/src/modules/views/types/ViewSort.ts
Normal 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;
|
||||
};
|
||||
@ -12,5 +12,6 @@ export const columnDefinitionsToViewFields = (
|
||||
position: columnDefinition.position,
|
||||
size: columnDefinition.size,
|
||||
isVisible: columnDefinition.isVisible ?? true,
|
||||
definition: columnDefinition,
|
||||
}));
|
||||
};
|
||||
|
||||
@ -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);
|
||||
};
|
||||
15
front/src/modules/views/utils/viewFiltersToFilters.ts
Normal file
15
front/src/modules/views/utils/viewFiltersToFilters.ts
Normal 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,
|
||||
};
|
||||
});
|
||||
};
|
||||
13
front/src/modules/views/utils/viewSortsToSorts.ts
Normal file
13
front/src/modules/views/utils/viewSortsToSorts.ts
Normal 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,
|
||||
};
|
||||
});
|
||||
};
|
||||
@ -28,7 +28,7 @@ import { getLogoUrlFromDomainName } from '~/utils';
|
||||
import { CompanyNameEditableField } from '../../modules/companies/editable-field/components/CompanyNameEditableField';
|
||||
import { ShowPageContainer } from '../../modules/ui/layout/page/ShowPageContainer';
|
||||
|
||||
import { companyShowFieldDefinition } from './constants/companyShowFieldDefinition';
|
||||
import { companyShowFieldDefinitions } from './constants/companyShowFieldDefinitions';
|
||||
|
||||
export const CompanyShow = () => {
|
||||
const companyId = useParams().companyId ?? '';
|
||||
@ -90,7 +90,7 @@ export const CompanyShow = () => {
|
||||
avatarType="squared"
|
||||
/>
|
||||
<PropertyBox extraPadding={true}>
|
||||
{companyShowFieldDefinition.map((fieldDefinition) => {
|
||||
{companyShowFieldDefinitions.map((fieldDefinition) => {
|
||||
return (
|
||||
<FieldContext.Provider
|
||||
key={company.id + fieldDefinition.fieldId}
|
||||
|
||||
@ -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 />,
|
||||
},
|
||||
];
|
||||
@ -20,7 +20,7 @@ import {
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { User } from '~/generated/graphql';
|
||||
|
||||
export const companyShowFieldDefinition: FieldDefinition<FieldMetadata>[] = [
|
||||
export const companyShowFieldDefinitions: FieldDefinition<FieldMetadata>[] = [
|
||||
{
|
||||
fieldId: 'domainName',
|
||||
label: 'Domain name',
|
||||
@ -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 />,
|
||||
},
|
||||
];
|
||||
@ -7,29 +7,29 @@ import {
|
||||
IconUsers,
|
||||
} from '@/ui/display/icon/index';
|
||||
|
||||
export const companyAvailableSorts: SortDefinition[] = [
|
||||
export const companyTableSortDefinitions: SortDefinition[] = [
|
||||
{
|
||||
key: 'name',
|
||||
fieldId: 'name',
|
||||
label: 'Name',
|
||||
Icon: IconBuildingSkyscraper,
|
||||
},
|
||||
{
|
||||
key: 'employees',
|
||||
fieldId: 'employees',
|
||||
label: 'Employees',
|
||||
Icon: IconUsers,
|
||||
},
|
||||
{
|
||||
key: 'domainName',
|
||||
fieldId: 'domainName',
|
||||
label: 'Url',
|
||||
Icon: IconLink,
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
fieldId: 'address',
|
||||
label: 'Address',
|
||||
Icon: IconMap,
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
fieldId: 'createdAt',
|
||||
label: 'Creation',
|
||||
Icon: IconCalendarEvent,
|
||||
},
|
||||
@ -1,3 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { CompanyBoard } from '@/companies/board/components/CompanyBoard';
|
||||
import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
|
||||
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 { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
||||
|
||||
const StyledBoardContainer = styled.div`
|
||||
display: flex;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const Opportunities = () => {
|
||||
const { handlePipelineStageAdd, handlePipelineStageDelete } =
|
||||
usePipelineStages();
|
||||
@ -51,11 +58,13 @@ export const Opportunities = () => {
|
||||
scopeId="opportunities"
|
||||
CustomRecoilScopeContext={CompanyBoardRecoilScopeContext}
|
||||
>
|
||||
<CompanyBoard
|
||||
onColumnAdd={handlePipelineStageAdd}
|
||||
onColumnDelete={handlePipelineStageDelete}
|
||||
onEditColumnTitle={handleEditColumnTitle}
|
||||
/>
|
||||
<StyledBoardContainer>
|
||||
<CompanyBoard
|
||||
onColumnAdd={handlePipelineStageAdd}
|
||||
onColumnDelete={handlePipelineStageDelete}
|
||||
onEditColumnTitle={handleEditColumnTitle}
|
||||
/>
|
||||
</StyledBoardContainer>
|
||||
</RecoilScope>
|
||||
</BoardOptionsContext.Provider>
|
||||
</PageBody>
|
||||
|
||||
@ -8,31 +8,31 @@ import {
|
||||
} from '@/ui/display/icon/index';
|
||||
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',
|
||||
Icon: IconCurrencyDollar,
|
||||
type: 'number',
|
||||
},
|
||||
{
|
||||
key: 'closeDate',
|
||||
fieldId: 'closeDate',
|
||||
label: 'Close date',
|
||||
Icon: IconCalendarEvent,
|
||||
type: 'date',
|
||||
},
|
||||
{
|
||||
key: 'companyId',
|
||||
fieldId: 'companyId',
|
||||
label: 'Company',
|
||||
Icon: IconBuildingSkyscraper,
|
||||
type: 'entity',
|
||||
entitySelectComponent: <FilterDropdownCompanySearchSelect />,
|
||||
},
|
||||
{
|
||||
key: 'pointOfContactId',
|
||||
fieldId: 'pointOfContactId',
|
||||
label: 'Point of contact',
|
||||
Icon: IconUser,
|
||||
type: 'entity',
|
||||
@ -1,19 +1,19 @@
|
||||
import { SortDefinition } from '@/ui/data/sort/types/SortDefinition';
|
||||
import { IconCalendarEvent, IconCurrencyDollar } from '@/ui/display/icon/index';
|
||||
|
||||
export const opportunitiesSorts: SortDefinition[] = [
|
||||
export const opportunityBoardSortDefinitions: SortDefinition[] = [
|
||||
{
|
||||
key: 'createdAt',
|
||||
fieldId: 'createdAt',
|
||||
label: 'Creation',
|
||||
Icon: IconCalendarEvent,
|
||||
},
|
||||
{
|
||||
key: 'amount',
|
||||
fieldId: 'amount',
|
||||
label: 'Amount',
|
||||
Icon: IconCurrencyDollar,
|
||||
},
|
||||
{
|
||||
key: 'closeDate',
|
||||
fieldId: 'closeDate',
|
||||
label: 'Expected close date',
|
||||
Icon: IconCalendarEvent,
|
||||
},
|
||||
@ -2,12 +2,12 @@ import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
|
||||
import { NewCompanyProgressButton } from '@/companies/components/NewCompanyProgressButton';
|
||||
import { BoardOptions } from '@/ui/layout/board/types/BoardOptions';
|
||||
|
||||
import { opportunitiesFilters } from './opportunities-filters';
|
||||
import { opportunitiesSorts } from './opportunities-sorts';
|
||||
import { opportunityBoardFilterDefinitions } from './constants/opportunityBoardFilterDefinitions';
|
||||
import { opportunityBoardSortDefinitions } from './constants/opportunityBoardSortDefinitions';
|
||||
|
||||
export const opportunitiesBoardOptions: BoardOptions = {
|
||||
newCardComponent: <NewCompanyProgressButton />,
|
||||
CardComponent: CompanyBoardCard,
|
||||
filters: opportunitiesFilters,
|
||||
sorts: opportunitiesSorts,
|
||||
filterDefinitions: opportunityBoardFilterDefinitions,
|
||||
sortDefinitions: opportunityBoardSortDefinitions,
|
||||
};
|
||||
|
||||
@ -2,7 +2,7 @@ import styled from '@emotion/styled';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
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 { DataTableActionBar } from '@/ui/data/data-table/action-bar/components/DataTableActionBar';
|
||||
import { DataTableContextMenu } from '@/ui/data/data-table/context-menu/components/DataTableContextMenu';
|
||||
@ -62,7 +62,7 @@ export const People = () => {
|
||||
CustomRecoilScopeContext={TableRecoilScopeContext}
|
||||
>
|
||||
<StyledTableContainer>
|
||||
<PeopleTable />
|
||||
<PersonTable />
|
||||
</StyledTableContainer>
|
||||
<DataTableActionBar />
|
||||
<DataTableContextMenu />
|
||||
|
||||
@ -31,7 +31,7 @@ import {
|
||||
import { PeopleFullNameEditableField } from '../../modules/people/editable-field/components/PeopleFullNameEditableField';
|
||||
import { ShowPageContainer } from '../../modules/ui/layout/page/ShowPageContainer';
|
||||
|
||||
import { personShowFieldDefinition } from './constants/personShowFieldDefinition';
|
||||
import { personShowFieldDefinition } from './constants/personShowFieldDefinitions';
|
||||
|
||||
export const PersonShow = () => {
|
||||
const personId = useParams().personId ?? '';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user