Improve viewbar api (#2233)
* create scopes * fix import bug * add useView hook * wip * wip * currentViewId is now retrieved via useView * working on sorts with useView * refactor in progress * refactor in progress * refactor in progress * refactor in progress * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * fix code * fix code * wip * push * Fix issue dependencies * Fix resize --------- Co-authored-by: bosiraphael <raphael.bosi@gmail.com>
This commit is contained in:
@ -1,19 +1,13 @@
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext';
|
||||
import { filtersScopedState } from '@/ui/data/view-bar/states/filtersScopedState';
|
||||
import { useFilter } from '@/ui/data/filter/hooks/useFilter';
|
||||
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { ActivityType } from '~/generated/graphql';
|
||||
|
||||
export const PageAddTaskButton = () => {
|
||||
const { selectedFilters } = useFilter();
|
||||
const openCreateActivity = useOpenCreateActivityDrawer();
|
||||
|
||||
const filters = useRecoilScopedValue(
|
||||
filtersScopedState,
|
||||
TasksRecoilScopeContext,
|
||||
);
|
||||
|
||||
const assigneeIdFilter = filters.find(
|
||||
const assigneeIdFilter = selectedFilters.find(
|
||||
(filter) => filter.key === 'assigneeId',
|
||||
);
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { DateTime } from 'luxon';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { turnFilterIntoWhereClause } from '@/ui/data/view-bar/utils/turnFilterIntoWhereClause';
|
||||
import { turnFilterIntoWhereClause } from '@/ui/data/filter/utils/turnFilterIntoWhereClause';
|
||||
import {
|
||||
ActivityType,
|
||||
useGetActivitiesQuery,
|
||||
|
||||
@ -1,18 +1,13 @@
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contexts/TasksRecoilScopeContext';
|
||||
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { filtersScopedState } from '@/ui/data/view-bar/states/filtersScopedState';
|
||||
import { turnFilterIntoWhereClause } from '@/ui/data/view-bar/utils/turnFilterIntoWhereClause';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useFilter } from '@/ui/data/filter/hooks/useFilter';
|
||||
import { turnFilterIntoWhereClause } from '@/ui/data/filter/utils/turnFilterIntoWhereClause';
|
||||
import { ActivityType, useGetActivitiesQuery } from '~/generated/graphql';
|
||||
import { parseDate } from '~/utils/date-utils';
|
||||
|
||||
export const useTasks = (entity?: ActivityTargetableEntity) => {
|
||||
const [filters] = useRecoilScopedState(
|
||||
filtersScopedState,
|
||||
TasksRecoilScopeContext,
|
||||
);
|
||||
const { selectedFilters } = useFilter();
|
||||
|
||||
const whereFilters = entity
|
||||
? {
|
||||
@ -27,7 +22,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
|
||||
}
|
||||
: Object.assign(
|
||||
{},
|
||||
...filters.map((filter) => {
|
||||
...selectedFilters.map((filter) => {
|
||||
return turnFilterIntoWhereClause(filter);
|
||||
}),
|
||||
);
|
||||
@ -40,7 +35,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
|
||||
...whereFilters,
|
||||
},
|
||||
},
|
||||
skip: !entity && filters.length === 0,
|
||||
skip: !entity && selectedFilters.length === 0,
|
||||
});
|
||||
|
||||
const { data: incompleteTaskData } = useGetActivitiesQuery({
|
||||
@ -51,7 +46,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
|
||||
...whereFilters,
|
||||
},
|
||||
},
|
||||
skip: !entity && filters.length === 0,
|
||||
skip: !entity && selectedFilters.length === 0,
|
||||
});
|
||||
|
||||
const todayOrPreviousTasks = incompleteTaskData?.findManyActivities.filter(
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
import { BoardContext } from '@/companies/states/contexts/BoardContext';
|
||||
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
|
||||
import { ViewBarContext } from '@/ui/data/view-bar/contexts/ViewBarContext';
|
||||
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 { useBoardViews } from '@/views/hooks/useBoardViews';
|
||||
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
||||
|
||||
import { HooksCompanyBoardEffect } from '../../components/HooksCompanyBoardEffect';
|
||||
@ -23,15 +20,6 @@ export const CompanyBoard = ({
|
||||
onColumnDelete,
|
||||
onEditColumnTitle,
|
||||
}: CompanyBoardProps) => {
|
||||
// TODO: we can store objectId and fieldDefinitions in the ViewBarContext
|
||||
// And then use the useBoardViews web-hook wherever we need it in the board
|
||||
const { createView, deleteView, submitCurrentView, updateView } =
|
||||
useBoardViews({
|
||||
objectId: 'company',
|
||||
RecoilScopeContext: CompanyBoardRecoilScopeContext,
|
||||
fieldDefinitions: pipelineAvailableFieldDefinitions,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<BoardContext.Provider
|
||||
@ -40,23 +28,13 @@ export const CompanyBoard = ({
|
||||
}}
|
||||
>
|
||||
<HooksCompanyBoardEffect />
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
defaultViewName: 'All Opportunities',
|
||||
onCurrentViewSubmit: submitCurrentView,
|
||||
onViewCreate: createView,
|
||||
onViewEdit: updateView,
|
||||
onViewRemove: deleteView,
|
||||
ViewBarRecoilScopeContext: CompanyBoardRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<EntityBoard
|
||||
boardOptions={opportunitiesBoardOptions}
|
||||
onColumnAdd={onColumnAdd}
|
||||
onColumnDelete={onColumnDelete}
|
||||
onEditColumnTitle={onEditColumnTitle}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
|
||||
<EntityBoard
|
||||
boardOptions={opportunitiesBoardOptions}
|
||||
onColumnAdd={onColumnAdd}
|
||||
onColumnDelete={onColumnDelete}
|
||||
onEditColumnTitle={onEditColumnTitle}
|
||||
/>
|
||||
<EntityBoardActionBar />
|
||||
<EntityBoardContextMenu />
|
||||
</BoardContext.Provider>
|
||||
|
||||
@ -1,24 +1,11 @@
|
||||
import { FilterDropdownEntitySearchSelect } from '@/ui/data/view-bar/components/FilterDropdownEntitySearchSelect';
|
||||
import { useViewBarContext } from '@/ui/data/view-bar/hooks/useViewBarContext';
|
||||
import { filterDropdownSearchInputScopedState } from '@/ui/data/view-bar/states/filterDropdownSearchInputScopedState';
|
||||
import { filterDropdownSelectedEntityIdScopedState } from '@/ui/data/view-bar/states/filterDropdownSelectedEntityIdScopedState';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { FilterDropdownEntitySearchSelect } from '@/ui/data/filter/components/FilterDropdownEntitySearchSelect';
|
||||
import { useFilter } from '@/ui/data/filter/hooks/useFilter';
|
||||
|
||||
import { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompanyQuery';
|
||||
|
||||
export const FilterDropdownCompanySearchSelect = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const filterDropdownSearchInput = useRecoilScopedValue(
|
||||
filterDropdownSearchInputScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [filterDropdownSelectedEntityId] = useRecoilScopedState(
|
||||
filterDropdownSelectedEntityIdScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const { filterDropdownSearchInput, filterDropdownSelectedEntityId } =
|
||||
useFilter();
|
||||
|
||||
const usersForSelect = useFilteredSearchCompanyQuery({
|
||||
searchFilter: filterDropdownSearchInput,
|
||||
|
||||
@ -1,23 +1,13 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { availableFiltersScopedState } from '@/ui/data/view-bar/states/availableFiltersScopedState';
|
||||
import { availableSortsScopedState } from '@/ui/data/view-bar/states/availableSortsScopedState';
|
||||
import { currentViewIdScopedState } from '@/ui/data/view-bar/states/currentViewIdScopedState';
|
||||
import { entityCountInCurrentViewState } from '@/ui/data/view-bar/states/entityCountInCurrentViewState';
|
||||
import { filtersScopedState } from '@/ui/data/view-bar/states/filtersScopedState';
|
||||
import { savedFiltersFamilyState } from '@/ui/data/view-bar/states/savedFiltersFamilyState';
|
||||
import { savedSortsFamilyState } from '@/ui/data/view-bar/states/savedSortsFamilyState';
|
||||
import { sortsOrderByScopedSelector } from '@/ui/data/view-bar/states/selectors/sortsOrderByScopedSelector';
|
||||
import { sortsScopedState } from '@/ui/data/view-bar/states/sortsScopedState';
|
||||
import { turnFilterIntoWhereClause } from '@/ui/data/view-bar/utils/turnFilterIntoWhereClause';
|
||||
import { turnFilterIntoWhereClause } from '@/ui/data/filter/utils/turnFilterIntoWhereClause';
|
||||
import { useBoardActionBarEntries } from '@/ui/layout/board/hooks/useBoardActionBarEntries';
|
||||
import { useBoardContextMenuEntries } from '@/ui/layout/board/hooks/useBoardContextMenuEntries';
|
||||
import { isBoardLoadedState } from '@/ui/layout/board/states/isBoardLoadedState';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { useViewInternalStates } from '@/views/hooks/useViewInternalStates';
|
||||
import {
|
||||
Pipeline,
|
||||
PipelineProgressableType,
|
||||
@ -29,35 +19,25 @@ import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBo
|
||||
|
||||
import { useUpdateCompanyBoardCardIds } from '../hooks/useUpdateBoardCardIds';
|
||||
import { useUpdateCompanyBoard } from '../hooks/useUpdateCompanyBoardColumns';
|
||||
import { CompanyBoardRecoilScopeContext } from '../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
|
||||
|
||||
export const HooksCompanyBoardEffect = () => {
|
||||
const [, setAvailableFilters] = useRecoilScopedState(
|
||||
availableFiltersScopedState,
|
||||
CompanyBoardRecoilScopeContext,
|
||||
);
|
||||
const {
|
||||
setAvailableFilters,
|
||||
setAvailableSorts,
|
||||
setEntityCountInCurrentView,
|
||||
setCurrentViewId,
|
||||
} = useView();
|
||||
|
||||
const [, setAvailableSorts] = useRecoilScopedState(
|
||||
availableSortsScopedState,
|
||||
CompanyBoardRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setEntityCountInCurrentView] = useRecoilState(
|
||||
entityCountInCurrentViewState,
|
||||
);
|
||||
const { currentViewFilters, currentViewSortsOrderBy } =
|
||||
useViewInternalStates();
|
||||
|
||||
useEffect(() => {
|
||||
setAvailableFilters(opportunitiesBoardOptions.filters);
|
||||
setAvailableSorts(opportunitiesBoardOptions.sorts);
|
||||
});
|
||||
setAvailableSorts?.(opportunitiesBoardOptions.sorts);
|
||||
}, [setAvailableFilters, setAvailableSorts]);
|
||||
|
||||
const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState);
|
||||
|
||||
const filters = useRecoilScopedValue(
|
||||
filtersScopedState,
|
||||
CompanyBoardRecoilScopeContext,
|
||||
);
|
||||
|
||||
const updateCompanyBoard = useUpdateCompanyBoard();
|
||||
|
||||
const { data: pipelineData, loading: loadingGetPipelines } =
|
||||
@ -77,18 +57,14 @@ export const HooksCompanyBoardEffect = () => {
|
||||
?.map((pipelineStage) => pipelineStage.id)
|
||||
.flat();
|
||||
|
||||
const sortsOrderBy = useRecoilScopedValue(
|
||||
sortsOrderByScopedSelector,
|
||||
CompanyBoardRecoilScopeContext,
|
||||
);
|
||||
const whereFilters = useMemo(() => {
|
||||
return {
|
||||
AND: [
|
||||
{ pipelineStageId: { in: pipelineStageIds } },
|
||||
...filters.map(turnFilterIntoWhereClause),
|
||||
...(currentViewFilters?.map(turnFilterIntoWhereClause) || []),
|
||||
],
|
||||
};
|
||||
}, [filters, pipelineStageIds]) as any;
|
||||
}, [currentViewFilters, pipelineStageIds]) as any;
|
||||
|
||||
const updateCompanyBoardCardIds = useUpdateCompanyBoardCardIds();
|
||||
|
||||
@ -96,7 +72,7 @@ export const HooksCompanyBoardEffect = () => {
|
||||
useGetPipelineProgressQuery({
|
||||
variables: {
|
||||
where: whereFilters,
|
||||
orderBy: sortsOrderBy,
|
||||
orderBy: currentViewSortsOrderBy,
|
||||
},
|
||||
onCompleted: (data) => {
|
||||
const pipelineProgresses = data?.findManyPipelineProgress || [];
|
||||
@ -123,30 +99,6 @@ export const HooksCompanyBoardEffect = () => {
|
||||
});
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const boardRecoilScopeId = useRecoilScopeId(CompanyBoardRecoilScopeContext);
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const currentView = await snapshot.getPromise(
|
||||
currentViewIdScopedState(boardRecoilScopeId),
|
||||
);
|
||||
if (currentView === viewId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const savedFilters = await snapshot.getPromise(
|
||||
savedFiltersFamilyState(viewId),
|
||||
);
|
||||
const savedSorts = await snapshot.getPromise(
|
||||
savedSortsFamilyState(viewId),
|
||||
);
|
||||
|
||||
set(filtersScopedState(boardRecoilScopeId), savedFilters);
|
||||
set(sortsScopedState(boardRecoilScopeId), savedSorts);
|
||||
set(currentViewIdScopedState(boardRecoilScopeId), viewId);
|
||||
},
|
||||
[boardRecoilScopeId],
|
||||
);
|
||||
|
||||
const loading =
|
||||
loadingGetPipelines || loadingGetPipelineProgress || loadingGetCompanies;
|
||||
@ -158,7 +110,7 @@ export const HooksCompanyBoardEffect = () => {
|
||||
if (!loading && pipeline && pipelineProgresses && companiesData) {
|
||||
const viewId = searchParams.get('view');
|
||||
if (viewId) {
|
||||
handleViewSelect(viewId);
|
||||
//setCurrentViewId(viewId);
|
||||
}
|
||||
setActionBarEntries();
|
||||
setContextMenuEntries();
|
||||
@ -174,8 +126,8 @@ export const HooksCompanyBoardEffect = () => {
|
||||
setActionBarEntries,
|
||||
setContextMenuEntries,
|
||||
searchParams,
|
||||
handleViewSelect,
|
||||
setEntityCountInCurrentView,
|
||||
setCurrentViewId,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
|
||||
@ -25,7 +25,7 @@ import {
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import { User } from '~/generated/graphql';
|
||||
|
||||
export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata>[] =
|
||||
export const companiesAvailableFieldDefinitions: ColumnDefinition<FieldMetadata>[] =
|
||||
[
|
||||
{
|
||||
key: 'name',
|
||||
@ -1,41 +1,39 @@
|
||||
import { companiesAvailableColumnDefinitions } from '@/companies/constants/companiesAvailableColumnDefinitions';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { getCompaniesOptimisticEffectDefinition } from '@/companies/graphql/optimistic-effect-definitions/getCompaniesOptimisticEffectDefinition';
|
||||
import { useCompanyTableActionBarEntries } from '@/companies/hooks/useCompanyTableActionBarEntries';
|
||||
import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyTableContextMenuEntries';
|
||||
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 { TableContext } from '@/ui/data/data-table/contexts/TableContext';
|
||||
import { useUpsertDataTableItem } from '@/ui/data/data-table/hooks/useUpsertDataTableItem';
|
||||
import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { ViewBarContext } from '@/ui/data/view-bar/contexts/ViewBarContext';
|
||||
import { useTableViews } from '@/views/hooks/useTableViews';
|
||||
import { TableOptionsDropdown } from '@/ui/data/data-table/options/components/TableOptionsDropdown';
|
||||
import { TableOptionsHotkeyScope } from '@/ui/data/data-table/types/TableOptionsHotkeyScope';
|
||||
import { ViewBar } from '@/views/components/ViewBar';
|
||||
import { ViewBarEffect } from '@/views/components/ViewBarEffect';
|
||||
import { useViewFields } from '@/views/hooks/internal/useViewFields';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||
import {
|
||||
UpdateOneCompanyMutationVariables,
|
||||
useGetCompaniesQuery,
|
||||
useGetWorkspaceMembersLazyQuery,
|
||||
useUpdateOneCompanyMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { companiesFilters } from '~/pages/companies/companies-filters';
|
||||
import { companyAvailableFilters } from '~/pages/companies/companies-filters';
|
||||
import { companyAvailableSorts } from '~/pages/companies/companies-sorts';
|
||||
|
||||
import CompanyTableEffect from './CompanyTableEffect';
|
||||
|
||||
export const CompanyTable = () => {
|
||||
const tableViewScopeId = 'company-table';
|
||||
|
||||
const [updateEntityMutation] = useUpdateOneCompanyMutation();
|
||||
const upsertDataTableItem = useUpsertDataTableItem();
|
||||
|
||||
const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery();
|
||||
const {
|
||||
createView,
|
||||
deleteView,
|
||||
persistColumns,
|
||||
submitCurrentView,
|
||||
updateView,
|
||||
} = useTableViews({
|
||||
objectId: 'company',
|
||||
columnDefinitions: companiesAvailableColumnDefinitions,
|
||||
});
|
||||
|
||||
const { openCompanySpreadsheetImport } = useSpreadsheetCompanyImport();
|
||||
const { persistViewFields } = useViewFields(tableViewScopeId);
|
||||
const { setCurrentViewFields } = useView({ viewScopeId: tableViewScopeId });
|
||||
|
||||
const { setContextMenuEntries } = useCompanyTableContextMenuEntries();
|
||||
const { setActionBarEntries } = useCompanyTableActionBarEntries();
|
||||
@ -69,38 +67,61 @@ export const CompanyTable = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
return (
|
||||
<TableContext.Provider value={{ onColumnsChange: persistColumns }}>
|
||||
<DataTableEffect
|
||||
getRequestResultKey="companies"
|
||||
useGetRequest={useGetCompaniesQuery}
|
||||
getRequestOptimisticEffectDefinition={
|
||||
getCompaniesOptimisticEffectDefinition
|
||||
}
|
||||
filterDefinitionArray={companiesFilters}
|
||||
sortDefinitionArray={companyAvailableSorts}
|
||||
setContextMenuEntries={setContextMenuEntries}
|
||||
setActionBarEntries={setActionBarEntries}
|
||||
/>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
defaultViewName: 'All Companies',
|
||||
onCurrentViewSubmit: submitCurrentView,
|
||||
onViewCreate: createView,
|
||||
onViewEdit: updateView,
|
||||
onViewRemove: deleteView,
|
||||
onImport: openCompanySpreadsheetImport,
|
||||
ViewBarRecoilScopeContext: TableRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<DataTable
|
||||
updateEntityMutation={({
|
||||
variables,
|
||||
}: {
|
||||
variables: UpdateOneCompanyMutationVariables;
|
||||
}) => updateCompany(variables)}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
</TableContext.Provider>
|
||||
<ViewScope
|
||||
viewScopeId={tableViewScopeId}
|
||||
onViewFieldsChange={() => {}}
|
||||
onViewSortsChange={() => {}}
|
||||
onViewFiltersChange={() => {}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<TableContext.Provider
|
||||
value={{
|
||||
onColumnsChange: (columns) => {
|
||||
setCurrentViewFields?.(columns);
|
||||
persistViewFields(columns);
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ViewBarEffect />
|
||||
|
||||
<ViewBar
|
||||
optionsDropdownButton={
|
||||
<TableOptionsDropdown
|
||||
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
||||
/>
|
||||
}
|
||||
optionsDropdownScopeId="table-dropdown-option"
|
||||
/>
|
||||
<CompanyTableEffect />
|
||||
|
||||
<DataTableEffect
|
||||
getRequestResultKey="companies"
|
||||
useGetRequest={useGetCompaniesQuery}
|
||||
getRequestOptimisticEffectDefinition={
|
||||
getCompaniesOptimisticEffectDefinition
|
||||
}
|
||||
filterDefinitionArray={companyAvailableFilters}
|
||||
sortDefinitionArray={companyAvailableSorts}
|
||||
setContextMenuEntries={setContextMenuEntries}
|
||||
setActionBarEntries={setActionBarEntries}
|
||||
/>
|
||||
<DataTable
|
||||
updateEntityMutation={({
|
||||
variables,
|
||||
}: {
|
||||
variables: UpdateOneCompanyMutationVariables;
|
||||
}) => updateCompany(variables)}
|
||||
/>
|
||||
</TableContext.Provider>
|
||||
</StyledContainer>
|
||||
</ViewScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { companiesAvailableFieldDefinitions } from '@/companies/constants/companiesAvailableFieldDefinitions';
|
||||
import { availableTableColumnsScopedState } from '@/ui/data/data-table/states/availableTableColumnsScopedState';
|
||||
import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumnsScopedState';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { useViewInternalStates } from '@/views/hooks/useViewInternalStates';
|
||||
import { ViewType } from '~/generated/graphql';
|
||||
import { companyAvailableFilters } from '~/pages/companies/companies-filters';
|
||||
import { companyAvailableSorts } from '~/pages/companies/companies-sorts';
|
||||
|
||||
const CompanyTableEffect = () => {
|
||||
const {
|
||||
setAvailableSorts,
|
||||
setAvailableFilters,
|
||||
setAvailableFields,
|
||||
setViewType,
|
||||
setViewObjectId,
|
||||
} = useView();
|
||||
const { currentViewFields } = useViewInternalStates();
|
||||
|
||||
const [, setTableColumns] = useRecoilScopedState(
|
||||
tableColumnsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setAvailableTableColumns] = useRecoilScopedState(
|
||||
availableTableColumnsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setAvailableSorts?.(companyAvailableSorts);
|
||||
setAvailableFilters?.(companyAvailableFilters);
|
||||
setAvailableFields?.(companiesAvailableFieldDefinitions);
|
||||
setViewObjectId?.('company');
|
||||
setViewType?.(ViewType.Table);
|
||||
|
||||
setAvailableTableColumns(companiesAvailableFieldDefinitions);
|
||||
}, [
|
||||
setAvailableFields,
|
||||
setAvailableFilters,
|
||||
setAvailableSorts,
|
||||
setAvailableTableColumns,
|
||||
setTableColumns,
|
||||
setViewObjectId,
|
||||
setViewType,
|
||||
]);
|
||||
useEffect(() => {
|
||||
if (currentViewFields) {
|
||||
setTableColumns([...currentViewFields].sort((a, b) => a.index - b.index));
|
||||
}
|
||||
}, [currentViewFields, setTableColumns]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export default CompanyTableEffect;
|
||||
@ -1,12 +1,11 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { companiesAvailableFieldDefinitions } from '@/companies/constants/companiesAvailableFieldDefinitions';
|
||||
import { useSetDataTableData } from '@/ui/data/data-table/hooks/useSetDataTableData';
|
||||
import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumnsScopedState';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { companiesAvailableColumnDefinitions } from '../../constants/companiesAvailableColumnDefinitions';
|
||||
|
||||
import { mockedCompaniesData } from './companies-mock-data';
|
||||
|
||||
export const CompanyTableMockDataEffect = () => {
|
||||
@ -19,7 +18,7 @@ export const CompanyTableMockDataEffect = () => {
|
||||
useEffect(() => {
|
||||
setDataTableData(mockedCompaniesData, [], []);
|
||||
|
||||
setTableColumns(companiesAvailableColumnDefinitions);
|
||||
setTableColumns(companiesAvailableFieldDefinitions);
|
||||
}, [setDataTableData, setTableColumns]);
|
||||
|
||||
return <></>;
|
||||
|
||||
@ -1,22 +1,15 @@
|
||||
import { DataTable } from '@/ui/data/data-table/components/DataTable';
|
||||
import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { ViewBarContext } from '@/ui/data/view-bar/contexts/ViewBarContext';
|
||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||
import { useUpdateOneCompanyMutation } from '~/generated/graphql';
|
||||
|
||||
import { CompanyTableMockDataEffect } from './CompanyTableMockDataEffect';
|
||||
|
||||
export const CompanyTableMockMode = () => {
|
||||
return (
|
||||
<>
|
||||
<ViewScope viewScopeId="company-table-mock-mode">
|
||||
<CompanyTableMockDataEffect />
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
defaultViewName: 'All Companies',
|
||||
ViewBarRecoilScopeContext: TableRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<DataTable updateEntityMutation={useUpdateOneCompanyMutation} />
|
||||
</ViewBarContext.Provider>
|
||||
</>
|
||||
|
||||
<DataTable updateEntityMutation={useUpdateOneCompanyMutation} />
|
||||
</ViewScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -3,12 +3,8 @@ import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { currentViewIdScopedState } from '@/ui/data/view-bar/states/currentViewIdScopedState';
|
||||
import { filtersScopedState } from '@/ui/data/view-bar/states/filtersScopedState';
|
||||
import { savedFiltersFamilyState } from '@/ui/data/view-bar/states/savedFiltersFamilyState';
|
||||
import { savedSortsFamilyState } from '@/ui/data/view-bar/states/savedSortsFamilyState';
|
||||
import { sortsScopedState } from '@/ui/data/view-bar/states/sortsScopedState';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState';
|
||||
|
||||
import { useFindManyObjects } from '../hooks/useFindManyObjects';
|
||||
import { useSetObjectDataTableData } from '../hooks/useSetDataTableData';
|
||||
@ -39,22 +35,13 @@ export const ObjectDataTableEffect = ({
|
||||
({ set, snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const currentView = await snapshot.getPromise(
|
||||
currentViewIdScopedState(tableRecoilScopeId),
|
||||
currentViewIdScopedState({ scopeId: tableRecoilScopeId }),
|
||||
);
|
||||
if (currentView === viewId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const savedFilters = await snapshot.getPromise(
|
||||
savedFiltersFamilyState(viewId),
|
||||
);
|
||||
const savedSorts = await snapshot.getPromise(
|
||||
savedSortsFamilyState(viewId),
|
||||
);
|
||||
|
||||
set(filtersScopedState(tableRecoilScopeId), savedFilters);
|
||||
set(sortsScopedState(tableRecoilScopeId), savedSorts);
|
||||
set(currentViewIdScopedState(tableRecoilScopeId), viewId);
|
||||
set(currentViewIdScopedState({ scopeId: tableRecoilScopeId }), viewId);
|
||||
},
|
||||
[tableRecoilScopeId],
|
||||
);
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import { DataTable } from '@/ui/data/data-table/components/DataTable';
|
||||
import { TableContext } from '@/ui/data/data-table/contexts/TableContext';
|
||||
import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { ViewBarContext } from '@/ui/data/view-bar/contexts/ViewBarContext';
|
||||
|
||||
import { useMetadataTableViews } from '../hooks/useMetadataTableViews';
|
||||
import { useUpdateOneObject } from '../hooks/useUpdateOneObject';
|
||||
import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier';
|
||||
|
||||
@ -12,9 +9,6 @@ import { ObjectDataTableEffect } from './ObjectDataTableEffect';
|
||||
export type ObjectTableProps = MetadataObjectIdentifier;
|
||||
|
||||
export const ObjectTable = ({ objectNamePlural }: ObjectTableProps) => {
|
||||
const { createView, deleteView, submitCurrentView, updateView } =
|
||||
useMetadataTableViews();
|
||||
|
||||
const { updateOneObject } = useUpdateOneObject({
|
||||
objectNamePlural,
|
||||
});
|
||||
@ -44,18 +38,8 @@ export const ObjectTable = ({ objectNamePlural }: ObjectTableProps) => {
|
||||
}}
|
||||
>
|
||||
<ObjectDataTableEffect objectNamePlural={objectNamePlural} />
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
defaultViewName: `All ${objectNamePlural}`,
|
||||
onCurrentViewSubmit: submitCurrentView,
|
||||
onViewCreate: createView,
|
||||
onViewEdit: updateView,
|
||||
onViewRemove: deleteView,
|
||||
ViewBarRecoilScopeContext: TableRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<DataTable updateEntityMutation={updateEntity} />
|
||||
</ViewBarContext.Provider>
|
||||
|
||||
<DataTable updateEntityMutation={updateEntity} />
|
||||
</TableContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -6,7 +6,7 @@ import { FieldType } from '@/ui/data/field/types/FieldType';
|
||||
import { IconBrandLinkedin } from '@/ui/display/icon';
|
||||
import { GET_VIEW_FIELDS } from '@/views/graphql/queries/getViewFields';
|
||||
import { GET_VIEWS } from '@/views/graphql/queries/getViews';
|
||||
import { toViewFieldInput } from '@/views/hooks/useTableViewFields';
|
||||
import { toViewFieldInput } from '@/views/hooks/internal/useViewFields';
|
||||
import {
|
||||
useCreateViewFieldsMutation,
|
||||
useCreateViewMutation,
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumnsScopedState';
|
||||
import { filtersScopedState } from '@/ui/data/view-bar/states/filtersScopedState';
|
||||
import { sortsScopedState } from '@/ui/data/view-bar/states/sortsScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useTableViewFields } from '@/views/hooks/useTableViewFields';
|
||||
import { useViewFilters } from '@/views/hooks/useViewFilters';
|
||||
import { useViews } from '@/views/hooks/useViews';
|
||||
import { useViewSorts } from '@/views/hooks/useViewSorts';
|
||||
import { ViewType } from '~/generated/graphql';
|
||||
|
||||
import { useMetadataObjectInContext } from './useMetadataObjectInContext';
|
||||
|
||||
export const useMetadataTableViews = () => {
|
||||
const { objectNamePlural, columnDefinitions } = useMetadataObjectInContext();
|
||||
|
||||
const tableColumns = useRecoilScopedValue(
|
||||
tableColumnsScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const filters = useRecoilScopedValue(
|
||||
filtersScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const sorts = useRecoilScopedValue(sortsScopedState, TableRecoilScopeContext);
|
||||
|
||||
const [_, setSearchParams] = useSearchParams();
|
||||
|
||||
const handleViewCreate = async (viewId: string) => {
|
||||
await createViewFields(tableColumns, viewId);
|
||||
await createViewFilters(filters, viewId);
|
||||
await createViewSorts(sorts, viewId);
|
||||
setSearchParams({ view: viewId });
|
||||
};
|
||||
|
||||
const objectId = objectNamePlural;
|
||||
|
||||
const { createView, deleteView, isFetchingViews, updateView } = useViews({
|
||||
objectId,
|
||||
onViewCreate: handleViewCreate,
|
||||
type: ViewType.Table,
|
||||
RecoilScopeContext: TableRecoilScopeContext,
|
||||
});
|
||||
|
||||
const { createViewFields, persistColumns } = useTableViewFields({
|
||||
objectId,
|
||||
columnDefinitions,
|
||||
skipFetch: isFetchingViews,
|
||||
});
|
||||
|
||||
const createDefaultViewFields = async () => {
|
||||
await createViewFields(tableColumns);
|
||||
};
|
||||
|
||||
const { createViewFilters, persistFilters } = useViewFilters({
|
||||
RecoilScopeContext: TableRecoilScopeContext,
|
||||
skipFetch: isFetchingViews,
|
||||
});
|
||||
|
||||
const { createViewSorts, persistSorts } = useViewSorts({
|
||||
RecoilScopeContext: TableRecoilScopeContext,
|
||||
skipFetch: isFetchingViews,
|
||||
});
|
||||
|
||||
const submitCurrentView = async () => {
|
||||
await persistFilters();
|
||||
await persistSorts();
|
||||
};
|
||||
|
||||
return {
|
||||
createView,
|
||||
deleteView,
|
||||
persistColumns,
|
||||
submitCurrentView,
|
||||
updateView,
|
||||
createDefaultViewFields,
|
||||
isFetchingViews,
|
||||
};
|
||||
};
|
||||
@ -6,10 +6,8 @@ import { numberOfTableRowsState } from '@/ui/data/data-table/states/numberOfTabl
|
||||
import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { tableRowIdsState } from '@/ui/data/data-table/states/tableRowIdsState';
|
||||
import { entityFieldsFamilyState } from '@/ui/data/field/states/entityFieldsFamilyState';
|
||||
import { availableFiltersScopedState } from '@/ui/data/view-bar/states/availableFiltersScopedState';
|
||||
import { availableSortsScopedState } from '@/ui/data/view-bar/states/availableSortsScopedState';
|
||||
import { entityCountInCurrentViewState } from '@/ui/data/view-bar/states/entityCountInCurrentViewState';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
import { availableSortsScopedState } from '@/views/states/availableSortsScopedState';
|
||||
|
||||
export const useSetObjectDataTableData = () => {
|
||||
const resetTableRowSelection = useResetTableRowSelection();
|
||||
@ -43,11 +41,7 @@ export const useSetObjectDataTableData = () => {
|
||||
|
||||
set(numberOfTableRowsState, entityIds.length);
|
||||
|
||||
set(entityCountInCurrentViewState, entityIds.length);
|
||||
|
||||
set(availableFiltersScopedState(tableContextScopeId), []);
|
||||
|
||||
set(availableSortsScopedState(tableContextScopeId), []);
|
||||
set(availableSortsScopedState({ scopeId: tableContextScopeId }), []);
|
||||
|
||||
set(isFetchingDataTableDataState, false);
|
||||
},
|
||||
|
||||
@ -1,23 +1,10 @@
|
||||
import { useFilteredSearchPeopleQuery } from '@/people/hooks/useFilteredSearchPeopleQuery';
|
||||
import { FilterDropdownEntitySearchSelect } from '@/ui/data/view-bar/components/FilterDropdownEntitySearchSelect';
|
||||
import { useViewBarContext } from '@/ui/data/view-bar/hooks/useViewBarContext';
|
||||
import { filterDropdownSearchInputScopedState } from '@/ui/data/view-bar/states/filterDropdownSearchInputScopedState';
|
||||
import { filterDropdownSelectedEntityIdScopedState } from '@/ui/data/view-bar/states/filterDropdownSelectedEntityIdScopedState';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { FilterDropdownEntitySearchSelect } from '@/ui/data/filter/components/FilterDropdownEntitySearchSelect';
|
||||
import { useFilter } from '@/ui/data/filter/hooks/useFilter';
|
||||
|
||||
export const FilterDropdownPeopleSearchSelect = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const filterDropdownSearchInput = useRecoilScopedValue(
|
||||
filterDropdownSearchInputScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [filterDropdownSelectedEntityId] = useRecoilScopedState(
|
||||
filterDropdownSelectedEntityIdScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const { filterDropdownSearchInput, filterDropdownSelectedEntityId } =
|
||||
useFilter();
|
||||
|
||||
const peopleForSelect = useFilteredSearchPeopleQuery({
|
||||
searchFilter: filterDropdownSearchInput,
|
||||
|
||||
@ -4,13 +4,9 @@ import { useRecoilCallback } from 'recoil';
|
||||
import { useResetTableRowSelection } from '@/ui/data/data-table/hooks/useResetTableRowSelection';
|
||||
import { isFetchingDataTableDataState } from '@/ui/data/data-table/states/isFetchingDataTableDataState';
|
||||
import { numberOfTableRowsState } from '@/ui/data/data-table/states/numberOfTableRowsState';
|
||||
import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { tableRowIdsState } from '@/ui/data/data-table/states/tableRowIdsState';
|
||||
import { availableFiltersScopedState } from '@/ui/data/view-bar/states/availableFiltersScopedState';
|
||||
import { currentPageLocationState } from '@/ui/utilities/loading-state/states/currentPageLocationState';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
import { GetPeopleQuery } from '~/generated/graphql';
|
||||
import { peopleFilters } from '~/pages/people/people-filters';
|
||||
|
||||
import { peopleCityFamilyState } from '../states/peopleCityFamilyState';
|
||||
import { peopleCompanyFamilyState } from '../states/peopleCompanyFamilyState';
|
||||
@ -24,8 +20,6 @@ import { peoplePhoneFamilyState } from '../states/peoplePhoneFamilyState';
|
||||
export const useSetPeopleDataTable = () => {
|
||||
const resetTableRowSelection = useResetTableRowSelection();
|
||||
|
||||
const tableContextScopeId = useRecoilScopeId(TableRecoilScopeContext);
|
||||
|
||||
const currentLocation = useLocation().pathname;
|
||||
|
||||
return useRecoilCallback(
|
||||
@ -126,12 +120,10 @@ export const useSetPeopleDataTable = () => {
|
||||
|
||||
set(numberOfTableRowsState, peopleIds.length);
|
||||
|
||||
set(availableFiltersScopedState(tableContextScopeId), peopleFilters);
|
||||
|
||||
set(currentPageLocationState, currentLocation);
|
||||
|
||||
set(isFetchingDataTableDataState, false);
|
||||
},
|
||||
[currentLocation, resetTableRowSelection, tableContextScopeId],
|
||||
[currentLocation, resetTableRowSelection],
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
import { peopleAvailableColumnDefinitions } from '@/people/constants/peopleAvailableColumnDefinitions';
|
||||
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 { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { ViewBarContext } from '@/ui/data/view-bar/contexts/ViewBarContext';
|
||||
import { useTableViews } from '@/views/hooks/useTableViews';
|
||||
import { SortScope } from '@/ui/data/sort/scopes/SortScope';
|
||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||
import {
|
||||
UpdateOnePersonMutationVariables,
|
||||
useGetPeopleQuery,
|
||||
@ -21,68 +18,54 @@ import { peopleAvailableSorts } from '~/pages/people/people-sorts';
|
||||
export const PeopleTable = () => {
|
||||
const [updateEntityMutation] = useUpdateOnePersonMutation();
|
||||
const upsertDataTableItem = useUpsertDataTableItem();
|
||||
const { openPersonSpreadsheetImport } = useSpreadsheetPersonImport();
|
||||
|
||||
const {
|
||||
createView,
|
||||
deleteView,
|
||||
persistColumns,
|
||||
submitCurrentView,
|
||||
updateView,
|
||||
} = useTableViews({
|
||||
objectId: 'person',
|
||||
columnDefinitions: peopleAvailableColumnDefinitions,
|
||||
});
|
||||
const tableViewScopeId = 'people-table';
|
||||
|
||||
const { setContextMenuEntries } = usePersonTableContextMenuEntries();
|
||||
const { setActionBarEntries } = usePersonTableActionBarEntries();
|
||||
|
||||
const handleImport = () => {
|
||||
openPersonSpreadsheetImport();
|
||||
};
|
||||
|
||||
return (
|
||||
<TableContext.Provider value={{ onColumnsChange: persistColumns }}>
|
||||
<DataTableEffect
|
||||
getRequestResultKey="people"
|
||||
useGetRequest={useGetPeopleQuery}
|
||||
getRequestOptimisticEffectDefinition={
|
||||
getPeopleOptimisticEffectDefinition
|
||||
}
|
||||
filterDefinitionArray={peopleFilters}
|
||||
setContextMenuEntries={setContextMenuEntries}
|
||||
setActionBarEntries={setActionBarEntries}
|
||||
sortDefinitionArray={peopleAvailableSorts}
|
||||
/>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
defaultViewName: 'All People',
|
||||
onCurrentViewSubmit: submitCurrentView,
|
||||
onViewCreate: createView,
|
||||
onViewEdit: updateView,
|
||||
onViewRemove: deleteView,
|
||||
onImport: handleImport,
|
||||
ViewBarRecoilScopeContext: TableRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<DataTable
|
||||
updateEntityMutation={({
|
||||
variables,
|
||||
}: {
|
||||
variables: UpdateOnePersonMutationVariables;
|
||||
}) =>
|
||||
updateEntityMutation({
|
||||
<ViewScope viewScopeId={tableViewScopeId}>
|
||||
<SortScope sortScopeId={tableViewScopeId}>
|
||||
<TableContext.Provider
|
||||
value={{
|
||||
onColumnsChange: () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('persist columns');
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DataTableEffect
|
||||
getRequestResultKey="people"
|
||||
useGetRequest={useGetPeopleQuery}
|
||||
getRequestOptimisticEffectDefinition={
|
||||
getPeopleOptimisticEffectDefinition
|
||||
}
|
||||
filterDefinitionArray={peopleFilters}
|
||||
setContextMenuEntries={setContextMenuEntries}
|
||||
setActionBarEntries={setActionBarEntries}
|
||||
sortDefinitionArray={peopleAvailableSorts}
|
||||
/>
|
||||
|
||||
<DataTable
|
||||
updateEntityMutation={({
|
||||
variables,
|
||||
onCompleted: (data) => {
|
||||
if (!data.updateOnePerson) {
|
||||
return;
|
||||
}
|
||||
upsertDataTableItem(data.updateOnePerson);
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
</TableContext.Provider>
|
||||
}: {
|
||||
variables: UpdateOnePersonMutationVariables;
|
||||
}) =>
|
||||
updateEntityMutation({
|
||||
variables,
|
||||
onCompleted: (data) => {
|
||||
if (!data.updateOnePerson) {
|
||||
return;
|
||||
}
|
||||
upsertDataTableItem(data.updateOnePerson);
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</TableContext.Provider>
|
||||
</SortScope>
|
||||
</ViewScope>
|
||||
);
|
||||
};
|
||||
|
||||
@ -14,7 +14,6 @@ import { useLeaveTableFocus } from '../hooks/useLeaveTableFocus';
|
||||
import { useMapKeyboardToSoftFocus } from '../hooks/useMapKeyboardToSoftFocus';
|
||||
import { useResetTableRowSelection } from '../hooks/useResetTableRowSelection';
|
||||
import { useSetRowSelectedState } from '../hooks/useSetRowSelectedState';
|
||||
import { TableHeader } from '../table-header/components/TableHeader';
|
||||
import { TableHotkeyScope } from '../types/TableHotkeyScope';
|
||||
|
||||
import { DataTableBody } from './DataTableBody';
|
||||
@ -123,7 +122,6 @@ export const DataTable = ({ updateEntityMutation }: DataTableProps) => {
|
||||
<EntityUpdateMutationContext.Provider value={updateEntityMutation}>
|
||||
<StyledTableWithHeader>
|
||||
<StyledTableContainer ref={tableBodyRef}>
|
||||
<TableHeader />
|
||||
<ScrollWrapper>
|
||||
<div>
|
||||
<StyledTable className="entity-table-cell">
|
||||
|
||||
@ -1,27 +1,21 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import defaults from 'lodash/defaults';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
import { OptimisticEffectDefinition } from '@/apollo/optimistic-effect/types/OptimisticEffectDefinition';
|
||||
import { currentViewIdScopedState } from '@/ui/data/view-bar/states/currentViewIdScopedState';
|
||||
import { filtersScopedState } from '@/ui/data/view-bar/states/filtersScopedState';
|
||||
import { savedFiltersFamilyState } from '@/ui/data/view-bar/states/savedFiltersFamilyState';
|
||||
import { savedSortsFamilyState } from '@/ui/data/view-bar/states/savedSortsFamilyState';
|
||||
import { sortsScopedState } from '@/ui/data/view-bar/states/sortsScopedState';
|
||||
import { FilterDefinition } from '@/ui/data/view-bar/types/FilterDefinition';
|
||||
import { SortDefinition } from '@/ui/data/view-bar/types/SortDefinition';
|
||||
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { useViewInternalStates } from '@/views/hooks/useViewInternalStates';
|
||||
import {
|
||||
SortOrder,
|
||||
useGetCompaniesQuery,
|
||||
useGetPeopleQuery,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { filtersWhereScopedSelector } from '../../view-bar/states/selectors/filtersWhereScopedSelector';
|
||||
import { sortsOrderByScopedSelector } from '../../view-bar/states/selectors/sortsOrderByScopedSelector';
|
||||
import { filtersWhereScopedSelector } from '../../filter/states/selectors/filtersWhereScopedSelector';
|
||||
import { SortDefinition } from '../../sort/types/SortDefinition';
|
||||
import { useSetDataTableData } from '../hooks/useSetDataTableData';
|
||||
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
|
||||
@ -46,12 +40,10 @@ export const DataTableEffect = ({
|
||||
}) => {
|
||||
const setDataTableData = useSetDataTableData();
|
||||
const { registerOptimisticEffect } = useOptimisticEffect();
|
||||
const { setCurrentViewId } = useView();
|
||||
const { currentViewSortsOrderBy } = useViewInternalStates();
|
||||
|
||||
let sortsOrderBy = useRecoilScopedValue(
|
||||
sortsOrderByScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
sortsOrderBy = defaults(sortsOrderBy, [
|
||||
const sortsOrderBy = defaults(currentViewSortsOrderBy, [
|
||||
{
|
||||
createdAt: SortOrder.Desc,
|
||||
},
|
||||
@ -76,43 +68,19 @@ export const DataTableEffect = ({
|
||||
});
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const tableRecoilScopeId = useRecoilScopeId(TableRecoilScopeContext);
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const currentView = await snapshot.getPromise(
|
||||
currentViewIdScopedState(tableRecoilScopeId),
|
||||
);
|
||||
if (currentView === viewId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const savedFilters = await snapshot.getPromise(
|
||||
savedFiltersFamilyState(viewId),
|
||||
);
|
||||
const savedSorts = await snapshot.getPromise(
|
||||
savedSortsFamilyState(viewId),
|
||||
);
|
||||
|
||||
set(filtersScopedState(tableRecoilScopeId), savedFilters);
|
||||
set(sortsScopedState(tableRecoilScopeId), savedSorts);
|
||||
set(currentViewIdScopedState(tableRecoilScopeId), viewId);
|
||||
},
|
||||
[tableRecoilScopeId],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const viewId = searchParams.get('view');
|
||||
if (viewId) {
|
||||
handleViewSelect(viewId);
|
||||
//setCurrentViewId(viewId);
|
||||
}
|
||||
setActionBarEntries?.();
|
||||
setContextMenuEntries?.();
|
||||
}, [
|
||||
handleViewSelect,
|
||||
searchParams,
|
||||
setActionBarEntries,
|
||||
setContextMenuEntries,
|
||||
setCurrentViewId,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
|
||||
@ -166,33 +166,35 @@ export const DataTableHeader = () => {
|
||||
>
|
||||
<SelectAllCheckbox />
|
||||
</th>
|
||||
{visibleTableColumns.map((column, index) => (
|
||||
<StyledColumnHeaderCell
|
||||
key={column.key}
|
||||
isResizing={resizedFieldKey === column.key}
|
||||
columnWidth={Math.max(
|
||||
tableColumnsByKey[column.key].size +
|
||||
(resizedFieldKey === column.key ? resizeFieldOffset : 0),
|
||||
COLUMN_MIN_WIDTH,
|
||||
)}
|
||||
>
|
||||
<StyledColumnHeadContainer>
|
||||
<ColumnHeadWithDropdown
|
||||
column={column}
|
||||
isFirstColumn={index === 1}
|
||||
isLastColumn={index === visibleTableColumns.length - 1}
|
||||
primaryColumnKey={primaryColumn.key}
|
||||
{[...visibleTableColumns]
|
||||
.sort((columnA, columnB) => columnA.index - columnB.index)
|
||||
.map((column, index) => (
|
||||
<StyledColumnHeaderCell
|
||||
key={column.key}
|
||||
isResizing={resizedFieldKey === column.key}
|
||||
columnWidth={Math.max(
|
||||
tableColumnsByKey[column.key].size +
|
||||
(resizedFieldKey === column.key ? resizeFieldOffset : 0),
|
||||
COLUMN_MIN_WIDTH,
|
||||
)}
|
||||
>
|
||||
<StyledColumnHeadContainer>
|
||||
<ColumnHeadWithDropdown
|
||||
column={column}
|
||||
isFirstColumn={index === 1}
|
||||
isLastColumn={index === visibleTableColumns.length - 1}
|
||||
primaryColumnKey={primaryColumn.key}
|
||||
/>
|
||||
</StyledColumnHeadContainer>
|
||||
<StyledResizeHandler
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
onPointerDown={() => {
|
||||
setResizedFieldKey(column.key);
|
||||
}}
|
||||
/>
|
||||
</StyledColumnHeadContainer>
|
||||
<StyledResizeHandler
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
onPointerDown={() => {
|
||||
setResizedFieldKey(column.key);
|
||||
}}
|
||||
/>
|
||||
</StyledColumnHeaderCell>
|
||||
))}
|
||||
</StyledColumnHeaderCell>
|
||||
))}
|
||||
<th>
|
||||
{hiddenTableColumns.length > 0 && (
|
||||
<StyledColumnHeadContainer>
|
||||
|
||||
@ -38,13 +38,15 @@ export const DataTableRow = forwardRef<HTMLTableRowElement, DataTableRowProps>(
|
||||
<td>
|
||||
<CheckboxCell />
|
||||
</td>
|
||||
{visibleTableColumns.map((column, columnIndex) => {
|
||||
return (
|
||||
<ColumnContext.Provider value={column} key={column.key}>
|
||||
<DataTableCell cellIndex={columnIndex} />
|
||||
</ColumnContext.Provider>
|
||||
);
|
||||
})}
|
||||
{[...visibleTableColumns]
|
||||
.sort((columnA, columnB) => columnA.index - columnB.index)
|
||||
.map((column, columnIndex) => {
|
||||
return (
|
||||
<ColumnContext.Provider value={column} key={column.key}>
|
||||
<DataTableCell cellIndex={columnIndex} />
|
||||
</ColumnContext.Provider>
|
||||
);
|
||||
})}
|
||||
<td></td>
|
||||
</StyledRow>
|
||||
);
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { entityFieldsFamilyState } from '@/ui/data/field/states/entityFieldsFamilyState';
|
||||
import { availableFiltersScopedState } from '@/ui/data/view-bar/states/availableFiltersScopedState';
|
||||
import { availableSortsScopedState } from '@/ui/data/view-bar/states/availableSortsScopedState';
|
||||
import { entityCountInCurrentViewState } from '@/ui/data/view-bar/states/entityCountInCurrentViewState';
|
||||
import { FilterDefinition } from '@/ui/data/view-bar/types/FilterDefinition';
|
||||
import { SortDefinition } from '@/ui/data/view-bar/types/SortDefinition';
|
||||
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 { SortDefinition } from '../../sort/types/SortDefinition';
|
||||
import { isFetchingDataTableDataState } from '../states/isFetchingDataTableDataState';
|
||||
import { numberOfTableRowsState } from '../states/numberOfTableRowsState';
|
||||
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
@ -17,6 +16,7 @@ import { useResetTableRowSelection } from './useResetTableRowSelection';
|
||||
|
||||
export const useSetDataTableData = () => {
|
||||
const resetTableRowSelection = useResetTableRowSelection();
|
||||
const { setEntityCountInCurrentView } = useView();
|
||||
|
||||
const tableContextScopeId = useRecoilScopeId(TableRecoilScopeContext);
|
||||
|
||||
@ -51,20 +51,15 @@ export const useSetDataTableData = () => {
|
||||
|
||||
set(numberOfTableRowsState, entityIds.length);
|
||||
|
||||
set(entityCountInCurrentViewState, entityIds.length);
|
||||
setEntityCountInCurrentView(entityIds.length);
|
||||
|
||||
set(
|
||||
availableFiltersScopedState(tableContextScopeId),
|
||||
filterDefinitionArray,
|
||||
);
|
||||
|
||||
set(
|
||||
availableSortsScopedState(tableContextScopeId),
|
||||
availableSortsScopedState({ scopeId: tableContextScopeId }),
|
||||
sortDefinitionArray,
|
||||
);
|
||||
|
||||
set(isFetchingDataTableDataState, false);
|
||||
},
|
||||
[resetTableRowSelection, tableContextScopeId],
|
||||
[resetTableRowSelection, setEntityCountInCurrentView, tableContextScopeId],
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import { useCallback, useContext } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useMoveViewColumns } from '@/ui/data/data-table/hooks/useMoveViewColumns';
|
||||
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
|
||||
import { currentViewIdScopedState } from '@/ui/data/view-bar/states/currentViewIdScopedState';
|
||||
import { ViewFieldForVisibility } from '@/ui/data/view-bar/types/ViewFieldForVisibility';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useMoveViewColumns } from '@/views/hooks/useMoveViewColumns';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { ViewFieldForVisibility } from '@/views/types/ViewFieldForVisibility';
|
||||
|
||||
import { TableContext } from '../contexts/TableContext';
|
||||
import { availableTableColumnsScopedState } from '../states/availableTableColumnsScopedState';
|
||||
@ -23,10 +22,8 @@ export const useTableColumns = () => {
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
currentViewIdScopedState,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const { currentViewId } = useView();
|
||||
|
||||
const setSavedTableColumns = useSetRecoilState(
|
||||
savedTableColumnsFamilyState(currentViewId),
|
||||
);
|
||||
@ -91,7 +88,10 @@ export const useTableColumns = () => {
|
||||
);
|
||||
|
||||
const handleMoveTableColumn = useCallback(
|
||||
(direction: 'left' | 'right', column: ColumnDefinition<FieldMetadata>) => {
|
||||
async (
|
||||
direction: 'left' | 'right',
|
||||
column: ColumnDefinition<FieldMetadata>,
|
||||
) => {
|
||||
const currentColumnArrayIndex = tableColumns.findIndex(
|
||||
(tableColumn) => tableColumn.key === column.key,
|
||||
);
|
||||
@ -101,9 +101,9 @@ export const useTableColumns = () => {
|
||||
tableColumns,
|
||||
);
|
||||
|
||||
setTableColumns(columns);
|
||||
await handleColumnsChange(columns);
|
||||
},
|
||||
[tableColumns, setTableColumns, handleColumnMove],
|
||||
[tableColumns, handleColumnMove, handleColumnsChange],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import { useResetRecoilState } from 'recoil';
|
||||
|
||||
import { viewEditModeState } from '@/ui/data/view-bar/states/viewEditModeState';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
|
||||
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
|
||||
|
||||
@ -17,7 +15,7 @@ type TableOptionsDropdownProps = {
|
||||
export const TableOptionsDropdown = ({
|
||||
customHotkeyScope,
|
||||
}: TableOptionsDropdownProps) => {
|
||||
const resetViewEditMode = useResetRecoilState(viewEditModeState);
|
||||
const { setViewEditMode } = useView();
|
||||
|
||||
return (
|
||||
<DropdownScope dropdownScopeId={TableOptionsDropdownId}>
|
||||
@ -26,7 +24,7 @@ export const TableOptionsDropdown = ({
|
||||
dropdownHotkeyScope={customHotkeyScope}
|
||||
dropdownOffset={{ y: 8 }}
|
||||
dropdownComponents={<TableOptionsDropdownContent />}
|
||||
onClickOutside={resetViewEditMode}
|
||||
onClickOutside={() => setViewEditMode('none')}
|
||||
/>
|
||||
</DropdownScope>
|
||||
);
|
||||
|
||||
@ -1,15 +1,8 @@
|
||||
import { useCallback, useContext, useRef, useState } from 'react';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { OnDragEndResponder } from '@hello-pangea/dnd';
|
||||
import { useRecoilCallback, useRecoilValue, useResetRecoilState } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/ui/data/view-bar/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { ViewBarContext } from '@/ui/data/view-bar/contexts/ViewBarContext';
|
||||
import { useUpsertView } from '@/ui/data/view-bar/hooks/useUpsertView';
|
||||
import { currentViewScopedSelector } from '@/ui/data/view-bar/states/selectors/currentViewScopedSelector';
|
||||
import { viewsByIdScopedSelector } from '@/ui/data/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||
import { viewEditModeState } from '@/ui/data/view-bar/states/viewEditModeState';
|
||||
import { IconChevronLeft, IconFileImport, IconTag } from '@/ui/display/icon';
|
||||
import { IconChevronLeft, IconTag } from '@/ui/display/icon';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
@ -18,22 +11,22 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
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 { useTableColumns } from '../../hooks/useTableColumns';
|
||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { savedTableColumnsFamilyState } from '../../states/savedTableColumnsFamilyState';
|
||||
import { hiddenTableColumnsScopedSelector } from '../../states/selectors/hiddenTableColumnsScopedSelector';
|
||||
import { visibleTableColumnsScopedSelector } from '../../states/selectors/visibleTableColumnsScopedSelector';
|
||||
import { tableColumnsScopedState } from '../../states/tableColumnsScopedState';
|
||||
import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
|
||||
|
||||
type TableOptionsMenu = 'fields';
|
||||
|
||||
export const TableOptionsDropdownContent = () => {
|
||||
const scopeId = useRecoilScopeId(TableRecoilScopeContext);
|
||||
const { setViewEditMode, handleViewNameSubmit } = useView();
|
||||
const { viewEditMode, currentView } = useViewInternalStates();
|
||||
|
||||
const { onImport } = useContext(ViewBarContext);
|
||||
const { closeDropdown } = useDropdown();
|
||||
|
||||
const [currentMenu, setCurrentMenu] = useState<TableOptionsMenu | undefined>(
|
||||
@ -42,12 +35,6 @@ export const TableOptionsDropdownContent = () => {
|
||||
|
||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const currentView = useRecoilScopedValue(
|
||||
currentViewScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const viewEditMode = useRecoilValue(viewEditModeState);
|
||||
const resetViewEditMode = useResetRecoilState(viewEditModeState);
|
||||
const visibleTableColumns = useRecoilScopedValue(
|
||||
visibleTableColumnsScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
@ -56,35 +43,13 @@ export const TableOptionsDropdownContent = () => {
|
||||
hiddenTableColumnsScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
const viewsById = useRecoilScopedValue(
|
||||
viewsByIdScopedSelector,
|
||||
TableRecoilScopeContext,
|
||||
);
|
||||
|
||||
const { handleColumnVisibilityChange, handleColumnReorder } =
|
||||
useTableColumns();
|
||||
|
||||
const { upsertView } = useUpsertView();
|
||||
|
||||
const handleViewNameSubmit = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async () => {
|
||||
const tableColumns = await snapshot.getPromise(
|
||||
tableColumnsScopedState(scopeId),
|
||||
);
|
||||
const isCreateMode = viewEditMode.mode === 'create';
|
||||
const name = viewEditInputRef.current?.value;
|
||||
const view = await upsertView(name);
|
||||
|
||||
if (view && isCreateMode) {
|
||||
set(savedTableColumnsFamilyState(view.id), tableColumns);
|
||||
}
|
||||
},
|
||||
[scopeId, upsertView, viewEditMode.mode],
|
||||
);
|
||||
|
||||
const handleSelectMenu = (option: TableOptionsMenu) => {
|
||||
handleViewNameSubmit();
|
||||
const name = viewEditInputRef.current?.value;
|
||||
handleViewNameSubmit(name);
|
||||
setCurrentMenu(option);
|
||||
};
|
||||
|
||||
@ -108,7 +73,6 @@ export const TableOptionsDropdownContent = () => {
|
||||
useScopedHotkeys(
|
||||
Key.Escape,
|
||||
() => {
|
||||
resetViewEditMode();
|
||||
closeDropdown();
|
||||
},
|
||||
TableOptionsHotkeyScope.Dropdown,
|
||||
@ -117,9 +81,10 @@ export const TableOptionsDropdownContent = () => {
|
||||
useScopedHotkeys(
|
||||
Key.Enter,
|
||||
() => {
|
||||
handleViewNameSubmit();
|
||||
const name = viewEditInputRef.current?.value;
|
||||
handleViewNameSubmit(name);
|
||||
resetMenu();
|
||||
resetViewEditMode();
|
||||
setViewEditMode('none');
|
||||
closeDropdown();
|
||||
},
|
||||
TableOptionsHotkeyScope.Dropdown,
|
||||
@ -130,16 +95,21 @@ export const TableOptionsDropdownContent = () => {
|
||||
{!currentMenu && (
|
||||
<>
|
||||
<DropdownMenuInput
|
||||
autoFocus={viewEditMode.mode === 'create' || !!viewEditMode.viewId}
|
||||
ref={viewEditInputRef}
|
||||
autoFocus={viewEditMode !== 'none'}
|
||||
placeholder={
|
||||
viewEditMode.mode === 'create' ? 'New view' : 'View name'
|
||||
viewEditMode === 'create'
|
||||
? 'New view'
|
||||
: viewEditMode === 'edit'
|
||||
? 'View name'
|
||||
: ''
|
||||
}
|
||||
defaultValue={
|
||||
viewEditMode.mode === 'create'
|
||||
viewEditMode === 'create'
|
||||
? ''
|
||||
: viewEditMode.viewId
|
||||
? viewsById[viewEditMode.viewId]?.name
|
||||
: currentView?.name
|
||||
: viewEditMode === 'edit'
|
||||
? currentView?.name
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
<DropdownMenuSeparator />
|
||||
@ -149,13 +119,13 @@ export const TableOptionsDropdownContent = () => {
|
||||
LeftIcon={IconTag}
|
||||
text="Fields"
|
||||
/>
|
||||
{onImport && (
|
||||
{/*onImport && (
|
||||
<MenuItem
|
||||
onClick={onImport}
|
||||
LeftIcon={IconFileImport}
|
||||
text="Import"
|
||||
/>
|
||||
)}
|
||||
)*/}
|
||||
</DropdownMenuItemsContainer>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, within } from '@storybook/testing-library';
|
||||
|
||||
import { ViewBarContext } from '@/ui/data/view-bar/contexts/ViewBarContext';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
|
||||
import { TableRecoilScopeContext } from '../../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { TableOptionsDropdown } from '../TableOptionsDropdown';
|
||||
|
||||
const meta: Meta<typeof TableOptionsDropdown> = {
|
||||
title: 'UI/Data/DataTable/Options/TableOptionsDropdown',
|
||||
component: TableOptionsDropdown,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<RecoilScope CustomRecoilScopeContext={TableRecoilScopeContext}>
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
ViewBarRecoilScopeContext: TableRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</ViewBarContext.Provider>
|
||||
</RecoilScope>
|
||||
),
|
||||
ComponentDecorator,
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof TableOptionsDropdown>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
customHotkeyScope: { scope: 'options' },
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const dropdownButton = canvas.getByText('Options');
|
||||
|
||||
await userEvent.click(dropdownButton);
|
||||
},
|
||||
};
|
||||
@ -1,52 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { ViewBar } from '@/ui/data/view-bar/components/ViewBar';
|
||||
import { ViewBarContext } from '@/ui/data/view-bar/contexts/ViewBarContext';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
|
||||
import { TableOptionsDropdownId } from '../../constants/TableOptionsDropdownId';
|
||||
import { TableOptionsDropdown } from '../../options/components/TableOptionsDropdown';
|
||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||
import { savedTableColumnsFamilyState } from '../../states/savedTableColumnsFamilyState';
|
||||
import { tableColumnsScopedState } from '../../states/tableColumnsScopedState';
|
||||
import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
|
||||
|
||||
export const TableHeader = () => {
|
||||
const { onCurrentViewSubmit, ...viewBarContextProps } =
|
||||
useContext(ViewBarContext);
|
||||
const tableRecoilScopeId = useRecoilScopeId(TableRecoilScopeContext);
|
||||
const [_, setSearchParams] = useSearchParams();
|
||||
|
||||
const handleViewSelect = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const savedTableColumns = await snapshot.getPromise(
|
||||
savedTableColumnsFamilyState(viewId),
|
||||
);
|
||||
set(tableColumnsScopedState(tableRecoilScopeId), savedTableColumns);
|
||||
setSearchParams({ view: viewId });
|
||||
},
|
||||
[tableRecoilScopeId, setSearchParams],
|
||||
);
|
||||
|
||||
return (
|
||||
<ViewBarContext.Provider
|
||||
value={{
|
||||
...viewBarContextProps,
|
||||
onCurrentViewSubmit,
|
||||
onViewSelect: handleViewSelect,
|
||||
}}
|
||||
>
|
||||
<ViewBar
|
||||
optionsDropdownButton={
|
||||
<TableOptionsDropdown
|
||||
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
||||
/>
|
||||
}
|
||||
optionsDropdownScopeId={TableOptionsDropdownId}
|
||||
/>
|
||||
</ViewBarContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -1,8 +1,6 @@
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { availableFiltersScopedState } from '../states/availableFiltersScopedState';
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
|
||||
import { MultipleFiltersDropdownButton } from './MultipleFiltersDropdownButton';
|
||||
import { SingleEntityFilterDropdownButton } from './SingleEntityFilterDropdownButton';
|
||||
@ -14,12 +12,7 @@ type FilterDropdownButtonProps = {
|
||||
export const FilterDropdownButton = ({
|
||||
hotkeyScope,
|
||||
}: FilterDropdownButtonProps) => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [availableFilters] = useRecoilScopedState(
|
||||
availableFiltersScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const { availableFilters } = useFilter();
|
||||
|
||||
const hasOnlyOneEntityFilter =
|
||||
availableFilters.length === 1 && availableFilters[0].type === 'entity';
|
||||
@ -0,0 +1,36 @@
|
||||
import { InternalDatePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
|
||||
import { useUpsertFilter } from '@/views/hooks/useUpsertFilter';
|
||||
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
|
||||
export const FilterDropdownDateSearchInput = () => {
|
||||
const {
|
||||
filterDefinitionUsedInDropdown,
|
||||
selectedOperandInDropdown,
|
||||
setIsFilterDropdownUnfolded,
|
||||
} = useFilter();
|
||||
|
||||
const upsertFilter = useUpsertFilter();
|
||||
|
||||
const handleChange = (date: Date) => {
|
||||
if (!filterDefinitionUsedInDropdown || !selectedOperandInDropdown) return;
|
||||
|
||||
upsertFilter({
|
||||
key: filterDefinitionUsedInDropdown.key,
|
||||
type: filterDefinitionUsedInDropdown.type,
|
||||
value: date.toISOString(),
|
||||
operand: selectedOperandInDropdown,
|
||||
displayValue: date.toLocaleDateString(),
|
||||
});
|
||||
|
||||
setIsFilterDropdownUnfolded(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<InternalDatePicker
|
||||
date={new Date()}
|
||||
onChange={handleChange}
|
||||
onMouseSelect={handleChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,29 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
|
||||
export const FilterDropdownEntitySearchInput = () => {
|
||||
const {
|
||||
filterDefinitionUsedInDropdown,
|
||||
selectedOperandInDropdown,
|
||||
filterDropdownSearchInput,
|
||||
setFilterDropdownSearchInput,
|
||||
} = useFilter();
|
||||
|
||||
return (
|
||||
filterDefinitionUsedInDropdown &&
|
||||
selectedOperandInDropdown && (
|
||||
<DropdownMenuSearchInput
|
||||
autoFocus
|
||||
type="text"
|
||||
value={filterDropdownSearchInput}
|
||||
placeholder={filterDefinitionUsedInDropdown.label}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setFilterDropdownSearchInput(event.target.value);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
};
|
||||
@ -1,45 +1,30 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { useFilterCurrentlyEdited } from '@/ui/data/view-bar/hooks/useFilterCurrentlyEdited';
|
||||
import { useRemoveFilter } from '@/ui/data/view-bar/hooks/useRemoveFilter';
|
||||
import { useUpsertFilter } from '@/ui/data/view-bar/hooks/useUpsertFilter';
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '@/ui/data/view-bar/states/filterDefinitionUsedInDropdownScopedState';
|
||||
import { filterDropdownSelectedEntityIdScopedState } from '@/ui/data/view-bar/states/filterDropdownSelectedEntityIdScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '@/ui/data/view-bar/states/selectedOperandInDropdownScopedState';
|
||||
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 { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRemoveFilter } from '@/views/hooks/useRemoveFilter';
|
||||
import { useUpsertFilter } from '@/views/hooks/useUpsertFilter';
|
||||
import { ViewFilterOperand } from '~/generated/graphql';
|
||||
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { filterDropdownSearchInputScopedState } from '../states/filterDropdownSearchInputScopedState';
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
|
||||
export const FilterDropdownEntitySearchSelect = ({
|
||||
entitiesForSelect,
|
||||
}: {
|
||||
entitiesForSelect: EntitiesForMultipleEntitySelect<EntityForSelect>;
|
||||
}) => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
const {
|
||||
filterDropdownSelectedEntityId,
|
||||
setFilterDropdownSelectedEntityId,
|
||||
filterDefinitionUsedInDropdown,
|
||||
selectedOperandInDropdown,
|
||||
filterDropdownSearchInput,
|
||||
} = useFilter();
|
||||
|
||||
const [isAllEntitySelected, setIsAllEntitySelected] = useState(false);
|
||||
|
||||
const [filterDropdownSelectedEntityId, setFilterDropdownSelectedEntityId] =
|
||||
useRecoilScopedState(
|
||||
filterDropdownSelectedEntityIdScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [selectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [filterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const upsertFilter = useUpsertFilter();
|
||||
const removeFilter = useRemoveFilter();
|
||||
|
||||
@ -80,11 +65,6 @@ export const FilterDropdownEntitySearchSelect = ({
|
||||
}
|
||||
};
|
||||
|
||||
const [filterDropdownSearchInput] = useRecoilScopedState(
|
||||
filterDropdownSearchInputScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const isAllEntitySelectShown =
|
||||
!!filterDefinitionUsedInDropdown?.selectAllLabel &&
|
||||
!!filterDefinitionUsedInDropdown?.SelectAllIcon &&
|
||||
@ -0,0 +1,21 @@
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
|
||||
export const FilterDropdownEntitySelect = () => {
|
||||
const { filterDefinitionUsedInDropdown } = useFilter();
|
||||
|
||||
if (filterDefinitionUsedInDropdown?.type !== 'entity') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<RecoilScope>
|
||||
{filterDefinitionUsedInDropdown.entitySelectComponent}
|
||||
</RecoilScope>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -2,38 +2,17 @@ import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/Rela
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { availableFiltersScopedState } from '../states/availableFiltersScopedState';
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
|
||||
import { filterDropdownSearchInputScopedState } from '../states/filterDropdownSearchInputScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
|
||||
|
||||
export const FilterDropdownFilterSelect = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [, setFilterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setSelectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setFilterDropdownSearchInput] = useRecoilScopedState(
|
||||
filterDropdownSearchInputScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const availableFilters = useRecoilScopedValue(
|
||||
availableFiltersScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const {
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
setFilterDropdownSearchInput,
|
||||
availableFilters,
|
||||
} = useFilter();
|
||||
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
@ -1,26 +1,14 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { useRemoveFilter } from '../hooks/useRemoveFilter';
|
||||
import { useUpsertFilter } from '../hooks/useUpsertFilter';
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
||||
import { useRemoveFilter } from '../../../../views/hooks/useRemoveFilter';
|
||||
import { useUpsertFilter } from '../../../../views/hooks/useUpsertFilter';
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
|
||||
export const FilterDropdownNumberSearchInput = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [filterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [selectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const { selectedOperandInDropdown, filterDefinitionUsedInDropdown } =
|
||||
useFilter();
|
||||
|
||||
const upsertFilter = useUpsertFilter();
|
||||
const removeFilter = useRemoveFilter();
|
||||
@ -0,0 +1,27 @@
|
||||
import { IconChevronDown } from '@/ui/display/icon';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
import { getOperandLabel } from '../utils/getOperandLabel';
|
||||
|
||||
export const FilterDropdownOperandButton = () => {
|
||||
const {
|
||||
selectedOperandInDropdown,
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
isFilterDropdownOperandSelectUnfolded,
|
||||
} = useFilter();
|
||||
|
||||
if (isFilterDropdownOperandSelectUnfolded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenuHeader
|
||||
key={'selected-filter-operand'}
|
||||
EndIcon={IconChevronDown}
|
||||
onClick={() => setIsFilterDropdownOperandSelectUnfolded(true)}
|
||||
>
|
||||
{getOperandLabel(selectedOperandInDropdown)}
|
||||
</DropdownMenuHeader>
|
||||
);
|
||||
};
|
||||
@ -1,42 +1,25 @@
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { ViewFilterOperand } from '~/generated/graphql';
|
||||
|
||||
import { useUpsertFilter } from '../../../../views/hooks/useUpsertFilter';
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
import { useFilterCurrentlyEdited } from '../hooks/useFilterCurrentlyEdited';
|
||||
import { useUpsertFilter } from '../hooks/useUpsertFilter';
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
|
||||
import { isFilterDropdownOperandSelectUnfoldedScopedState } from '../states/isFilterDropdownOperandSelectUnfoldedScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
||||
import { getOperandLabel } from '../utils/getOperandLabel';
|
||||
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
|
||||
|
||||
export const FilterDropdownOperandSelect = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [filterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setSelectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const {
|
||||
filterDefinitionUsedInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
isFilterDropdownOperandSelectUnfolded,
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
} = useFilter();
|
||||
|
||||
const operandsForFilterType = getOperandsForFilterType(
|
||||
filterDefinitionUsedInDropdown?.type,
|
||||
);
|
||||
|
||||
const [
|
||||
isFilterDropdownOperandSelectUnfolded,
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
] = useRecoilScopedState(
|
||||
isFilterDropdownOperandSelectUnfoldedScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const filterCurrentlyEdited = useFilterCurrentlyEdited();
|
||||
|
||||
const upsertFilter = useUpsertFilter();
|
||||
@ -1,34 +1,19 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { useRemoveFilter } from '../../../../views/hooks/useRemoveFilter';
|
||||
import { useUpsertFilter } from '../../../../views/hooks/useUpsertFilter';
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
import { useFilterCurrentlyEdited } from '../hooks/useFilterCurrentlyEdited';
|
||||
import { useRemoveFilter } from '../hooks/useRemoveFilter';
|
||||
import { useUpsertFilter } from '../hooks/useUpsertFilter';
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
|
||||
import { filterDropdownSearchInputScopedState } from '../states/filterDropdownSearchInputScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
||||
|
||||
export const FilterDropdownTextSearchInput = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [filterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [selectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [filterDropdownSearchInput, setFilterDropdownSearchInput] =
|
||||
useRecoilScopedState(
|
||||
filterDropdownSearchInputScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const {
|
||||
filterDefinitionUsedInDropdown,
|
||||
selectedOperandInDropdown,
|
||||
filterDropdownSearchInput,
|
||||
setFilterDropdownSearchInput,
|
||||
} = useFilter();
|
||||
|
||||
const upsertFilter = useUpsertFilter();
|
||||
const removeFilter = useRemoveFilter();
|
||||
@ -0,0 +1,39 @@
|
||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
|
||||
import { FilterDropdownId } from '../constants/FilterDropdownId';
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
|
||||
export const MultipleFiltersButton = () => {
|
||||
const {
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
setFilterDropdownSearchInput,
|
||||
setSelectedOperandInDropdown,
|
||||
} = useFilter();
|
||||
|
||||
const { isDropdownOpen, toggleDropdown } = useDropdown({
|
||||
dropdownScopeId: FilterDropdownId,
|
||||
});
|
||||
|
||||
const resetState = () => {
|
||||
setIsFilterDropdownOperandSelectUnfolded(false);
|
||||
setFilterDefinitionUsedInDropdown(null);
|
||||
setSelectedOperandInDropdown(null);
|
||||
setFilterDropdownSearchInput('');
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
toggleDropdown();
|
||||
resetState();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledHeaderDropdownButton
|
||||
isUnfolded={isDropdownOpen}
|
||||
onClick={handleClick}
|
||||
>
|
||||
Filter
|
||||
</StyledHeaderDropdownButton>
|
||||
);
|
||||
};
|
||||
@ -1,10 +1,6 @@
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
|
||||
import { isFilterDropdownOperandSelectUnfoldedScopedState } from '../states/isFilterDropdownOperandSelectUnfoldedScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
|
||||
import { FilterDropdownDateSearchInput } from './FilterDropdownDateSearchInput';
|
||||
import { FilterDropdownEntitySearchInput } from './FilterDropdownEntitySearchInput';
|
||||
@ -17,22 +13,11 @@ import { FilterDropdownTextSearchInput } from './FilterDropdownTextSearchInput';
|
||||
import { MultipleFiltersDropdownFilterOnFilterChangedEffect } from './MultipleFiltersDropdownFilterOnFilterChangedEffect';
|
||||
|
||||
export const MultipleFiltersDropdownContent = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [isFilterDropdownOperandSelectUnfolded] = useRecoilScopedState(
|
||||
isFilterDropdownOperandSelectUnfoldedScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [filterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [selectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const {
|
||||
isFilterDropdownOperandSelectUnfolded,
|
||||
filterDefinitionUsedInDropdown,
|
||||
selectedOperandInDropdown,
|
||||
} = useFilter();
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -6,14 +6,9 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { ViewFilterOperand } from '~/generated/graphql';
|
||||
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { availableFiltersScopedState } from '../states/availableFiltersScopedState';
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
|
||||
import { filtersScopedState } from '../states/filtersScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
||||
import { useFilter } from '../hooks/useFilter';
|
||||
import { getOperandsForFilterType } from '../utils/getOperandsForFilterType';
|
||||
|
||||
import { FilterDropdownEntitySearchInput } from './FilterDropdownEntitySearchInput';
|
||||
@ -25,29 +20,15 @@ export const SingleEntityFilterDropdownButton = ({
|
||||
}: {
|
||||
hotkeyScope: HotkeyScope;
|
||||
}) => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
const {
|
||||
availableFilters,
|
||||
selectedFilters,
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
} = useFilter();
|
||||
|
||||
const [availableFilters] = useRecoilScopedState(
|
||||
availableFiltersScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const availableFilter = availableFilters[0];
|
||||
|
||||
const [filters] = useRecoilScopedState(
|
||||
filtersScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setFilterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setSelectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
setFilterDefinitionUsedInDropdown(availableFilter);
|
||||
const defaultOperand = getOperandsForFilterType(availableFilter?.type)[0];
|
||||
@ -67,11 +48,11 @@ export const SingleEntityFilterDropdownButton = ({
|
||||
dropdownOffset={{ x: 0, y: -28 }}
|
||||
clickableComponent={
|
||||
<StyledHeaderDropdownButton>
|
||||
{filters[0] ? (
|
||||
{selectedFilters[0] ? (
|
||||
<GenericEntityFilterChip
|
||||
filter={filters[0]}
|
||||
filter={selectedFilters[0]}
|
||||
Icon={
|
||||
filters[0].operand === ViewFilterOperand.IsNotNull
|
||||
selectedFilters[0].operand === ViewFilterOperand.IsNotNull
|
||||
? availableFilter.SelectAllIcon
|
||||
: undefined
|
||||
}
|
||||
54
front/src/modules/ui/data/filter/hooks/useFilter.ts
Normal file
54
front/src/modules/ui/data/filter/hooks/useFilter.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
import { FilterScopeInternalContext } from '../scopes/scope-internal-context/FilterScopeInternalContext';
|
||||
|
||||
import { useFilterStates } from './useFilterStates';
|
||||
|
||||
type UseFilterProps = {
|
||||
filterScopeId?: string;
|
||||
};
|
||||
|
||||
export const useFilter = (props?: UseFilterProps) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
FilterScopeInternalContext,
|
||||
props?.filterScopeId,
|
||||
);
|
||||
const {
|
||||
availableFilters,
|
||||
setAvailableFilters,
|
||||
filterDefinitionUsedInDropdown,
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
filterDropdownSearchInput,
|
||||
setFilterDropdownSearchInput,
|
||||
filterDropdownSelectedEntityId,
|
||||
setFilterDropdownSelectedEntityId,
|
||||
isFilterDropdownOperandSelectUnfolded,
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
isFilterDropdownUnfolded,
|
||||
setIsFilterDropdownUnfolded,
|
||||
selectedFilters,
|
||||
setSelectedFilters,
|
||||
selectedOperandInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
} = useFilterStates(scopeId);
|
||||
|
||||
return {
|
||||
scopeId,
|
||||
availableFilters,
|
||||
setAvailableFilters,
|
||||
filterDefinitionUsedInDropdown,
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
filterDropdownSearchInput,
|
||||
setFilterDropdownSearchInput,
|
||||
filterDropdownSelectedEntityId,
|
||||
setFilterDropdownSelectedEntityId,
|
||||
isFilterDropdownOperandSelectUnfolded,
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
isFilterDropdownUnfolded,
|
||||
setIsFilterDropdownUnfolded,
|
||||
selectedFilters,
|
||||
setSelectedFilters,
|
||||
selectedOperandInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,13 @@
|
||||
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]);
|
||||
};
|
||||
64
front/src/modules/ui/data/filter/hooks/useFilterStates.ts
Normal file
64
front/src/modules/ui/data/filter/hooks/useFilterStates.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2';
|
||||
|
||||
import { availableFiltersScopedState } from '../states/availableFiltersScopedState';
|
||||
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 { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
||||
|
||||
export const useFilterStates = (scopeId: string) => {
|
||||
const [availableFilters, setAvailableFilters] = useRecoilScopedStateV2(
|
||||
availableFiltersScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const [filterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown] =
|
||||
useRecoilScopedStateV2(filterDefinitionUsedInDropdownScopedState, scopeId);
|
||||
|
||||
const [filterDropdownSearchInput, setFilterDropdownSearchInput] =
|
||||
useRecoilScopedStateV2(filterDropdownSearchInputScopedState, scopeId);
|
||||
|
||||
const [filterDropdownSelectedEntityId, setFilterDropdownSelectedEntityId] =
|
||||
useRecoilScopedStateV2(filterDropdownSelectedEntityIdScopedState, scopeId);
|
||||
|
||||
const [
|
||||
isFilterDropdownOperandSelectUnfolded,
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
] = useRecoilScopedStateV2(
|
||||
isFilterDropdownOperandSelectUnfoldedScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const [isFilterDropdownUnfolded, setIsFilterDropdownUnfolded] =
|
||||
useRecoilScopedStateV2(isFilterDropdownUnfoldedScopedState, scopeId);
|
||||
|
||||
const [selectedFilters, setSelectedFilters] = useRecoilScopedStateV2(
|
||||
selectedFiltersScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const [selectedOperandInDropdown, setSelectedOperandInDropdown] =
|
||||
useRecoilScopedStateV2(selectedOperandInDropdownScopedState, scopeId);
|
||||
|
||||
return {
|
||||
availableFilters,
|
||||
setAvailableFilters,
|
||||
filterDefinitionUsedInDropdown,
|
||||
setFilterDefinitionUsedInDropdown,
|
||||
filterDropdownSearchInput,
|
||||
setFilterDropdownSearchInput,
|
||||
filterDropdownSelectedEntityId,
|
||||
setFilterDropdownSelectedEntityId,
|
||||
isFilterDropdownOperandSelectUnfolded,
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
isFilterDropdownUnfolded,
|
||||
setIsFilterDropdownUnfolded,
|
||||
selectedFilters,
|
||||
setSelectedFilters,
|
||||
selectedOperandInDropdown,
|
||||
setSelectedOperandInDropdown,
|
||||
};
|
||||
};
|
||||
28
front/src/modules/ui/data/filter/scopes/FilterScope.tsx
Normal file
28
front/src/modules/ui/data/filter/scopes/FilterScope.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
|
||||
|
||||
import { FilterScopeInitEffect } from './init-effect/FilterScopeInitEffect';
|
||||
import { FilterScopeInternalContext } from './scope-internal-context/FilterScopeInternalContext';
|
||||
|
||||
type FilterScopeProps = {
|
||||
children: ReactNode;
|
||||
filterScopeId: string;
|
||||
availableFilters?: FilterDefinition[];
|
||||
};
|
||||
|
||||
export const FilterScope = ({
|
||||
children,
|
||||
filterScopeId,
|
||||
availableFilters,
|
||||
}: FilterScopeProps) => {
|
||||
return (
|
||||
<FilterScopeInternalContext.Provider value={{ scopeId: filterScopeId }}>
|
||||
<FilterScopeInitEffect
|
||||
filterScopeId={filterScopeId}
|
||||
availableFilters={availableFilters}
|
||||
/>
|
||||
{children}
|
||||
</FilterScopeInternalContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,25 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
|
||||
|
||||
import { useFilterStates } from '../../hooks/useFilterStates';
|
||||
|
||||
type FilterScopeInitEffectProps = {
|
||||
filterScopeId: string;
|
||||
availableFilters?: FilterDefinition[];
|
||||
};
|
||||
|
||||
export const FilterScopeInitEffect = ({
|
||||
filterScopeId,
|
||||
availableFilters,
|
||||
}: FilterScopeInitEffectProps) => {
|
||||
const { setAvailableFilters } = useFilterStates(filterScopeId);
|
||||
|
||||
useEffect(() => {
|
||||
if (availableFilters) {
|
||||
setAvailableFilters(availableFilters);
|
||||
}
|
||||
}, [availableFilters, setAvailableFilters]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
@ -0,0 +1,9 @@
|
||||
import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey';
|
||||
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
|
||||
|
||||
type FilterScopeInternalContextProps = ScopedStateKey & {
|
||||
test?: string;
|
||||
};
|
||||
|
||||
export const FilterScopeInternalContext =
|
||||
createScopeInternalContext<FilterScopeInternalContextProps>();
|
||||
@ -0,0 +1,9 @@
|
||||
import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition';
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const availableFiltersScopedState = createScopedState<
|
||||
FilterDefinition[]
|
||||
>({
|
||||
key: 'availableFiltersScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -0,0 +1,9 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
import { FilterDefinition } from '../types/FilterDefinition';
|
||||
|
||||
export const filterDefinitionUsedInDropdownScopedState =
|
||||
createScopedState<FilterDefinition | null>({
|
||||
key: 'filterDefinitionUsedInDropdownScopedState',
|
||||
defaultValue: null,
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const filterDropdownSearchInputScopedState = createScopedState<string>({
|
||||
key: 'filterDropdownSearchInputScopedState',
|
||||
defaultValue: '',
|
||||
});
|
||||
@ -0,0 +1,8 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const filterDropdownSelectedEntityIdScopedState = createScopedState<
|
||||
string | null
|
||||
>({
|
||||
key: 'filterDropdownSelectedEntityIdScopedState',
|
||||
defaultValue: null,
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const isFilterDropdownOperandSelectUnfoldedScopedState =
|
||||
createScopedState<boolean>({
|
||||
key: 'isFilterDropdownOperandSelectUnfoldedScopedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const isFilterDropdownUnfoldedScopedState = createScopedState<boolean>({
|
||||
key: 'isFilterDropdownUnfoldedScopedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -0,0 +1,8 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
import { Filter } from '../types/Filter';
|
||||
|
||||
export const selectedFiltersScopedState = createScopedState<Filter[]>({
|
||||
key: 'selectedFiltersScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -0,0 +1,8 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
import { ViewFilterOperand } from '~/generated/graphql';
|
||||
|
||||
export const selectedOperandInDropdownScopedState =
|
||||
createScopedState<ViewFilterOperand | null>({
|
||||
key: 'selectedOperandInDropdownScopedState',
|
||||
defaultValue: null,
|
||||
});
|
||||
@ -1,13 +1,15 @@
|
||||
import { selectorFamily } from 'recoil';
|
||||
|
||||
import { turnFilterIntoWhereClause } from '../../utils/turnFilterIntoWhereClause';
|
||||
import { filtersScopedState } from '../filtersScopedState';
|
||||
import { selectedFiltersScopedState } from '../selectedFiltersScopedState';
|
||||
|
||||
export const filtersWhereScopedSelector = selectorFamily({
|
||||
key: 'filtersWhereScopedSelector',
|
||||
get:
|
||||
(param: string) =>
|
||||
(scopeId: string) =>
|
||||
({ get }) => ({
|
||||
AND: get(filtersScopedState(param)).map(turnFilterIntoWhereClause),
|
||||
AND: get(selectedFiltersScopedState({ scopeId })).map(
|
||||
turnFilterIntoWhereClause,
|
||||
),
|
||||
}),
|
||||
});
|
||||
@ -1,5 +1,4 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { produce } from 'immer';
|
||||
|
||||
import { IconChevronDown } from '@/ui/display/icon';
|
||||
import { LightButton } from '@/ui/input/button/components/LightButton';
|
||||
@ -11,12 +10,9 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { SortDropdownId } from '../constants/SortDropdownId';
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { availableSortsScopedState } from '../states/availableSortsScopedState';
|
||||
import { sortsScopedState } from '../states/sortsScopedState';
|
||||
import { useSort } from '../hooks/useSort';
|
||||
import { SortDefinition } from '../types/SortDefinition';
|
||||
import { SORT_DIRECTIONS, SortDirection } from '../types/SortDirection';
|
||||
|
||||
@ -28,8 +24,6 @@ export type SortDropdownButtonProps = {
|
||||
export const SortDropdownButton = ({
|
||||
hotkeyScope,
|
||||
}: SortDropdownButtonProps) => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [isSortDirectionMenuUnfolded, setIsSortDirectionMenuUnfolded] =
|
||||
useState(false);
|
||||
|
||||
@ -41,17 +35,7 @@ export const SortDropdownButton = ({
|
||||
setSelectedSortDirection('asc');
|
||||
}, []);
|
||||
|
||||
const [availableSorts] = useRecoilScopedState(
|
||||
availableSortsScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [sorts, setSorts] = useRecoilScopedState(
|
||||
sortsScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const isSortSelected = sorts.length > 0;
|
||||
const { availableSorts, onSortAdd, isSortSelected } = useSort();
|
||||
|
||||
const { toggleDropdown } = useDropdown({
|
||||
dropdownScopeId: SortDropdownId,
|
||||
@ -64,25 +48,11 @@ export const SortDropdownButton = ({
|
||||
|
||||
const handleAddSort = (selectedSortDefinition: SortDefinition) => {
|
||||
toggleDropdown();
|
||||
|
||||
setSorts(
|
||||
produce(sorts, (existingSortsDraft) => {
|
||||
const foundExistingSortIndex = existingSortsDraft.findIndex(
|
||||
(existingSort) => existingSort.key === selectedSortDefinition.key,
|
||||
);
|
||||
|
||||
if (foundExistingSortIndex !== -1) {
|
||||
existingSortsDraft[foundExistingSortIndex].direction =
|
||||
selectedSortDirection;
|
||||
} else {
|
||||
existingSortsDraft.push({
|
||||
key: selectedSortDefinition.key,
|
||||
direction: selectedSortDirection,
|
||||
definition: selectedSortDefinition,
|
||||
});
|
||||
}
|
||||
}),
|
||||
);
|
||||
onSortAdd?.({
|
||||
key: selectedSortDefinition.key,
|
||||
direction: selectedSortDirection,
|
||||
definition: selectedSortDefinition,
|
||||
});
|
||||
};
|
||||
|
||||
const handleDropdownButtonClose = () => {
|
||||
36
front/src/modules/ui/data/sort/hooks/useSort.ts
Normal file
36
front/src/modules/ui/data/sort/hooks/useSort.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
|
||||
|
||||
import { SortScopeInternalContext } from '../scopes/scope-internal-context/SortScopeInternalContext';
|
||||
|
||||
import { useScopeInternalContextOrThrow } from './../../../utilities/recoil-scope/scopes-internal/hooks/useScopeInternalContextOrThrow';
|
||||
import { useSortStates } from './useSortStates';
|
||||
|
||||
type UseSortProps = {
|
||||
sortScopeId?: string;
|
||||
};
|
||||
|
||||
export const useSort = (props?: UseSortProps) => {
|
||||
const scopeId = useAvailableScopeIdOrThrow(
|
||||
SortScopeInternalContext,
|
||||
props?.sortScopeId,
|
||||
);
|
||||
const {
|
||||
availableSorts,
|
||||
setAvailableSorts,
|
||||
isSortSelected,
|
||||
setIsSortSelected,
|
||||
} = useSortStates(scopeId);
|
||||
|
||||
const { onSortAdd } = useScopeInternalContextOrThrow(
|
||||
SortScopeInternalContext,
|
||||
);
|
||||
|
||||
return {
|
||||
onSortAdd,
|
||||
scopeId,
|
||||
availableSorts,
|
||||
isSortSelected,
|
||||
setIsSortSelected,
|
||||
setAvailableSorts,
|
||||
};
|
||||
};
|
||||
23
front/src/modules/ui/data/sort/hooks/useSortStates.ts
Normal file
23
front/src/modules/ui/data/sort/hooks/useSortStates.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2';
|
||||
import { availableSortsScopedState } from '@/views/states/availableSortsScopedState';
|
||||
|
||||
import { isSortSelectedScopedState } from '../states/isSortSelectedScopedState';
|
||||
|
||||
export const useSortStates = (scopeId: string) => {
|
||||
const [availableSorts, setAvailableSorts] = useRecoilScopedStateV2(
|
||||
availableSortsScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
const [isSortSelected, setIsSortSelected] = useRecoilScopedStateV2(
|
||||
isSortSelectedScopedState,
|
||||
scopeId,
|
||||
);
|
||||
|
||||
return {
|
||||
availableSorts,
|
||||
setAvailableSorts,
|
||||
isSortSelected,
|
||||
setIsSortSelected,
|
||||
};
|
||||
};
|
||||
33
front/src/modules/ui/data/sort/scopes/SortScope.tsx
Normal file
33
front/src/modules/ui/data/sort/scopes/SortScope.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { Sort } from '../types/Sort';
|
||||
import { SortDefinition } from '../types/SortDefinition';
|
||||
|
||||
import { SortScopeInitEffect } from './init-effect/SortScopeInitEffect';
|
||||
import { SortScopeInternalContext } from './scope-internal-context/SortScopeInternalContext';
|
||||
|
||||
type SortScopeProps = {
|
||||
children: ReactNode;
|
||||
sortScopeId: string;
|
||||
availableSorts?: SortDefinition[];
|
||||
onSortAdd?: (sort: Sort) => void | Promise<void>;
|
||||
};
|
||||
|
||||
export const SortScope = ({
|
||||
children,
|
||||
sortScopeId,
|
||||
availableSorts,
|
||||
onSortAdd,
|
||||
}: SortScopeProps) => {
|
||||
return (
|
||||
<SortScopeInternalContext.Provider
|
||||
value={{ scopeId: sortScopeId, onSortAdd }}
|
||||
>
|
||||
<SortScopeInitEffect
|
||||
sortScopeId={sortScopeId}
|
||||
availableSorts={availableSorts}
|
||||
/>
|
||||
{children}
|
||||
</SortScopeInternalContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,25 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { SortDefinition } from '@/ui/data/sort/types/SortDefinition';
|
||||
|
||||
import { useSortStates } from '../../hooks/useSortStates';
|
||||
|
||||
type SortScopeInitEffectProps = {
|
||||
sortScopeId: string;
|
||||
availableSorts?: SortDefinition[];
|
||||
};
|
||||
|
||||
export const SortScopeInitEffect = ({
|
||||
sortScopeId,
|
||||
availableSorts,
|
||||
}: SortScopeInitEffectProps) => {
|
||||
const { setAvailableSorts } = useSortStates(sortScopeId);
|
||||
|
||||
useEffect(() => {
|
||||
if (availableSorts) {
|
||||
setAvailableSorts(availableSorts);
|
||||
}
|
||||
}, [availableSorts, setAvailableSorts]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
@ -0,0 +1,13 @@
|
||||
import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey';
|
||||
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[];
|
||||
};
|
||||
|
||||
export const SortScopeInternalContext =
|
||||
createScopeInternalContext<SortScopeInternalContextProps>();
|
||||
@ -0,0 +1,8 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
import { SortDefinition } from '../types/SortDefinition';
|
||||
|
||||
export const availableSortsScopedState = createScopedState<SortDefinition[]>({
|
||||
key: 'availableSortsScopedState',
|
||||
defaultValue: [],
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState';
|
||||
|
||||
export const isSortSelectedScopedState = createScopedState<boolean>({
|
||||
key: 'isSortSelectedScopedState',
|
||||
defaultValue: false,
|
||||
});
|
||||
@ -1,51 +0,0 @@
|
||||
import { useUpsertFilter } from '@/ui/data/view-bar/hooks/useUpsertFilter';
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '@/ui/data/view-bar/states/filterDefinitionUsedInDropdownScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '@/ui/data/view-bar/states/selectedOperandInDropdownScopedState';
|
||||
import { InternalDatePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { isFilterDropdownUnfoldedScopedState } from '../states/isFilterDropdownUnfoldedScopedState';
|
||||
|
||||
export const FilterDropdownDateSearchInput = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [filterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [selectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setIsFilterDropdownUnfolded] = useRecoilScopedState(
|
||||
isFilterDropdownUnfoldedScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const upsertFilter = useUpsertFilter();
|
||||
|
||||
const handleChange = (date: Date) => {
|
||||
if (!filterDefinitionUsedInDropdown || !selectedOperandInDropdown) return;
|
||||
|
||||
upsertFilter({
|
||||
key: filterDefinitionUsedInDropdown.key,
|
||||
type: filterDefinitionUsedInDropdown.type,
|
||||
value: date.toISOString(),
|
||||
operand: selectedOperandInDropdown,
|
||||
displayValue: date.toLocaleDateString(),
|
||||
});
|
||||
|
||||
setIsFilterDropdownUnfolded(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<InternalDatePicker
|
||||
date={new Date()}
|
||||
onChange={handleChange}
|
||||
onMouseSelect={handleChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,44 +0,0 @@
|
||||
import { ChangeEvent } from 'react';
|
||||
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '@/ui/data/view-bar/states/filterDefinitionUsedInDropdownScopedState';
|
||||
import { filterDropdownSearchInputScopedState } from '@/ui/data/view-bar/states/filterDropdownSearchInputScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '@/ui/data/view-bar/states/selectedOperandInDropdownScopedState';
|
||||
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
|
||||
export const FilterDropdownEntitySearchInput = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [filterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [selectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [filterDropdownSearchInput, setFilterDropdownSearchInput] =
|
||||
useRecoilScopedState(
|
||||
filterDropdownSearchInputScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
return (
|
||||
filterDefinitionUsedInDropdown &&
|
||||
selectedOperandInDropdown && (
|
||||
<DropdownMenuSearchInput
|
||||
autoFocus
|
||||
type="text"
|
||||
value={filterDropdownSearchInput}
|
||||
placeholder={filterDefinitionUsedInDropdown.label}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setFilterDropdownSearchInput(event.target.value);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
};
|
||||
@ -1,28 +0,0 @@
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
|
||||
|
||||
export const FilterDropdownEntitySelect = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [filterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
if (filterDefinitionUsedInDropdown?.type !== 'entity') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<RecoilScope>
|
||||
{filterDefinitionUsedInDropdown.entitySelectComponent}
|
||||
</RecoilScope>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -1,39 +0,0 @@
|
||||
import { IconChevronDown } from '@/ui/display/icon';
|
||||
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { isFilterDropdownOperandSelectUnfoldedScopedState } from '../states/isFilterDropdownOperandSelectUnfoldedScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
||||
import { getOperandLabel } from '../utils/getOperandLabel';
|
||||
|
||||
export const FilterDropdownOperandButton = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [selectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [
|
||||
isFilterDropdownOperandSelectUnfolded,
|
||||
setIsFilterDropdownOperandSelectUnfolded,
|
||||
] = useRecoilScopedState(
|
||||
isFilterDropdownOperandSelectUnfoldedScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
if (isFilterDropdownOperandSelectUnfolded) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenuHeader
|
||||
key={'selected-filter-operand'}
|
||||
EndIcon={IconChevronDown}
|
||||
onClick={() => setIsFilterDropdownOperandSelectUnfolded(true)}
|
||||
>
|
||||
{getOperandLabel(selectedOperandInDropdown)}
|
||||
</DropdownMenuHeader>
|
||||
);
|
||||
};
|
||||
@ -1,59 +0,0 @@
|
||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { FilterDropdownId } from '../constants/FilterDropdownId';
|
||||
import { useViewBarContext } from '../hooks/useViewBarContext';
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
|
||||
import { filterDropdownSearchInputScopedState } from '../states/filterDropdownSearchInputScopedState';
|
||||
import { isFilterDropdownOperandSelectUnfoldedScopedState } from '../states/isFilterDropdownOperandSelectUnfoldedScopedState';
|
||||
import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState';
|
||||
|
||||
export const MultipleFiltersButton = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const { isDropdownOpen, toggleDropdown } = useDropdown({
|
||||
dropdownScopeId: FilterDropdownId,
|
||||
});
|
||||
|
||||
const [, setIsFilterDropdownOperandSelectUnfolded] = useRecoilScopedState(
|
||||
isFilterDropdownOperandSelectUnfoldedScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setFilterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setFilterDropdownSearchInput] = useRecoilScopedState(
|
||||
filterDropdownSearchInputScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [, setSelectedOperandInDropdown] = useRecoilScopedState(
|
||||
selectedOperandInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const resetState = () => {
|
||||
setIsFilterDropdownOperandSelectUnfolded(false);
|
||||
setFilterDefinitionUsedInDropdown(null);
|
||||
setSelectedOperandInDropdown(null);
|
||||
setFilterDropdownSearchInput('');
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
toggleDropdown();
|
||||
resetState();
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledHeaderDropdownButton
|
||||
isUnfolded={isDropdownOpen}
|
||||
onClick={handleClick}
|
||||
>
|
||||
Filter
|
||||
</StyledHeaderDropdownButton>
|
||||
);
|
||||
};
|
||||
@ -1,138 +0,0 @@
|
||||
import { useCallback, useContext, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { Key } from 'ts-key-enum';
|
||||
|
||||
import { currentViewIdScopedState } from '@/ui/data/view-bar/states/currentViewIdScopedState';
|
||||
import { filtersScopedState } from '@/ui/data/view-bar/states/filtersScopedState';
|
||||
import { savedFiltersFamilyState } from '@/ui/data/view-bar/states/savedFiltersFamilyState';
|
||||
import { savedSortsFamilyState } from '@/ui/data/view-bar/states/savedSortsFamilyState';
|
||||
import { canPersistFiltersScopedFamilySelector } from '@/ui/data/view-bar/states/selectors/canPersistFiltersScopedFamilySelector';
|
||||
import { canPersistSortsScopedFamilySelector } from '@/ui/data/view-bar/states/selectors/canPersistSortsScopedFamilySelector';
|
||||
import { sortsScopedState } from '@/ui/data/view-bar/states/sortsScopedState';
|
||||
import { viewEditModeState } from '@/ui/data/view-bar/states/viewEditModeState';
|
||||
import { IconChevronDown, IconPlus } from '@/ui/display/icon';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { ButtonGroup } from '@/ui/input/button/components/ButtonGroup';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: inline-flex;
|
||||
margin-right: ${({ theme }) => theme.spacing(2)};
|
||||
position: relative;
|
||||
`;
|
||||
export type UpdateViewButtonGroupProps = {
|
||||
hotkeyScope: string;
|
||||
onViewEditModeChange?: () => void;
|
||||
};
|
||||
|
||||
export const UpdateViewButtonGroup = ({
|
||||
hotkeyScope,
|
||||
onViewEditModeChange,
|
||||
}: UpdateViewButtonGroupProps) => {
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
|
||||
const {
|
||||
canPersistViewFields,
|
||||
onCurrentViewSubmit,
|
||||
ViewBarRecoilScopeContext,
|
||||
} = useContext(ViewBarContext);
|
||||
|
||||
const recoilScopeId = useRecoilScopeId(ViewBarRecoilScopeContext);
|
||||
|
||||
const currentViewId = useRecoilScopedValue(
|
||||
currentViewIdScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const filters = useRecoilScopedValue(
|
||||
filtersScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const setSavedFilters = useSetRecoilState(
|
||||
savedFiltersFamilyState(currentViewId),
|
||||
);
|
||||
const canPersistFilters = useRecoilValue(
|
||||
canPersistFiltersScopedFamilySelector({
|
||||
recoilScopeId,
|
||||
viewId: currentViewId,
|
||||
}),
|
||||
);
|
||||
|
||||
const sorts = useRecoilScopedValue(
|
||||
sortsScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const setSavedSorts = useSetRecoilState(savedSortsFamilyState(currentViewId));
|
||||
const canPersistSorts = useRecoilValue(
|
||||
canPersistSortsScopedFamilySelector({
|
||||
recoilScopeId,
|
||||
viewId: currentViewId,
|
||||
}),
|
||||
);
|
||||
|
||||
const setViewEditMode = useSetRecoilState(viewEditModeState);
|
||||
|
||||
const canPersistView =
|
||||
currentViewId &&
|
||||
(canPersistViewFields || canPersistFilters || canPersistSorts);
|
||||
|
||||
const handleArrowDownButtonClick = useCallback(() => {
|
||||
setIsDropdownOpen((previousIsOpen) => !previousIsOpen);
|
||||
}, []);
|
||||
|
||||
const handleCreateViewButtonClick = useCallback(() => {
|
||||
setViewEditMode({ mode: 'create', viewId: undefined });
|
||||
onViewEditModeChange?.();
|
||||
setIsDropdownOpen(false);
|
||||
}, [setViewEditMode, onViewEditModeChange]);
|
||||
|
||||
const handleDropdownClose = useCallback(() => {
|
||||
setIsDropdownOpen(false);
|
||||
}, []);
|
||||
|
||||
const handleViewSubmit = async () => {
|
||||
if (canPersistFilters) setSavedFilters(filters);
|
||||
if (canPersistSorts) setSavedSorts(sorts);
|
||||
|
||||
await onCurrentViewSubmit?.();
|
||||
};
|
||||
|
||||
useScopedHotkeys(
|
||||
[Key.Enter, Key.Escape],
|
||||
handleDropdownClose,
|
||||
hotkeyScope,
|
||||
[],
|
||||
);
|
||||
|
||||
if (!canPersistView) return null;
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<ButtonGroup accent="blue">
|
||||
<Button title="Update view" onClick={handleViewSubmit} />
|
||||
<Button
|
||||
size="small"
|
||||
Icon={IconChevronDown}
|
||||
onClick={handleArrowDownButtonClick}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
|
||||
{isDropdownOpen && (
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
onClick={handleCreateViewButtonClick}
|
||||
LeftIcon={IconPlus}
|
||||
text="Create view"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
)}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -1,65 +0,0 @@
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { TopBar } from '@/ui/layout/top-bar/TopBar';
|
||||
|
||||
import { FiltersHotkeyScope } from '../types/FiltersHotkeyScope';
|
||||
import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
|
||||
|
||||
import { FilterDropdownButton } from './FilterDropdownButton';
|
||||
import { SortDropdownButton } from './SortDropdownButton';
|
||||
import { UpdateViewButtonGroup } from './UpdateViewButtonGroup';
|
||||
import { ViewBarDetails } from './ViewBarDetails';
|
||||
import { ViewsDropdownButton } from './ViewsDropdownButton';
|
||||
|
||||
export type ViewBarProps = {
|
||||
className?: string;
|
||||
optionsDropdownButton: ReactNode;
|
||||
optionsDropdownScopeId: string;
|
||||
};
|
||||
|
||||
export const ViewBar = ({
|
||||
className,
|
||||
optionsDropdownButton,
|
||||
optionsDropdownScopeId,
|
||||
}: ViewBarProps) => {
|
||||
const { openDropdown: openOptionsDropdownButton } = useDropdown({
|
||||
dropdownScopeId: optionsDropdownScopeId,
|
||||
});
|
||||
|
||||
return (
|
||||
<TopBar
|
||||
className={className}
|
||||
leftComponent={
|
||||
<ViewsDropdownButton
|
||||
onViewEditModeChange={openOptionsDropdownButton}
|
||||
hotkeyScope={{ scope: ViewsHotkeyScope.ListDropdown }}
|
||||
/>
|
||||
}
|
||||
displayBottomBorder={false}
|
||||
rightComponent={
|
||||
<>
|
||||
<FilterDropdownButton
|
||||
hotkeyScope={{ scope: FiltersHotkeyScope.FilterDropdownButton }}
|
||||
/>
|
||||
<SortDropdownButton
|
||||
hotkeyScope={{ scope: FiltersHotkeyScope.SortDropdownButton }}
|
||||
isPrimaryButton
|
||||
/>
|
||||
{optionsDropdownButton}
|
||||
</>
|
||||
}
|
||||
bottomComponent={
|
||||
<ViewBarDetails
|
||||
hasFilterButton
|
||||
rightComponent={
|
||||
<UpdateViewButtonGroup
|
||||
onViewEditModeChange={openOptionsDropdownButton}
|
||||
hotkeyScope={ViewsHotkeyScope.CreateDropdown}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -1,20 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
import { RecoilScopeContext } from '@/types/RecoilScopeContext';
|
||||
|
||||
import { View } from '../types/View';
|
||||
|
||||
export const ViewBarContext = createContext<{
|
||||
canPersistViewFields?: boolean;
|
||||
defaultViewName?: string;
|
||||
onCurrentViewSubmit?: () => void | Promise<void>;
|
||||
onViewBarReset?: () => void;
|
||||
onViewCreate?: (view: View) => void | Promise<void>;
|
||||
onViewEdit?: (view: View) => void | Promise<void>;
|
||||
onViewRemove?: (viewId: string) => void | Promise<void>;
|
||||
onViewSelect?: (viewId: string) => void | Promise<void>;
|
||||
onImport?: () => void | Promise<void>;
|
||||
ViewBarRecoilScopeContext: RecoilScopeContext;
|
||||
}>({
|
||||
ViewBarRecoilScopeContext: createContext<string | null>(null),
|
||||
});
|
||||
@ -1,28 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState';
|
||||
import { filtersScopedState } from '../states/filtersScopedState';
|
||||
|
||||
import { useViewBarContext } from './useViewBarContext';
|
||||
|
||||
export const useFilterCurrentlyEdited = () => {
|
||||
const { ViewBarRecoilScopeContext } = useViewBarContext();
|
||||
|
||||
const [filters] = useRecoilScopedState(
|
||||
filtersScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const [filterDefinitionUsedInDropdown] = useRecoilScopedState(
|
||||
filterDefinitionUsedInDropdownScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
return filters.find(
|
||||
(filter) => filter.key === filterDefinitionUsedInDropdown?.key,
|
||||
);
|
||||
}, [filterDefinitionUsedInDropdown, filters]);
|
||||
};
|
||||
@ -1,25 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { filtersScopedState } from '../states/filtersScopedState';
|
||||
|
||||
export const useRemoveFilter = () => {
|
||||
const { ViewBarRecoilScopeContext } = useContext(ViewBarContext);
|
||||
|
||||
const [, setFilters] = useRecoilScopedState(
|
||||
filtersScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
|
||||
const removeFilter = (filterKey: string) => {
|
||||
setFilters((filters) => {
|
||||
return filters.filter((filter) => {
|
||||
return filter.key !== filterKey;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return removeFilter;
|
||||
};
|
||||
@ -1,35 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
import { viewsScopedState } from '../states/viewsScopedState';
|
||||
|
||||
export const useRemoveView = () => {
|
||||
const { onViewRemove, ViewBarRecoilScopeContext } =
|
||||
useContext(ViewBarContext);
|
||||
|
||||
const recoilScopeId = useRecoilScopeId(ViewBarRecoilScopeContext);
|
||||
|
||||
const removeView = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const currentViewId = await snapshot.getPromise(
|
||||
currentViewIdScopedState(recoilScopeId),
|
||||
);
|
||||
|
||||
if (currentViewId === viewId)
|
||||
set(currentViewIdScopedState(recoilScopeId), undefined);
|
||||
|
||||
set(viewsScopedState(recoilScopeId), (previousViews) =>
|
||||
previousViews.filter((view) => view.id !== viewId),
|
||||
);
|
||||
await onViewRemove?.(viewId);
|
||||
},
|
||||
[onViewRemove, recoilScopeId],
|
||||
);
|
||||
|
||||
return { removeView };
|
||||
};
|
||||
@ -1,109 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilCallback, useRecoilValue, useResetRecoilState } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||
import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
import { currentViewIdScopedState } from '../states/currentViewIdScopedState';
|
||||
import { filtersScopedState } from '../states/filtersScopedState';
|
||||
import { savedFiltersFamilyState } from '../states/savedFiltersFamilyState';
|
||||
import { savedSortsFamilyState } from '../states/savedSortsFamilyState';
|
||||
import { currentViewScopedSelector } from '../states/selectors/currentViewScopedSelector';
|
||||
import { viewsByIdScopedSelector } from '../states/selectors/viewsByIdScopedSelector';
|
||||
import { sortsScopedState } from '../states/sortsScopedState';
|
||||
import { viewEditModeState } from '../states/viewEditModeState';
|
||||
import { viewsScopedState } from '../states/viewsScopedState';
|
||||
|
||||
export const useUpsertView = () => {
|
||||
const { onViewCreate, onViewEdit, ViewBarRecoilScopeContext } =
|
||||
useContext(ViewBarContext);
|
||||
const recoilScopeId = useRecoilScopeId(ViewBarRecoilScopeContext);
|
||||
|
||||
const filters = useRecoilScopedValue(
|
||||
filtersScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const sorts = useRecoilScopedValue(
|
||||
sortsScopedState,
|
||||
ViewBarRecoilScopeContext,
|
||||
);
|
||||
const viewEditMode = useRecoilValue(viewEditModeState);
|
||||
const resetViewEditMode = useResetRecoilState(viewEditModeState);
|
||||
|
||||
const upsertView = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
async (name?: string) => {
|
||||
if (!viewEditMode.mode || !name) {
|
||||
resetViewEditMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (viewEditMode.mode === 'create') {
|
||||
const createdView = { id: v4(), name };
|
||||
|
||||
set(savedFiltersFamilyState(createdView.id), filters);
|
||||
set(savedSortsFamilyState(createdView.id), sorts);
|
||||
|
||||
set(viewsScopedState(recoilScopeId), (previousViews) => [
|
||||
...previousViews,
|
||||
createdView,
|
||||
]);
|
||||
|
||||
await onViewCreate?.(createdView);
|
||||
|
||||
resetViewEditMode();
|
||||
|
||||
set(currentViewIdScopedState(recoilScopeId), createdView.id);
|
||||
|
||||
return createdView;
|
||||
}
|
||||
|
||||
const viewsById = await snapshot.getPromise(
|
||||
viewsByIdScopedSelector(recoilScopeId),
|
||||
);
|
||||
const currentView = await snapshot.getPromise(
|
||||
currentViewScopedSelector(recoilScopeId),
|
||||
);
|
||||
|
||||
const viewToEdit = viewEditMode.viewId
|
||||
? viewsById[viewEditMode.viewId]
|
||||
: currentView;
|
||||
|
||||
if (!viewToEdit) {
|
||||
resetViewEditMode();
|
||||
return;
|
||||
}
|
||||
|
||||
const editedView = {
|
||||
...viewToEdit,
|
||||
name,
|
||||
};
|
||||
|
||||
set(viewsScopedState(recoilScopeId), (previousViews) =>
|
||||
previousViews.map((previousView) =>
|
||||
previousView.id === editedView.id ? editedView : previousView,
|
||||
),
|
||||
);
|
||||
|
||||
await onViewEdit?.(editedView);
|
||||
|
||||
resetViewEditMode();
|
||||
|
||||
return editedView;
|
||||
},
|
||||
[
|
||||
filters,
|
||||
onViewCreate,
|
||||
onViewEdit,
|
||||
recoilScopeId,
|
||||
resetViewEditMode,
|
||||
sorts,
|
||||
viewEditMode.mode,
|
||||
viewEditMode.viewId,
|
||||
],
|
||||
);
|
||||
|
||||
return { upsertView };
|
||||
};
|
||||
@ -1,7 +0,0 @@
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { ViewBarContext } from '../contexts/ViewBarContext';
|
||||
|
||||
export const useViewBarContext = () => {
|
||||
return useContext(ViewBarContext);
|
||||
};
|
||||
@ -1,11 +0,0 @@
|
||||
import { atomFamily } from 'recoil';
|
||||
|
||||
import { FilterDefinition } from '../types/FilterDefinition';
|
||||
|
||||
export const availableFiltersScopedState = atomFamily<
|
||||
FilterDefinition[],
|
||||
string
|
||||
>({
|
||||
key: 'availableFiltersScopedState',
|
||||
default: [],
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user