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:
Charles Bochet
2023-10-27 10:52:26 +02:00
committed by GitHub
parent 6a72c14af3
commit 5ba68e997d
205 changed files with 3092 additions and 3249 deletions

View File

@ -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>

View File

@ -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,

View File

@ -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 <></>;

View File

@ -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',

View File

@ -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>
);
};

View File

@ -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;

View File

@ -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 <></>;

View File

@ -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>
);
};