From 9bab28912d762280f63f887d238f93070e1d0906 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Sun, 29 Oct 2023 16:29:00 +0100 Subject: [PATCH] Complete Fix view work (#2272) * Fix views * Make view sorts and view filters functional * Complete Company table view fix * Fix model creation * Start fixing board * Complete work --- .../tasks/components/PageAddTaskButton.tsx | 8 +- .../tasks/hooks/useCurrentUserDueTaskCount.ts | 6 +- .../activities/tasks/hooks/useTasks.ts | 13 +- .../board/components/CompanyBoard.tsx | 56 ++-- .../components/HooksCompanyBoardEffect.tsx | 63 ++++- .../table/components/CompanyTable.tsx | 55 ++-- .../table/components/CompanyTableEffect.tsx | 27 +- .../components/CompanyTableMockDataEffect.tsx | 1 - .../components/MetadataObjectNavItems.tsx | 29 +- .../metadata/components/ObjectTableEffect.tsx | 18 +- .../hooks/useFindOneMetadataObject.ts | 3 +- .../metadata/hooks/useSetDataTableData.ts | 7 +- ...x => usePersonTableContextMenuEntries.tsx} | 0 .../people/table/components/PeopleTable.tsx | 97 ------- .../people/table/components/PersonTable.tsx | 135 ++++++++++ .../table/components/PersonTableEffect.tsx | 22 +- .../pipelineAvailableFieldDefinitions.tsx | 16 +- .../object-edit/SettingsObjectIconSection.tsx | 7 +- .../data-table/components/DataTableHeader.tsx | 58 ++-- .../data-table/hooks/useSetDataTableData.ts | 4 +- .../TableOptionsDropdownContent.tsx | 4 +- .../components/FilterDropdownButton.tsx | 7 +- .../FilterDropdownDateSearchInput.tsx | 10 +- .../FilterDropdownEntitySearchSelect.tsx | 69 ++--- .../components/FilterDropdownFilterSelect.tsx | 14 +- .../FilterDropdownNumberSearchInput.tsx | 30 +-- .../FilterDropdownOperandSelect.tsx | 20 +- .../FilterDropdownTextSearchInput.tsx | 30 +-- .../SingleEntityFilterDropdownButton.tsx | 12 +- .../modules/ui/data/filter/hooks/useFilter.ts | 33 ++- .../filter/hooks/useFilterCurrentlyEdited.ts | 13 - .../ui/data/filter/hooks/useFilterStates.ts | 22 +- .../ui/data/filter/scopes/FilterScope.tsx | 14 +- .../init-effect/FilterScopeInitEffect.tsx | 12 +- .../FilterScopeInternalContext.ts | 4 +- .../availableFilterDefinitionsScopedState.ts} | 4 +- .../states/selectedFilterScopedState.ts | 8 + .../states/selectedFiltersScopedState.ts | 8 - .../modules/ui/data/filter/types/Filter.ts | 6 +- .../ui/data/filter/types/FilterDefinition.ts | 2 +- .../filter/types/FilterDefinitionByEntity.ts | 2 +- .../filter/utils/turnFilterIntoWhereClause.ts | 41 +-- .../sort/components/SortDropdownButton.tsx | 8 +- .../src/modules/ui/data/sort/hooks/useSort.ts | 12 +- .../ui/data/sort/hooks/useSortStates.ts | 12 +- .../modules/ui/data/sort/scopes/SortScope.tsx | 12 +- .../init-effect/SortScopeInitEffect.tsx | 12 +- .../SortScopeInternalContext.ts | 4 +- front/src/modules/ui/data/sort/types/Sort.ts | 2 +- .../ui/data/sort/types/SortDefinition.ts | 2 +- .../src/modules/ui/data/sort/utils/helpers.ts | 2 +- .../board/components/BoardOptionsDropdown.tsx | 15 +- .../components/BoardOptionsDropdownButton.tsx | 6 +- .../BoardOptionsDropdownContent.tsx | 85 ++---- .../layout/board/components/EntityBoard.tsx | 2 + .../constants/BoardOptionsDropdownId.ts | 2 + .../ui/layout/board/types/BoardOptions.ts | 4 +- .../layout/board/types/enums/BoardScopeIds.ts | 3 - .../components/UpdateViewButtonGroup.tsx | 4 +- .../src/modules/views/components/ViewBar.tsx | 15 +- .../views/components/ViewBarDetails.tsx | 49 +--- .../views/components/ViewBarEffect.tsx | 248 ++++++++++-------- .../views/components/ViewsDropdownButton.tsx | 22 +- .../views/hooks/internal/useViewFields.ts | 10 +- .../views/hooks/internal/useViewFilters.ts | 235 +++++++++++------ .../views/hooks/internal/useViewSorts.ts | 211 ++++++++++----- .../modules/views/hooks/internal/useViews.ts | 50 ++-- .../modules/views/hooks/useRemoveFilter.ts | 15 -- .../modules/views/hooks/useUpsertFilter.ts | 26 -- front/src/modules/views/hooks/useView.ts | 112 ++++++-- ...wInternalStates.ts => useViewGetStates.ts} | 94 +++---- .../{useViewStates.ts => useViewSetStates.ts} | 48 +++- .../init-effect/ViewScopeInitEffect.tsx | 5 +- ...> availableFieldDefinitionsScopedState.ts} | 4 +- .../availableFilterDefinitionsScopedState.ts} | 4 +- ...=> availableSortDefinitionsScopedState.ts} | 6 +- .../currentViewFiltersScopedFamilyState.ts | 5 +- .../currentViewSortsScopedFamilyState.ts | 5 +- .../savedViewFiltersScopedFamilyState.ts | 5 +- .../states/savedViewSortsScopedFamilyState.ts | 5 +- ...vedViewFiltersByKeyScopedFamilySelector.ts | 6 +- ...savedViewSortsByKeyScopedFamilySelector.ts | 6 +- front/src/modules/views/types/ViewField.ts | 4 + front/src/modules/views/types/ViewFilter.ts | 12 + front/src/modules/views/types/ViewSort.ts | 9 + .../utils/columnDefinitionToViewField.ts | 1 + .../viewFieldsToBoardFieldDefinitions.ts | 35 +++ .../views/utils/viewFiltersToFilters.ts | 15 ++ .../modules/views/utils/viewSortsToSorts.ts | 13 + front/src/pages/companies/CompanyShow.tsx | 4 +- .../src/pages/companies/companies-filters.tsx | 51 ---- ...on.tsx => companyShowFieldDefinitions.tsx} | 2 +- .../companyTableFilterDefinitions.tsx | 52 ++++ .../companyTableSortDefinitions.tsx} | 12 +- .../src/pages/opportunities/Opportunities.tsx | 19 +- .../opportunityBoardFilterDefinitions.tsx} | 12 +- .../opportunityBoardSortDefinitions.tsx} | 8 +- .../opportunitiesBoardOptions.tsx | 8 +- front/src/pages/people/People.tsx | 4 +- front/src/pages/people/PersonShow.tsx | 2 +- ...ion.tsx => personShowFieldDefinitions.tsx} | 0 .../personTableFilterDefinitions.tsx | 59 +++++ .../personTableSortDefinitions.tsx} | 14 +- front/src/pages/people/people-filters.tsx | 58 ---- front/src/pages/tasks/Tasks.tsx | 54 ++-- front/src/pages/tasks/TasksEffect.tsx | 28 +- ...lters.tsx => tasks-filter-definitions.tsx} | 4 +- .../companies/companies.metadata.json | 10 +- .../people/people.metadata.json | 113 -------- .../standard-object-metadata.ts | 6 +- .../view-fields/view-fields.metadata.json | 2 +- .../view-filters/view-filters.metadata.json | 66 +++++ .../view-sorts/view-sorts.metadata.json | 44 ++++ .../views/views.metadata.json | 2 +- .../migrations/1697618010-addPeopleTable.ts | 59 ----- .../1697618013-addViewFilterTable.ts | 39 +++ .../migrations/1697618014-addViewSortTable.ts | 29 ++ .../tenant-migration/standard-migrations.ts | 6 +- 118 files changed, 1806 insertions(+), 1413 deletions(-) rename front/src/modules/people/hooks/{usePeopleTableContextMenuEntries.tsx => usePersonTableContextMenuEntries.tsx} (100%) delete mode 100644 front/src/modules/people/table/components/PeopleTable.tsx create mode 100644 front/src/modules/people/table/components/PersonTable.tsx delete mode 100644 front/src/modules/ui/data/filter/hooks/useFilterCurrentlyEdited.ts rename front/src/modules/{views/states/availableFiltersScopedState.ts => ui/data/filter/states/availableFilterDefinitionsScopedState.ts} (64%) create mode 100644 front/src/modules/ui/data/filter/states/selectedFilterScopedState.ts delete mode 100644 front/src/modules/ui/data/filter/states/selectedFiltersScopedState.ts create mode 100644 front/src/modules/ui/layout/board/components/constants/BoardOptionsDropdownId.ts delete mode 100644 front/src/modules/ui/layout/board/types/enums/BoardScopeIds.ts delete mode 100644 front/src/modules/views/hooks/useRemoveFilter.ts delete mode 100644 front/src/modules/views/hooks/useUpsertFilter.ts rename front/src/modules/views/hooks/{useViewInternalStates.ts => useViewGetStates.ts} (66%) rename front/src/modules/views/hooks/{useViewStates.ts => useViewSetStates.ts} (71%) rename front/src/modules/views/states/{availableFieldsScopedState.ts => availableFieldDefinitionsScopedState.ts} (71%) rename front/src/modules/{ui/data/filter/states/availableFiltersScopedState.ts => views/states/availableFilterDefinitionsScopedState.ts} (64%) rename front/src/modules/views/states/{availableSortsScopedState.ts => availableSortDefinitionsScopedState.ts} (56%) create mode 100644 front/src/modules/views/types/ViewFilter.ts create mode 100644 front/src/modules/views/types/ViewSort.ts create mode 100644 front/src/modules/views/utils/viewFieldsToBoardFieldDefinitions.ts create mode 100644 front/src/modules/views/utils/viewFiltersToFilters.ts create mode 100644 front/src/modules/views/utils/viewSortsToSorts.ts delete mode 100644 front/src/pages/companies/companies-filters.tsx rename front/src/pages/companies/constants/{companyShowFieldDefinition.tsx => companyShowFieldDefinitions.tsx} (96%) create mode 100644 front/src/pages/companies/constants/companyTableFilterDefinitions.tsx rename front/src/pages/companies/{companies-sorts.tsx => constants/companyTableSortDefinitions.tsx} (71%) rename front/src/pages/opportunities/{opportunities-filters.tsx => constants/opportunityBoardFilterDefinitions.tsx} (72%) rename front/src/pages/opportunities/{opportunities-sorts.tsx => constants/opportunityBoardSortDefinitions.tsx} (71%) rename front/src/pages/people/constants/{personShowFieldDefinition.tsx => personShowFieldDefinitions.tsx} (100%) create mode 100644 front/src/pages/people/constants/personTableFilterDefinitions.tsx rename front/src/pages/people/{people-sorts.tsx => constants/personTableSortDefinitions.tsx} (80%) delete mode 100644 front/src/pages/people/people-filters.tsx rename front/src/pages/tasks/{tasks-filters.tsx => tasks-filter-definitions.tsx} (83%) delete mode 100644 server/src/metadata/tenant-initialisation/standard-objects/people/people.metadata.json create mode 100644 server/src/metadata/tenant-initialisation/standard-objects/view-filters/view-filters.metadata.json create mode 100644 server/src/metadata/tenant-initialisation/standard-objects/view-sorts/view-sorts.metadata.json delete mode 100644 server/src/metadata/tenant-migration/migrations/1697618010-addPeopleTable.ts create mode 100644 server/src/metadata/tenant-migration/migrations/1697618013-addViewFilterTable.ts create mode 100644 server/src/metadata/tenant-migration/migrations/1697618014-addViewSortTable.ts diff --git a/front/src/modules/activities/tasks/components/PageAddTaskButton.tsx b/front/src/modules/activities/tasks/components/PageAddTaskButton.tsx index f878b5bd9..aca576922 100644 --- a/front/src/modules/activities/tasks/components/PageAddTaskButton.tsx +++ b/front/src/modules/activities/tasks/components/PageAddTaskButton.tsx @@ -4,17 +4,13 @@ import { PageAddButton } from '@/ui/layout/page/PageAddButton'; import { ActivityType } from '~/generated/graphql'; export const PageAddTaskButton = () => { - const { selectedFilters } = useFilter(); + const { selectedFilter } = useFilter(); const openCreateActivity = useOpenCreateActivityDrawer(); - const assigneeIdFilter = selectedFilters.find( - (filter) => filter.key === 'assigneeId', - ); - const handleClick = () => { openCreateActivity({ type: ActivityType.Task, - assigneeId: assigneeIdFilter?.value, + assigneeId: selectedFilter?.value, }); }; diff --git a/front/src/modules/activities/tasks/hooks/useCurrentUserDueTaskCount.ts b/front/src/modules/activities/tasks/hooks/useCurrentUserDueTaskCount.ts index c282fe3a7..d08a52f3d 100644 --- a/front/src/modules/activities/tasks/hooks/useCurrentUserDueTaskCount.ts +++ b/front/src/modules/activities/tasks/hooks/useCurrentUserDueTaskCount.ts @@ -17,12 +17,14 @@ export const useCurrentUserTaskCount = () => { completedAt: { equals: null }, ...(currentUser ? turnFilterIntoWhereClause({ - key: 'assigneeId', - type: 'entity', + fieldId: 'assigneeId', value: currentUser.id, operand: ViewFilterOperand.Is, displayValue: currentUser.displayName, displayAvatarUrl: currentUser.avatarUrl ?? undefined, + definition: { + type: 'entity', + }, }) : {}), }, diff --git a/front/src/modules/activities/tasks/hooks/useTasks.ts b/front/src/modules/activities/tasks/hooks/useTasks.ts index 7db8f20cd..0d690c2f7 100644 --- a/front/src/modules/activities/tasks/hooks/useTasks.ts +++ b/front/src/modules/activities/tasks/hooks/useTasks.ts @@ -7,7 +7,7 @@ import { ActivityType, useGetActivitiesQuery } from '~/generated/graphql'; import { parseDate } from '~/utils/date-utils'; export const useTasks = (entity?: ActivityTargetableEntity) => { - const { selectedFilters } = useFilter(); + const { selectedFilter } = useFilter(); const whereFilters = entity ? { @@ -20,12 +20,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => { }, }, } - : Object.assign( - {}, - ...selectedFilters.map((filter) => { - return turnFilterIntoWhereClause(filter); - }), - ); + : Object.assign({}, turnFilterIntoWhereClause(selectedFilter)); const { data: completeTasksData } = useGetActivitiesQuery({ variables: { @@ -35,7 +30,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => { ...whereFilters, }, }, - skip: !entity && selectedFilters.length === 0, + skip: !entity && !selectedFilter, }); const { data: incompleteTaskData } = useGetActivitiesQuery({ @@ -46,7 +41,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => { ...whereFilters, }, }, - skip: !entity && selectedFilters.length === 0, + skip: !entity && !selectedFilter, }); const todayOrPreviousTasks = incompleteTaskData?.findManyActivities.filter( diff --git a/front/src/modules/companies/board/components/CompanyBoard.tsx b/front/src/modules/companies/board/components/CompanyBoard.tsx index 421652dd6..69d36cb8f 100644 --- a/front/src/modules/companies/board/components/CompanyBoard.tsx +++ b/front/src/modules/companies/board/components/CompanyBoard.tsx @@ -1,15 +1,29 @@ +import styled from '@emotion/styled'; + import { BoardContext } from '@/companies/states/contexts/BoardContext'; +import { BoardOptionsDropdown } from '@/ui/layout/board/components/BoardOptionsDropdown'; +import { BoardOptionsDropdownId } from '@/ui/layout/board/components/constants/BoardOptionsDropdownId'; import { EntityBoard, EntityBoardProps, } from '@/ui/layout/board/components/EntityBoard'; import { EntityBoardActionBar } from '@/ui/layout/board/components/EntityBoardActionBar'; import { EntityBoardContextMenu } from '@/ui/layout/board/components/EntityBoardContextMenu'; +import { ViewBar } from '@/views/components/ViewBar'; +import { ViewScope } from '@/views/scopes/ViewScope'; import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions'; import { HooksCompanyBoardEffect } from '../../components/HooksCompanyBoardEffect'; import { CompanyBoardRecoilScopeContext } from '../../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext'; +const StyledContainer = styled.div` + display: flex; + flex-direction: column; + height: 100%; + overflow: auto; + width: 100%; +`; + type CompanyBoardProps = Pick< EntityBoardProps, 'onColumnAdd' | 'onColumnDelete' | 'onEditColumnTitle' @@ -20,24 +34,30 @@ export const CompanyBoard = ({ onColumnDelete, onEditColumnTitle, }: CompanyBoardProps) => { + const viewScopeId = 'company-board-view'; return ( - <> - - - - - - - - + + + + } + optionsDropdownScopeId={BoardOptionsDropdownId} + /> + + + + + + + ); }; diff --git a/front/src/modules/companies/components/HooksCompanyBoardEffect.tsx b/front/src/modules/companies/components/HooksCompanyBoardEffect.tsx index 1657e1e44..80f9c9b82 100644 --- a/front/src/modules/companies/components/HooksCompanyBoardEffect.tsx +++ b/front/src/modules/companies/components/HooksCompanyBoardEffect.tsx @@ -2,12 +2,19 @@ import { useEffect, useMemo } from 'react'; import { useSearchParams } from 'react-router-dom'; import { useRecoilState } from 'recoil'; +import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions'; import { turnFilterIntoWhereClause } from '@/ui/data/filter/utils/turnFilterIntoWhereClause'; import { useBoardActionBarEntries } from '@/ui/layout/board/hooks/useBoardActionBarEntries'; +import { useBoardContext } from '@/ui/layout/board/hooks/useBoardContext'; import { useBoardContextMenuEntries } from '@/ui/layout/board/hooks/useBoardContextMenuEntries'; +import { availableBoardCardFieldsScopedState } from '@/ui/layout/board/states/availableBoardCardFieldsScopedState'; +import { boardCardFieldsScopedState } from '@/ui/layout/board/states/boardCardFieldsScopedState'; import { isBoardLoadedState } from '@/ui/layout/board/states/isBoardLoadedState'; +import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useView } from '@/views/hooks/useView'; -import { useViewInternalStates } from '@/views/hooks/useViewInternalStates'; +import { useViewGetStates } from '@/views/hooks/useViewGetStates'; +import { ViewType } from '@/views/types/ViewType'; +import { viewFieldsToBoardFieldDefinitions } from '@/views/utils/viewFieldsToBoardFieldDefinitions'; import { Pipeline, PipelineProgressableType, @@ -22,20 +29,30 @@ import { useUpdateCompanyBoard } from '../hooks/useUpdateCompanyBoardColumns'; export const HooksCompanyBoardEffect = () => { const { - setAvailableFilters, - setAvailableSorts, + setAvailableFilterDefinitions, + setAvailableSortDefinitions, + setAvailableFieldDefinitions, setEntityCountInCurrentView, + setViewObjectId, + setViewType, } = useView(); - const { currentViewFilters } = useViewInternalStates(); - - useEffect(() => { - setAvailableFilters(opportunitiesBoardOptions.filters); - setAvailableSorts?.(opportunitiesBoardOptions.sorts); - }, [setAvailableFilters, setAvailableSorts]); + const { currentViewFilters, currentViewFields } = useViewGetStates(); const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState); + const { BoardRecoilScopeContext } = useBoardContext(); + + const [, setBoardCardFields] = useRecoilScopedState( + boardCardFieldsScopedState, + BoardRecoilScopeContext, + ); + + const [, setAvailableBoardCardFields] = useRecoilScopedState( + availableBoardCardFieldsScopedState, + BoardRecoilScopeContext, + ); + const updateCompanyBoard = useUpdateCompanyBoard(); const { data: pipelineData, loading: loadingGetPipelines } = @@ -51,6 +68,21 @@ export const HooksCompanyBoardEffect = () => { const pipeline = pipelineData?.findManyPipeline[0] as Pipeline | undefined; + useEffect(() => { + setAvailableFilterDefinitions(opportunitiesBoardOptions.filterDefinitions); + setAvailableSortDefinitions?.(opportunitiesBoardOptions.sortDefinitions); + setAvailableFieldDefinitions?.(pipelineAvailableFieldDefinitions); + }, [ + setAvailableFieldDefinitions, + setAvailableFilterDefinitions, + setAvailableSortDefinitions, + ]); + + useEffect(() => { + setViewObjectId?.('company'); + setViewType?.(ViewType.Kanban); + }, [setViewObjectId, setViewType]); + const pipelineStageIds = pipeline?.pipelineStages ?.map((pipelineStage) => pipelineStage.id) .flat(); @@ -107,6 +139,7 @@ export const HooksCompanyBoardEffect = () => { if (!loading && pipeline && pipelineProgresses && companiesData) { setActionBarEntries(); setContextMenuEntries(); + setAvailableBoardCardFields(pipelineAvailableFieldDefinitions); updateCompanyBoard(pipeline, pipelineProgresses, companiesData.companies); setEntityCountInCurrentView(companiesData.companies.length); } @@ -120,7 +153,19 @@ export const HooksCompanyBoardEffect = () => { setContextMenuEntries, searchParams, setEntityCountInCurrentView, + setAvailableBoardCardFields, ]); + useEffect(() => { + if (currentViewFields) { + setBoardCardFields( + viewFieldsToBoardFieldDefinitions( + currentViewFields, + pipelineAvailableFieldDefinitions, + ), + ); + } + }, [currentViewFields, setBoardCardFields]); + return <>; }; diff --git a/front/src/modules/companies/table/components/CompanyTable.tsx b/front/src/modules/companies/table/components/CompanyTable.tsx index e3081f729..cb19871b2 100644 --- a/front/src/modules/companies/table/components/CompanyTable.tsx +++ b/front/src/modules/companies/table/components/CompanyTable.tsx @@ -8,40 +8,59 @@ import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyT import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport'; import { DataTable } from '@/ui/data/data-table/components/DataTable'; import { DataTableEffect } from '@/ui/data/data-table/components/DataTableEffect'; +import { TableOptionsDropdownId } from '@/ui/data/data-table/constants/TableOptionsDropdownId'; import { TableContext } from '@/ui/data/data-table/contexts/TableContext'; import { useUpsertDataTableItem } from '@/ui/data/data-table/hooks/useUpsertDataTableItem'; import { TableOptionsDropdown } from '@/ui/data/data-table/options/components/TableOptionsDropdown'; import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumnsScopedState'; +import { tableFiltersScopedState } from '@/ui/data/data-table/states/tableFiltersScopedState'; +import { tableSortsScopedState } from '@/ui/data/data-table/states/tableSortsScopedState'; import { ViewBar } from '@/views/components/ViewBar'; import { useViewFields } from '@/views/hooks/internal/useViewFields'; import { useView } from '@/views/hooks/useView'; import { ViewScope } from '@/views/scopes/ViewScope'; import { columnDefinitionsToViewFields } from '@/views/utils/columnDefinitionToViewField'; import { viewFieldsToColumnDefinitions } from '@/views/utils/viewFieldsToColumnDefinitions'; +import { viewFiltersToFilters } from '@/views/utils/viewFiltersToFilters'; +import { viewSortsToSorts } from '@/views/utils/viewSortsToSorts'; import { UpdateOneCompanyMutationVariables, useGetCompaniesQuery, useGetWorkspaceMembersLazyQuery, useUpdateOneCompanyMutation, } from '~/generated/graphql'; -import { companyAvailableFilters } from '~/pages/companies/companies-filters'; -import { companyAvailableSorts } from '~/pages/companies/companies-sorts'; +import { companyTableFilterDefinitions } from '~/pages/companies/constants/companyTableFilterDefinitions'; +import { companyTableSortDefinitions } from '~/pages/companies/constants/companyTableSortDefinitions'; import CompanyTableEffect from './CompanyTableEffect'; +const StyledContainer = styled.div` + display: flex; + flex-direction: column; + height: 100%; + overflow: auto; +`; + export const CompanyTable = () => { - const tableViewScopeId = 'company-table'; + const viewScopeId = 'company-table-view'; + const tableScopeId = 'companies'; const setTableColumns = useSetRecoilState( - tableColumnsScopedState('companies'), + tableColumnsScopedState(tableScopeId), ); + const setTableFilters = useSetRecoilState( + tableFiltersScopedState(tableScopeId), + ); + + const setTableSorts = useSetRecoilState(tableSortsScopedState(tableScopeId)); + const [updateEntityMutation] = useUpdateOneCompanyMutation(); const upsertDataTableItem = useUpsertDataTableItem(); const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery(); - const { persistViewFields } = useViewFields(tableViewScopeId); - const { setCurrentViewFields, currentViewId } = useView({ - viewScopeId: tableViewScopeId, + const { persistViewFields } = useViewFields(viewScopeId); + const { setCurrentViewFields } = useView({ + viewScopeId, }); const { setContextMenuEntries } = useCompanyTableContextMenuEntries(); @@ -79,16 +98,9 @@ export const CompanyTable = () => { const { openCompanySpreadsheetImport: onImport } = useSpreadsheetCompanyImport(); - const StyledContainer = styled.div` - display: flex; - flex-direction: column; - height: 100%; - overflow: auto; - `; - return ( { setTableColumns( viewFieldsToColumnDefinitions( @@ -97,7 +109,12 @@ export const CompanyTable = () => { ), ); }} - onViewFiltersChange={() => {}} + onViewFiltersChange={(viewFilters) => { + setTableFilters(viewFiltersToFilters(viewFilters)); + }} + onViewSortsChange={(viewSorts) => { + setTableSorts(viewSortsToSorts(viewSorts)); + }} > { > } - optionsDropdownScopeId="table-dropdown-option" + optionsDropdownScopeId={TableOptionsDropdownId} /> { getRequestOptimisticEffectDefinition={ getCompaniesOptimisticEffectDefinition } - filterDefinitionArray={companyAvailableFilters} - sortDefinitionArray={companyAvailableSorts} + filterDefinitionArray={companyTableFilterDefinitions} + sortDefinitionArray={companyTableSortDefinitions} setContextMenuEntries={setContextMenuEntries} setActionBarEntries={setActionBarEntries} /> diff --git a/front/src/modules/companies/table/components/CompanyTableEffect.tsx b/front/src/modules/companies/table/components/CompanyTableEffect.tsx index 24a1b05ad..8e9d72d8a 100644 --- a/front/src/modules/companies/table/components/CompanyTableEffect.tsx +++ b/front/src/modules/companies/table/components/CompanyTableEffect.tsx @@ -6,14 +6,14 @@ import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scop import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useView } from '@/views/hooks/useView'; import { ViewType } from '@/views/types/ViewType'; -import { companyAvailableFilters } from '~/pages/companies/companies-filters'; -import { companyAvailableSorts } from '~/pages/companies/companies-sorts'; +import { companyTableFilterDefinitions } from '~/pages/companies/constants/companyTableFilterDefinitions'; +import { companyTableSortDefinitions } from '~/pages/companies/constants/companyTableSortDefinitions'; const CompanyTableEffect = () => { const { - setAvailableSorts, - setAvailableFilters, - setAvailableFields, + setAvailableSortDefinitions, + setAvailableFilterDefinitions, + setAvailableFieldDefinitions, setViewType, setViewObjectId, } = useView(); @@ -24,26 +24,21 @@ const CompanyTableEffect = () => { ); useEffect(() => { - setAvailableSorts?.(companyAvailableSorts); - setAvailableFilters?.(companyAvailableFilters); - setAvailableFields?.(companiesAvailableFieldDefinitions); + setAvailableSortDefinitions?.(companyTableSortDefinitions); + setAvailableFilterDefinitions?.(companyTableFilterDefinitions); + setAvailableFieldDefinitions?.(companiesAvailableFieldDefinitions); setViewObjectId?.('company'); setViewType?.(ViewType.Table); setAvailableTableColumns(companiesAvailableFieldDefinitions); }, [ - setAvailableFields, - setAvailableFilters, - setAvailableSorts, + setAvailableFieldDefinitions, + setAvailableFilterDefinitions, + setAvailableSortDefinitions, setAvailableTableColumns, setViewObjectId, setViewType, ]); - // useEffect(() => { - // if (currentViewFields) { - // setTableColumns([...currentViewFields].sort((a, b) => a.index - b.index)); - // } - // }, [currentViewFields, setTableColumns]); // useEffect(() => { // if (currentViewSorts) { diff --git a/front/src/modules/companies/table/components/CompanyTableMockDataEffect.tsx b/front/src/modules/companies/table/components/CompanyTableMockDataEffect.tsx index 3ab251a73..0ae168346 100644 --- a/front/src/modules/companies/table/components/CompanyTableMockDataEffect.tsx +++ b/front/src/modules/companies/table/components/CompanyTableMockDataEffect.tsx @@ -17,7 +17,6 @@ export const CompanyTableMockDataEffect = () => { useEffect(() => { setDataTableData(mockedCompaniesData, [], []); - setTableColumns(companiesAvailableFieldDefinitions); }, [setDataTableData, setTableColumns]); diff --git a/front/src/modules/metadata/components/MetadataObjectNavItems.tsx b/front/src/modules/metadata/components/MetadataObjectNavItems.tsx index 9241a61a4..0426ba660 100644 --- a/front/src/modules/metadata/components/MetadataObjectNavItems.tsx +++ b/front/src/modules/metadata/components/MetadataObjectNavItems.tsx @@ -1,6 +1,7 @@ import { useNavigate } from 'react-router-dom'; -import { IconBuildingSkyscraper } from '@/ui/display/icon'; +import { Icon123 } from '@/ui/input/constants/icons'; +import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons'; import NavItem from '@/ui/navigation/navbar/components/NavItem'; import { capitalize } from '~/utils/string/capitalize'; @@ -10,22 +11,26 @@ export const MetadataObjectNavItems = () => { const { metadataObjects } = useFindManyMetadataObjects(); const navigate = useNavigate(); + const { icons } = useLazyLoadIcons(); return ( <> {metadataObjects .filter((metadataObject) => !!metadataObject.isActive) - .map((metadataObject) => ( - { - navigate(`/objects/${metadataObject.namePlural}`); - }} - /> - ))} + .filter((metadataObjects) => !metadataObjects.namePlural.endsWith('V2')) + .map((metadataObject) => { + return ( + { + navigate(`/objects/${metadataObject.namePlural}`); + }} + /> + ); + })} ); }; diff --git a/front/src/modules/metadata/components/ObjectTableEffect.tsx b/front/src/modules/metadata/components/ObjectTableEffect.tsx index b1f09e988..d5e53d32b 100644 --- a/front/src/modules/metadata/components/ObjectTableEffect.tsx +++ b/front/src/modules/metadata/components/ObjectTableEffect.tsx @@ -9,9 +9,9 @@ import { useMetadataObjectInContext } from '../hooks/useMetadataObjectInContext' export const ObjectTableEffect = () => { const { - setAvailableSorts, - setAvailableFilters, - setAvailableFields, + setAvailableSortDefinitions, + setAvailableFilterDefinitions, + setAvailableFieldDefinitions, setViewType, setViewObjectId, } = useView(); @@ -38,22 +38,22 @@ export const ObjectTableEffect = () => { ); useEffect(() => { - setAvailableSorts?.([]); // TODO: extract from metadata fields - setAvailableFilters?.([]); // TODO: extract from metadata fields - setAvailableFields?.(columnDefinitions); + setAvailableSortDefinitions?.([]); // TODO: extract from metadata fields + setAvailableFilterDefinitions?.([]); // TODO: extract from metadata fields + setAvailableFieldDefinitions?.(columnDefinitions); setViewObjectId?.(objectNamePlural); setViewType?.(ViewType.Table); setAvailableTableColumns(columnDefinitions); }, [ - setAvailableFields, - setAvailableFilters, - setAvailableSorts, setAvailableTableColumns, setViewObjectId, setViewType, columnDefinitions, objectNamePlural, + setAvailableSortDefinitions, + setAvailableFilterDefinitions, + setAvailableFieldDefinitions, ]); // useEffect(() => { diff --git a/front/src/modules/metadata/hooks/useFindOneMetadataObject.ts b/front/src/modules/metadata/hooks/useFindOneMetadataObject.ts index 6195e70f6..6e1413bd7 100644 --- a/front/src/modules/metadata/hooks/useFindOneMetadataObject.ts +++ b/front/src/modules/metadata/hooks/useFindOneMetadataObject.ts @@ -6,6 +6,7 @@ import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata'; import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier'; import { formatMetadataFieldAsColumnDefinition } from '../utils/formatMetadataFieldAsColumnDefinition'; import { generateCreateOneObjectMutation } from '../utils/generateCreateOneObjectMutation'; +import { generateDeleteOneObjectMutation } from '../utils/generateDeleteOneObjectMutation'; import { generateFindManyCustomObjectsQuery } from '../utils/generateFindManyCustomObjectsQuery'; import { generateFindOneCustomObjectQuery } from '../utils/generateFindOneCustomObjectQuery'; import { generateUpdateOneObjectMutation } from '../utils/generateUpdateOneObjectMutation'; @@ -79,7 +80,7 @@ export const useFindOneMetadataObject = ({ // TODO: implement backend delete const deleteOneMutation = foundMetadataObject - ? generateCreateOneObjectMutation({ + ? generateDeleteOneObjectMutation({ metadataObject: foundMetadataObject, }) : gql` diff --git a/front/src/modules/metadata/hooks/useSetDataTableData.ts b/front/src/modules/metadata/hooks/useSetDataTableData.ts index 0aae7fe38..df0095e45 100644 --- a/front/src/modules/metadata/hooks/useSetDataTableData.ts +++ b/front/src/modules/metadata/hooks/useSetDataTableData.ts @@ -7,7 +7,7 @@ import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scop import { tableRowIdsState } from '@/ui/data/data-table/states/tableRowIdsState'; import { entityFieldsFamilyState } from '@/ui/data/field/states/entityFieldsFamilyState'; import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId'; -import { availableSortsScopedState } from '@/views/states/availableSortsScopedState'; +import { availableSortDefinitionsScopedState } from '@/views/states/availableSortDefinitionsScopedState'; export const useSetObjectDataTableData = () => { const resetTableRowSelection = useResetTableRowSelection(); @@ -41,7 +41,10 @@ export const useSetObjectDataTableData = () => { set(numberOfTableRowsState, entityIds.length); - set(availableSortsScopedState({ scopeId: tableContextScopeId }), []); + set( + availableSortDefinitionsScopedState({ scopeId: tableContextScopeId }), + [], + ); set(isFetchingDataTableDataState, false); }, diff --git a/front/src/modules/people/hooks/usePeopleTableContextMenuEntries.tsx b/front/src/modules/people/hooks/usePersonTableContextMenuEntries.tsx similarity index 100% rename from front/src/modules/people/hooks/usePeopleTableContextMenuEntries.tsx rename to front/src/modules/people/hooks/usePersonTableContextMenuEntries.tsx diff --git a/front/src/modules/people/table/components/PeopleTable.tsx b/front/src/modules/people/table/components/PeopleTable.tsx deleted file mode 100644 index 4fea9d0dd..000000000 --- a/front/src/modules/people/table/components/PeopleTable.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import styled from '@emotion/styled'; - -import { getPeopleOptimisticEffectDefinition } from '@/people/graphql/optimistic-effect-definitions/getPeopleOptimisticEffectDefinition'; -import { usePersonTableContextMenuEntries } from '@/people/hooks/usePeopleTableContextMenuEntries'; -import { usePersonTableActionBarEntries } from '@/people/hooks/usePersonTableActionBarEntries'; -import { useSpreadsheetPersonImport } from '@/people/hooks/useSpreadsheetPersonImport'; -import { DataTable } from '@/ui/data/data-table/components/DataTable'; -import { DataTableEffect } from '@/ui/data/data-table/components/DataTableEffect'; -import { TableContext } from '@/ui/data/data-table/contexts/TableContext'; -import { useUpsertDataTableItem } from '@/ui/data/data-table/hooks/useUpsertDataTableItem'; -import { TableOptionsDropdown } from '@/ui/data/data-table/options/components/TableOptionsDropdown'; -import { ViewBar } from '@/views/components/ViewBar'; -import { ViewBarEffect } from '@/views/components/ViewBarEffect'; -import { ViewScope } from '@/views/scopes/ViewScope'; -import { - UpdateOnePersonMutationVariables, - useGetPeopleQuery, - useUpdateOnePersonMutation, -} from '~/generated/graphql'; -import { peopleAvailableFilters } from '~/pages/people/people-filters'; -import { peopleAvailableSorts } from '~/pages/people/people-sorts'; - -import PersonTableEffect from './PersonTableEffect'; - -export const PeopleTable = () => { - const tableViewScopeId = 'person-table'; - - const [updateEntityMutation] = useUpdateOnePersonMutation(); - const upsertDataTableItem = useUpsertDataTableItem(); - - const { setContextMenuEntries } = usePersonTableContextMenuEntries(); - const { setActionBarEntries } = usePersonTableActionBarEntries(); - - const { openPersonSpreadsheetImport: onImport } = - useSpreadsheetPersonImport(); - - const StyledContainer = styled.div` - display: flex; - flex-direction: column; - height: 100%; - overflow: auto; - `; - - return ( - {}} - onViewSortsChange={() => {}} - onViewFiltersChange={() => {}} - > - - {}, - }} - > - - - } - optionsDropdownScopeId="table-dropdown-option" - /> - - - - - updateEntityMutation({ - variables, - onCompleted: (data) => { - if (!data.updateOnePerson) { - return; - } - upsertDataTableItem(data.updateOnePerson); - }, - }) - } - /> - - - - ); -}; diff --git a/front/src/modules/people/table/components/PersonTable.tsx b/front/src/modules/people/table/components/PersonTable.tsx new file mode 100644 index 000000000..b6fbaeb33 --- /dev/null +++ b/front/src/modules/people/table/components/PersonTable.tsx @@ -0,0 +1,135 @@ +import styled from '@emotion/styled'; +import { useRecoilCallback, useSetRecoilState } from 'recoil'; + +import { peopleAvailableFieldDefinitions } from '@/people/constants/peopleAvailableFieldDefinitions'; +import { getPeopleOptimisticEffectDefinition } from '@/people/graphql/optimistic-effect-definitions/getPeopleOptimisticEffectDefinition'; +import { usePersonTableActionBarEntries } from '@/people/hooks/usePersonTableActionBarEntries'; +import { usePersonTableContextMenuEntries } from '@/people/hooks/usePersonTableContextMenuEntries'; +import { useSpreadsheetPersonImport } from '@/people/hooks/useSpreadsheetPersonImport'; +import { DataTable } from '@/ui/data/data-table/components/DataTable'; +import { DataTableEffect } from '@/ui/data/data-table/components/DataTableEffect'; +import { TableContext } from '@/ui/data/data-table/contexts/TableContext'; +import { useUpsertDataTableItem } from '@/ui/data/data-table/hooks/useUpsertDataTableItem'; +import { TableOptionsDropdown } from '@/ui/data/data-table/options/components/TableOptionsDropdown'; +import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumnsScopedState'; +import { tableFiltersScopedState } from '@/ui/data/data-table/states/tableFiltersScopedState'; +import { tableSortsScopedState } from '@/ui/data/data-table/states/tableSortsScopedState'; +import { ViewBar } from '@/views/components/ViewBar'; +import { useViewFields } from '@/views/hooks/internal/useViewFields'; +import { useView } from '@/views/hooks/useView'; +import { ViewScope } from '@/views/scopes/ViewScope'; +import { columnDefinitionsToViewFields } from '@/views/utils/columnDefinitionToViewField'; +import { viewFieldsToColumnDefinitions } from '@/views/utils/viewFieldsToColumnDefinitions'; +import { viewFiltersToFilters } from '@/views/utils/viewFiltersToFilters'; +import { viewSortsToSorts } from '@/views/utils/viewSortsToSorts'; +import { + UpdateOnePersonMutationVariables, + useGetPeopleQuery, + useUpdateOnePersonMutation, +} from '~/generated/graphql'; +import { personTableFilterDefinitions } from '~/pages/people/constants/personTableFilterDefinitions'; +import { personTableSortDefinitions } from '~/pages/people/constants/personTableSortDefinitions'; + +import PersonTableEffect from './PersonTableEffect'; + +export const PersonTable = () => { + const viewScopeId = 'person-table-view'; + const tableScopeId = 'people'; + const setTableColumns = useSetRecoilState( + tableColumnsScopedState(tableScopeId), + ); + + const setTableFilters = useSetRecoilState( + tableFiltersScopedState(tableScopeId), + ); + + const setTableSorts = useSetRecoilState(tableSortsScopedState(tableScopeId)); + + const [updateEntityMutation] = useUpdateOnePersonMutation(); + const upsertDataTableItem = useUpsertDataTableItem(); + + const { persistViewFields } = useViewFields(viewScopeId); + const { setCurrentViewFields } = useView({ + viewScopeId, + }); + + const { setContextMenuEntries } = usePersonTableContextMenuEntries(); + const { setActionBarEntries } = usePersonTableActionBarEntries(); + + const updatePerson = async (variables: UpdateOnePersonMutationVariables) => { + updateEntityMutation({ + variables: variables, + onCompleted: (data) => { + if (!data.updateOnePerson) { + return; + } + upsertDataTableItem(data.updateOnePerson); + }, + }); + }; + + const { openPersonSpreadsheetImport: onImport } = + useSpreadsheetPersonImport(); + + const StyledContainer = styled.div` + display: flex; + flex-direction: column; + height: 100%; + overflow: auto; + `; + + return ( + { + setTableColumns( + viewFieldsToColumnDefinitions( + viewFields, + peopleAvailableFieldDefinitions, + ), + ); + }} + onViewFiltersChange={(viewFilters) => { + setTableFilters(viewFiltersToFilters(viewFilters)); + }} + onViewSortsChange={(viewSorts) => { + setTableSorts(viewSortsToSorts(viewSorts)); + }} + > + + (columns) => { + setCurrentViewFields?.(columnDefinitionsToViewFields(columns)); + persistViewFields(columnDefinitionsToViewFields(columns)); + }), + }} + > + } + optionsDropdownScopeId="table-dropdown-option" + /> + + + updatePerson(variables)} + /> + + + + ); +}; diff --git a/front/src/modules/people/table/components/PersonTableEffect.tsx b/front/src/modules/people/table/components/PersonTableEffect.tsx index 6cddfabd0..6f6f7bf01 100644 --- a/front/src/modules/people/table/components/PersonTableEffect.tsx +++ b/front/src/modules/people/table/components/PersonTableEffect.tsx @@ -7,14 +7,14 @@ import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumn import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useView } from '@/views/hooks/useView'; import { ViewType } from '@/views/types/ViewType'; -import { peopleAvailableFilters } from '~/pages/people/people-filters'; -import { peopleAvailableSorts } from '~/pages/people/people-sorts'; +import { personTableFilterDefinitions } from '~/pages/people/constants/personTableFilterDefinitions'; +import { personTableSortDefinitions } from '~/pages/people/constants/personTableSortDefinitions'; const PeopleTableEffect = () => { const { - setAvailableSorts, - setAvailableFilters, - setAvailableFields, + setAvailableSortDefinitions, + setAvailableFilterDefinitions, + setAvailableFieldDefinitions, setViewType, setViewObjectId, } = useView(); @@ -30,17 +30,17 @@ const PeopleTableEffect = () => { ); useEffect(() => { - setAvailableSorts?.(peopleAvailableSorts); - setAvailableFilters?.(peopleAvailableFilters); - setAvailableFields?.(peopleAvailableFieldDefinitions); + setAvailableSortDefinitions?.(personTableSortDefinitions); + setAvailableFilterDefinitions?.(personTableFilterDefinitions); + setAvailableFieldDefinitions?.(peopleAvailableFieldDefinitions); setViewObjectId?.('person'); setViewType?.(ViewType.Table); setAvailableTableColumns(peopleAvailableFieldDefinitions); }, [ - setAvailableFields, - setAvailableFilters, - setAvailableSorts, + setAvailableFieldDefinitions, + setAvailableFilterDefinitions, + setAvailableSortDefinitions, setAvailableTableColumns, setTableColumns, setViewObjectId, diff --git a/front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx b/front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx index 65f1c96da..1cf9fdad7 100644 --- a/front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx +++ b/front/src/modules/pipeline/constants/pipelineAvailableFieldDefinitions.tsx @@ -1,3 +1,4 @@ +import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition'; import { FieldDateMetadata, FieldMetadata, @@ -12,10 +13,9 @@ import { IconUser, } from '@/ui/display/icon'; import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; -import { BoardFieldDefinition } from '@/ui/layout/board/types/BoardFieldDefinition'; import { Person } from '~/generated/graphql'; -export const pipelineAvailableFieldDefinitions: BoardFieldDefinition[] = +export const pipelineAvailableFieldDefinitions: ColumnDefinition[] = [ { fieldId: 'closeDate', @@ -26,10 +26,11 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition, + } satisfies ColumnDefinition, { fieldId: 'amount', label: 'Amount', @@ -40,9 +41,10 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition, + } satisfies ColumnDefinition, { fieldId: 'probability', label: 'Probability', @@ -52,10 +54,11 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition, + } satisfies ColumnDefinition, { fieldId: 'pointOfContact', label: 'Point of Contact', @@ -67,6 +70,7 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition { @@ -76,5 +80,5 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition, + } satisfies ColumnDefinition, ]; diff --git a/front/src/modules/settings/data-model/object-edit/SettingsObjectIconSection.tsx b/front/src/modules/settings/data-model/object-edit/SettingsObjectIconSection.tsx index 1f9030bfa..00835f3c5 100644 --- a/front/src/modules/settings/data-model/object-edit/SettingsObjectIconSection.tsx +++ b/front/src/modules/settings/data-model/object-edit/SettingsObjectIconSection.tsx @@ -55,7 +55,12 @@ export const SettingsObjectIconSection = ({ Arrow right - + {Icon && ( + + )} ); diff --git a/front/src/modules/ui/data/data-table/components/DataTableHeader.tsx b/front/src/modules/ui/data/data-table/components/DataTableHeader.tsx index 208da5452..40e2b282a 100644 --- a/front/src/modules/ui/data/data-table/components/DataTableHeader.tsx +++ b/front/src/modules/ui/data/data-table/components/DataTableHeader.tsx @@ -168,37 +168,35 @@ export const DataTableHeader = () => { > - {[...visibleTableColumns] - .sort((columnA, columnB) => columnA.position - columnB.position) - .map((column) => ( - - - - - { - setResizedFieldKey(column.fieldId); - }} + {visibleTableColumns.map((column) => ( + + + - - ))} + + { + setResizedFieldKey(column.fieldId); + }} + /> + + ))} {hiddenTableColumns.length > 0 && ( diff --git a/front/src/modules/ui/data/data-table/hooks/useSetDataTableData.ts b/front/src/modules/ui/data/data-table/hooks/useSetDataTableData.ts index 4ef19f7aa..9e77272a5 100644 --- a/front/src/modules/ui/data/data-table/hooks/useSetDataTableData.ts +++ b/front/src/modules/ui/data/data-table/hooks/useSetDataTableData.ts @@ -4,7 +4,7 @@ import { entityFieldsFamilyState } from '@/ui/data/field/states/entityFieldsFami import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition'; import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId'; import { useView } from '@/views/hooks/useView'; -import { availableSortsScopedState } from '@/views/states/availableSortsScopedState'; +import { availableSortDefinitionsScopedState } from '@/views/states/availableSortDefinitionsScopedState'; import { SortDefinition } from '../../sort/types/SortDefinition'; import { isFetchingDataTableDataState } from '../states/isFetchingDataTableDataState'; @@ -54,7 +54,7 @@ export const useSetDataTableData = () => { setEntityCountInCurrentView(entityIds.length); set( - availableSortsScopedState({ scopeId: tableContextScopeId }), + availableSortDefinitionsScopedState({ scopeId: tableContextScopeId }), sortDefinitionArray, ); diff --git a/front/src/modules/ui/data/data-table/options/components/TableOptionsDropdownContent.tsx b/front/src/modules/ui/data/data-table/options/components/TableOptionsDropdownContent.tsx index 20ac0e2fb..1357220d6 100644 --- a/front/src/modules/ui/data/data-table/options/components/TableOptionsDropdownContent.tsx +++ b/front/src/modules/ui/data/data-table/options/components/TableOptionsDropdownContent.tsx @@ -13,7 +13,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection'; import { useView } from '@/views/hooks/useView'; -import { useViewInternalStates } from '@/views/hooks/useViewInternalStates'; +import { useViewGetStates } from '@/views/hooks/useViewGetStates'; import { useTableColumns } from '../../hooks/useTableColumns'; import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext'; @@ -29,7 +29,7 @@ export const TableOptionsDropdownContent = ({ onImport?: () => void; }) => { const { setViewEditMode, handleViewNameSubmit } = useView(); - const { viewEditMode, currentView } = useViewInternalStates(); + const { viewEditMode, currentView } = useViewGetStates(); const { closeDropdown } = useDropdown(); diff --git a/front/src/modules/ui/data/filter/components/FilterDropdownButton.tsx b/front/src/modules/ui/data/filter/components/FilterDropdownButton.tsx index f4ae61453..17082cf92 100644 --- a/front/src/modules/ui/data/filter/components/FilterDropdownButton.tsx +++ b/front/src/modules/ui/data/filter/components/FilterDropdownButton.tsx @@ -12,12 +12,13 @@ type FilterDropdownButtonProps = { export const FilterDropdownButton = ({ hotkeyScope, }: FilterDropdownButtonProps) => { - const { availableFilters } = useFilter(); + const { availableFilterDefinitions } = useFilter(); const hasOnlyOneEntityFilter = - availableFilters.length === 1 && availableFilters[0].type === 'entity'; + availableFilterDefinitions.length === 1 && + availableFilterDefinitions[0].type === 'entity'; - if (!availableFilters.length) { + if (!availableFilterDefinitions.length) { return <>; } diff --git a/front/src/modules/ui/data/filter/components/FilterDropdownDateSearchInput.tsx b/front/src/modules/ui/data/filter/components/FilterDropdownDateSearchInput.tsx index 7ec5056d1..09a994d3f 100644 --- a/front/src/modules/ui/data/filter/components/FilterDropdownDateSearchInput.tsx +++ b/front/src/modules/ui/data/filter/components/FilterDropdownDateSearchInput.tsx @@ -1,5 +1,4 @@ import { InternalDatePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker'; -import { useUpsertFilter } from '@/views/hooks/useUpsertFilter'; import { useFilter } from '../hooks/useFilter'; @@ -8,19 +7,18 @@ export const FilterDropdownDateSearchInput = () => { filterDefinitionUsedInDropdown, selectedOperandInDropdown, setIsFilterDropdownUnfolded, + selectFilter, } = useFilter(); - const upsertFilter = useUpsertFilter(); - const handleChange = (date: Date) => { if (!filterDefinitionUsedInDropdown || !selectedOperandInDropdown) return; - upsertFilter({ - key: filterDefinitionUsedInDropdown.key, - type: filterDefinitionUsedInDropdown.type, + selectFilter?.({ + fieldId: filterDefinitionUsedInDropdown.fieldId, value: date.toISOString(), operand: selectedOperandInDropdown, displayValue: date.toLocaleDateString(), + definition: filterDefinitionUsedInDropdown, }); setIsFilterDropdownUnfolded(false); diff --git a/front/src/modules/ui/data/filter/components/FilterDropdownEntitySearchSelect.tsx b/front/src/modules/ui/data/filter/components/FilterDropdownEntitySearchSelect.tsx index cd180146a..2ce76876d 100644 --- a/front/src/modules/ui/data/filter/components/FilterDropdownEntitySearchSelect.tsx +++ b/front/src/modules/ui/data/filter/components/FilterDropdownEntitySearchSelect.tsx @@ -1,11 +1,8 @@ import { useEffect, useState } from 'react'; -import { useFilterCurrentlyEdited } from '@/ui/data/filter/hooks/useFilterCurrentlyEdited'; import { EntitiesForMultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect'; import { SingleEntitySelectBase } from '@/ui/input/relation-picker/components/SingleEntitySelectBase'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; -import { useRemoveFilter } from '@/views/hooks/useRemoveFilter'; -import { useUpsertFilter } from '@/views/hooks/useUpsertFilter'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { useFilter } from '../hooks/useFilter'; @@ -16,20 +13,16 @@ export const FilterDropdownEntitySearchSelect = ({ entitiesForSelect: EntitiesForMultipleEntitySelect; }) => { const { - filterDropdownSelectedEntityId, setFilterDropdownSelectedEntityId, filterDefinitionUsedInDropdown, selectedOperandInDropdown, filterDropdownSearchInput, + selectedFilter, + selectFilter, } = useFilter(); const [isAllEntitySelected, setIsAllEntitySelected] = useState(false); - const upsertFilter = useUpsertFilter(); - const removeFilter = useRemoveFilter(); - - const filterCurrentlyEdited = useFilterCurrentlyEdited(); - const handleUserSelected = ( selectedEntity: EntityForSelect | null | undefined, ) => { @@ -45,24 +38,16 @@ export const FilterDropdownEntitySearchSelect = ({ setIsAllEntitySelected(false); } - const clickedOnAlreadySelectedEntity = - selectedEntity.id === filterDropdownSelectedEntityId; + setFilterDropdownSelectedEntityId(selectedEntity.id); - if (clickedOnAlreadySelectedEntity) { - removeFilter(filterDefinitionUsedInDropdown.key); - setFilterDropdownSelectedEntityId(null); - } else { - setFilterDropdownSelectedEntityId(selectedEntity.id); - - upsertFilter({ - displayValue: selectedEntity.name, - key: filterDefinitionUsedInDropdown.key, - operand: selectedOperandInDropdown, - type: filterDefinitionUsedInDropdown.type, - value: selectedEntity.id, - displayAvatarUrl: selectedEntity.avatarUrl, - }); - } + selectFilter?.({ + displayValue: selectedEntity.name, + fieldId: filterDefinitionUsedInDropdown.fieldId, + operand: selectedOperandInDropdown, + value: selectedEntity.id, + displayAvatarUrl: selectedEntity.avatarUrl, + definition: filterDefinitionUsedInDropdown, + }); }; const isAllEntitySelectShown = @@ -81,36 +66,30 @@ export const FilterDropdownEntitySearchSelect = ({ ) { return; } - if (isAllEntitySelected) { - setIsAllEntitySelected(false); - removeFilter(filterDefinitionUsedInDropdown.key); - } else { - setIsAllEntitySelected(true); + setIsAllEntitySelected(true); + setFilterDropdownSelectedEntityId(null); - setFilterDropdownSelectedEntityId(null); - - upsertFilter({ - displayValue: filterDefinitionUsedInDropdown.selectAllLabel, - key: filterDefinitionUsedInDropdown.key, - operand: ViewFilterOperand.IsNotNull, - type: filterDefinitionUsedInDropdown.type, - value: '', - }); - } + selectFilter?.({ + displayValue: filterDefinitionUsedInDropdown.selectAllLabel, + fieldId: filterDefinitionUsedInDropdown.fieldId, + operand: ViewFilterOperand.IsNotNull, + value: '', + definition: filterDefinitionUsedInDropdown, + }); }; useEffect(() => { - if (!filterCurrentlyEdited) { + if (!selectedFilter) { setFilterDropdownSelectedEntityId(null); } else { - setFilterDropdownSelectedEntityId(filterCurrentlyEdited.value); + setFilterDropdownSelectedEntityId(selectedFilter.value); setIsAllEntitySelected( - filterCurrentlyEdited.operand === ViewFilterOperand.IsNotNull, + selectedFilter.operand === ViewFilterOperand.IsNotNull, ); } }, [ - filterCurrentlyEdited, + selectedFilter, setFilterDropdownSelectedEntityId, entitiesForSelect.selectedEntities, ]); diff --git a/front/src/modules/ui/data/filter/components/FilterDropdownFilterSelect.tsx b/front/src/modules/ui/data/filter/components/FilterDropdownFilterSelect.tsx index 556282495..6abe35548 100644 --- a/front/src/modules/ui/data/filter/components/FilterDropdownFilterSelect.tsx +++ b/front/src/modules/ui/data/filter/components/FilterDropdownFilterSelect.tsx @@ -11,32 +11,32 @@ export const FilterDropdownFilterSelect = () => { setFilterDefinitionUsedInDropdown, setSelectedOperandInDropdown, setFilterDropdownSearchInput, - availableFilters, + availableFilterDefinitions, } = useFilter(); const setHotkeyScope = useSetHotkeyScope(); return ( - {availableFilters.map((availableFilter, index) => ( + {availableFilterDefinitions.map((availableFilterDefinition, index) => ( { - setFilterDefinitionUsedInDropdown(availableFilter); + setFilterDefinitionUsedInDropdown(availableFilterDefinition); - if (availableFilter.type === 'entity') { + if (availableFilterDefinition.type === 'entity') { setHotkeyScope(RelationPickerHotkeyScope.RelationPicker); } setSelectedOperandInDropdown( - getOperandsForFilterType(availableFilter.type)?.[0], + getOperandsForFilterType(availableFilterDefinition.type)?.[0], ); setFilterDropdownSearchInput(''); }} - LeftIcon={availableFilter.Icon} - text={availableFilter.label} + LeftIcon={availableFilterDefinition.Icon} + text={availableFilterDefinition.label} /> ))} diff --git a/front/src/modules/ui/data/filter/components/FilterDropdownNumberSearchInput.tsx b/front/src/modules/ui/data/filter/components/FilterDropdownNumberSearchInput.tsx index ea8fc8585..7e12a60cd 100644 --- a/front/src/modules/ui/data/filter/components/FilterDropdownNumberSearchInput.tsx +++ b/front/src/modules/ui/data/filter/components/FilterDropdownNumberSearchInput.tsx @@ -2,16 +2,14 @@ import { ChangeEvent } from 'react'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; -import { useRemoveFilter } from '../../../../views/hooks/useRemoveFilter'; -import { useUpsertFilter } from '../../../../views/hooks/useUpsertFilter'; import { useFilter } from '../hooks/useFilter'; export const FilterDropdownNumberSearchInput = () => { - const { selectedOperandInDropdown, filterDefinitionUsedInDropdown } = - useFilter(); - - const upsertFilter = useUpsertFilter(); - const removeFilter = useRemoveFilter(); + const { + selectedOperandInDropdown, + filterDefinitionUsedInDropdown, + selectFilter, + } = useFilter(); return ( filterDefinitionUsedInDropdown && @@ -21,17 +19,13 @@ export const FilterDropdownNumberSearchInput = () => { type="number" placeholder={filterDefinitionUsedInDropdown.label} onChange={(event: ChangeEvent) => { - if (event.target.value === '') { - removeFilter(filterDefinitionUsedInDropdown.key); - } else { - upsertFilter({ - key: filterDefinitionUsedInDropdown.key, - type: filterDefinitionUsedInDropdown.type, - value: event.target.value, - operand: selectedOperandInDropdown, - displayValue: event.target.value, - }); - } + selectFilter?.({ + fieldId: filterDefinitionUsedInDropdown.fieldId, + value: event.target.value, + operand: selectedOperandInDropdown, + displayValue: event.target.value, + definition: filterDefinitionUsedInDropdown, + }); }} /> ) diff --git a/front/src/modules/ui/data/filter/components/FilterDropdownOperandSelect.tsx b/front/src/modules/ui/data/filter/components/FilterDropdownOperandSelect.tsx index 7640752e2..83a511f16 100644 --- a/front/src/modules/ui/data/filter/components/FilterDropdownOperandSelect.tsx +++ b/front/src/modules/ui/data/filter/components/FilterDropdownOperandSelect.tsx @@ -2,9 +2,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; -import { useUpsertFilter } from '../../../../views/hooks/useUpsertFilter'; import { useFilter } from '../hooks/useFilter'; -import { useFilterCurrentlyEdited } from '../hooks/useFilterCurrentlyEdited'; import { getOperandLabel } from '../utils/getOperandLabel'; import { getOperandsForFilterType } from '../utils/getOperandsForFilterType'; @@ -14,27 +12,25 @@ export const FilterDropdownOperandSelect = () => { setSelectedOperandInDropdown, isFilterDropdownOperandSelectUnfolded, setIsFilterDropdownOperandSelectUnfolded, + selectedFilter, + selectFilter, } = useFilter(); const operandsForFilterType = getOperandsForFilterType( filterDefinitionUsedInDropdown?.type, ); - const filterCurrentlyEdited = useFilterCurrentlyEdited(); - - const upsertFilter = useUpsertFilter(); - const handleOperangeChange = (newOperand: ViewFilterOperand) => { setSelectedOperandInDropdown(newOperand); setIsFilterDropdownOperandSelectUnfolded(false); - if (filterDefinitionUsedInDropdown && filterCurrentlyEdited) { - upsertFilter({ - key: filterCurrentlyEdited.key, - displayValue: filterCurrentlyEdited.displayValue, + if (filterDefinitionUsedInDropdown && selectedFilter) { + selectFilter?.({ + fieldId: selectedFilter.fieldId, + displayValue: selectedFilter.displayValue, operand: newOperand, - type: filterCurrentlyEdited.type, - value: filterCurrentlyEdited.value, + value: selectedFilter.value, + definition: filterDefinitionUsedInDropdown, }); } }; diff --git a/front/src/modules/ui/data/filter/components/FilterDropdownTextSearchInput.tsx b/front/src/modules/ui/data/filter/components/FilterDropdownTextSearchInput.tsx index b65cc5dc7..02b29946d 100644 --- a/front/src/modules/ui/data/filter/components/FilterDropdownTextSearchInput.tsx +++ b/front/src/modules/ui/data/filter/components/FilterDropdownTextSearchInput.tsx @@ -2,10 +2,7 @@ import { ChangeEvent } from 'react'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; -import { useRemoveFilter } from '../../../../views/hooks/useRemoveFilter'; -import { useUpsertFilter } from '../../../../views/hooks/useUpsertFilter'; import { useFilter } from '../hooks/useFilter'; -import { useFilterCurrentlyEdited } from '../hooks/useFilterCurrentlyEdited'; export const FilterDropdownTextSearchInput = () => { const { @@ -13,13 +10,10 @@ export const FilterDropdownTextSearchInput = () => { selectedOperandInDropdown, filterDropdownSearchInput, setFilterDropdownSearchInput, + selectedFilter, + selectFilter, } = useFilter(); - const upsertFilter = useUpsertFilter(); - const removeFilter = useRemoveFilter(); - - const filterCurrentlyEdited = useFilterCurrentlyEdited(); - return ( filterDefinitionUsedInDropdown && selectedOperandInDropdown && ( @@ -27,21 +21,17 @@ export const FilterDropdownTextSearchInput = () => { autoFocus type="text" placeholder={filterDefinitionUsedInDropdown.label} - value={filterCurrentlyEdited?.value ?? filterDropdownSearchInput} + value={selectedFilter?.value ?? filterDropdownSearchInput} onChange={(event: ChangeEvent) => { setFilterDropdownSearchInput(event.target.value); - if (event.target.value === '') { - removeFilter(filterDefinitionUsedInDropdown.key); - } else { - upsertFilter({ - key: filterDefinitionUsedInDropdown.key, - type: filterDefinitionUsedInDropdown.type, - value: event.target.value, - operand: selectedOperandInDropdown, - displayValue: event.target.value, - }); - } + selectFilter?.({ + fieldId: filterDefinitionUsedInDropdown.fieldId, + value: event.target.value, + operand: selectedOperandInDropdown, + displayValue: event.target.value, + definition: filterDefinitionUsedInDropdown, + }); }} /> ) diff --git a/front/src/modules/ui/data/filter/components/SingleEntityFilterDropdownButton.tsx b/front/src/modules/ui/data/filter/components/SingleEntityFilterDropdownButton.tsx index 1d91b8126..2962868bd 100644 --- a/front/src/modules/ui/data/filter/components/SingleEntityFilterDropdownButton.tsx +++ b/front/src/modules/ui/data/filter/components/SingleEntityFilterDropdownButton.tsx @@ -21,13 +21,13 @@ export const SingleEntityFilterDropdownButton = ({ hotkeyScope: HotkeyScope; }) => { const { - availableFilters, - selectedFilters, + availableFilterDefinitions, + selectedFilter, setFilterDefinitionUsedInDropdown, setSelectedOperandInDropdown, } = useFilter(); - const availableFilter = availableFilters[0]; + const availableFilter = availableFilterDefinitions[0]; React.useEffect(() => { setFilterDefinitionUsedInDropdown(availableFilter); @@ -48,11 +48,11 @@ export const SingleEntityFilterDropdownButton = ({ dropdownOffset={{ x: 0, y: -28 }} clickableComponent={ - {selectedFilters[0] ? ( + {selectedFilter ? ( { props?.filterScopeId, ); const { - availableFilters, - setAvailableFilters, + availableFilterDefinitions, + setAvailableFilterDefinitions, filterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown, filterDropdownSearchInput, @@ -26,16 +30,28 @@ export const useFilter = (props?: UseFilterProps) => { setIsFilterDropdownOperandSelectUnfolded, isFilterDropdownUnfolded, setIsFilterDropdownUnfolded, - selectedFilters, - setSelectedFilters, + selectedFilter, + setSelectedFilter, selectedOperandInDropdown, setSelectedOperandInDropdown, } = useFilterStates(scopeId); + const { onFilterSelect } = useScopeInternalContextOrThrow( + FilterScopeInternalContext, + ); + + const selectFilter = useCallback( + (filter: Filter) => { + setSelectedFilter(filter); + onFilterSelect?.(filter); + }, + [setSelectedFilter, onFilterSelect], + ); + return { scopeId, - availableFilters, - setAvailableFilters, + availableFilterDefinitions, + setAvailableFilterDefinitions, filterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown, filterDropdownSearchInput, @@ -46,9 +62,10 @@ export const useFilter = (props?: UseFilterProps) => { setIsFilterDropdownOperandSelectUnfolded, isFilterDropdownUnfolded, setIsFilterDropdownUnfolded, - selectedFilters, - setSelectedFilters, + selectedFilter, + setSelectedFilter, selectedOperandInDropdown, setSelectedOperandInDropdown, + selectFilter, }; }; diff --git a/front/src/modules/ui/data/filter/hooks/useFilterCurrentlyEdited.ts b/front/src/modules/ui/data/filter/hooks/useFilterCurrentlyEdited.ts deleted file mode 100644 index e0d4e4e5f..000000000 --- a/front/src/modules/ui/data/filter/hooks/useFilterCurrentlyEdited.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useMemo } from 'react'; - -import { useFilter } from './useFilter'; - -export const useFilterCurrentlyEdited = () => { - const { selectedFilters, filterDefinitionUsedInDropdown } = useFilter(); - - return useMemo(() => { - return selectedFilters?.find( - (filter) => filter.key === filterDefinitionUsedInDropdown?.key, - ); - }, [filterDefinitionUsedInDropdown?.key, selectedFilters]); -}; diff --git a/front/src/modules/ui/data/filter/hooks/useFilterStates.ts b/front/src/modules/ui/data/filter/hooks/useFilterStates.ts index 6dc39a724..3958b20f4 100644 --- a/front/src/modules/ui/data/filter/hooks/useFilterStates.ts +++ b/front/src/modules/ui/data/filter/hooks/useFilterStates.ts @@ -1,19 +1,17 @@ import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2'; -import { availableFiltersScopedState } from '../states/availableFiltersScopedState'; +import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState'; import { filterDefinitionUsedInDropdownScopedState } from '../states/filterDefinitionUsedInDropdownScopedState'; import { filterDropdownSearchInputScopedState } from '../states/filterDropdownSearchInputScopedState'; import { filterDropdownSelectedEntityIdScopedState } from '../states/filterDropdownSelectedEntityIdScopedState'; import { isFilterDropdownOperandSelectUnfoldedScopedState } from '../states/isFilterDropdownOperandSelectUnfoldedScopedState'; import { isFilterDropdownUnfoldedScopedState } from '../states/isFilterDropdownUnfoldedScopedState'; -import { selectedFiltersScopedState } from '../states/selectedFiltersScopedState'; +import { selectedFilterScopedState } from '../states/selectedFilterScopedState'; import { selectedOperandInDropdownScopedState } from '../states/selectedOperandInDropdownScopedState'; export const useFilterStates = (scopeId: string) => { - const [availableFilters, setAvailableFilters] = useRecoilScopedStateV2( - availableFiltersScopedState, - scopeId, - ); + const [availableFilterDefinitions, setAvailableFilterDefinitions] = + useRecoilScopedStateV2(availableFilterDefinitionsScopedState, scopeId); const [filterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown] = useRecoilScopedStateV2(filterDefinitionUsedInDropdownScopedState, scopeId); @@ -35,8 +33,8 @@ export const useFilterStates = (scopeId: string) => { const [isFilterDropdownUnfolded, setIsFilterDropdownUnfolded] = useRecoilScopedStateV2(isFilterDropdownUnfoldedScopedState, scopeId); - const [selectedFilters, setSelectedFilters] = useRecoilScopedStateV2( - selectedFiltersScopedState, + const [selectedFilter, setSelectedFilter] = useRecoilScopedStateV2( + selectedFilterScopedState, scopeId, ); @@ -44,8 +42,8 @@ export const useFilterStates = (scopeId: string) => { useRecoilScopedStateV2(selectedOperandInDropdownScopedState, scopeId); return { - availableFilters, - setAvailableFilters, + availableFilterDefinitions, + setAvailableFilterDefinitions, filterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown, filterDropdownSearchInput, @@ -56,8 +54,8 @@ export const useFilterStates = (scopeId: string) => { setIsFilterDropdownOperandSelectUnfolded, isFilterDropdownUnfolded, setIsFilterDropdownUnfolded, - selectedFilters, - setSelectedFilters, + selectedFilter, + setSelectedFilter, selectedOperandInDropdown, setSelectedOperandInDropdown, }; diff --git a/front/src/modules/ui/data/filter/scopes/FilterScope.tsx b/front/src/modules/ui/data/filter/scopes/FilterScope.tsx index 594554156..9c75f4a43 100644 --- a/front/src/modules/ui/data/filter/scopes/FilterScope.tsx +++ b/front/src/modules/ui/data/filter/scopes/FilterScope.tsx @@ -2,25 +2,31 @@ import { ReactNode } from 'react'; import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition'; +import { Filter } from '../types/Filter'; + import { FilterScopeInitEffect } from './init-effect/FilterScopeInitEffect'; import { FilterScopeInternalContext } from './scope-internal-context/FilterScopeInternalContext'; type FilterScopeProps = { children: ReactNode; filterScopeId: string; - availableFilters?: FilterDefinition[]; + availableFilterDefinitions?: FilterDefinition[]; + onFilterSelect?: (filter: Filter) => void; }; export const FilterScope = ({ children, filterScopeId, - availableFilters, + availableFilterDefinitions, + onFilterSelect, }: FilterScopeProps) => { return ( - + {children} diff --git a/front/src/modules/ui/data/filter/scopes/init-effect/FilterScopeInitEffect.tsx b/front/src/modules/ui/data/filter/scopes/init-effect/FilterScopeInitEffect.tsx index 1068f1797..fdbf81850 100644 --- a/front/src/modules/ui/data/filter/scopes/init-effect/FilterScopeInitEffect.tsx +++ b/front/src/modules/ui/data/filter/scopes/init-effect/FilterScopeInitEffect.tsx @@ -6,20 +6,20 @@ import { useFilterStates } from '../../hooks/useFilterStates'; type FilterScopeInitEffectProps = { filterScopeId: string; - availableFilters?: FilterDefinition[]; + availableFilterDefinitions?: FilterDefinition[]; }; export const FilterScopeInitEffect = ({ filterScopeId, - availableFilters, + availableFilterDefinitions, }: FilterScopeInitEffectProps) => { - const { setAvailableFilters } = useFilterStates(filterScopeId); + const { setAvailableFilterDefinitions } = useFilterStates(filterScopeId); useEffect(() => { - if (availableFilters) { - setAvailableFilters(availableFilters); + if (availableFilterDefinitions) { + setAvailableFilterDefinitions(availableFilterDefinitions); } - }, [availableFilters, setAvailableFilters]); + }, [availableFilterDefinitions, setAvailableFilterDefinitions]); return <>; }; diff --git a/front/src/modules/ui/data/filter/scopes/scope-internal-context/FilterScopeInternalContext.ts b/front/src/modules/ui/data/filter/scopes/scope-internal-context/FilterScopeInternalContext.ts index e45d8d6ae..b5326ad74 100644 --- a/front/src/modules/ui/data/filter/scopes/scope-internal-context/FilterScopeInternalContext.ts +++ b/front/src/modules/ui/data/filter/scopes/scope-internal-context/FilterScopeInternalContext.ts @@ -1,8 +1,10 @@ import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey'; import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; +import { Filter } from '../../types/Filter'; + type FilterScopeInternalContextProps = ScopedStateKey & { - test?: string; + onFilterSelect?: (sort: Filter) => void; }; export const FilterScopeInternalContext = diff --git a/front/src/modules/views/states/availableFiltersScopedState.ts b/front/src/modules/ui/data/filter/states/availableFilterDefinitionsScopedState.ts similarity index 64% rename from front/src/modules/views/states/availableFiltersScopedState.ts rename to front/src/modules/ui/data/filter/states/availableFilterDefinitionsScopedState.ts index 9c38f3952..d1949810c 100644 --- a/front/src/modules/views/states/availableFiltersScopedState.ts +++ b/front/src/modules/ui/data/filter/states/availableFilterDefinitionsScopedState.ts @@ -1,9 +1,9 @@ import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition'; import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState'; -export const availableFiltersScopedState = createScopedState< +export const availableFilterDefinitionsScopedState = createScopedState< FilterDefinition[] >({ - key: 'availableFiltersScopedState', + key: 'availableFilterDefinitionsScopedState', defaultValue: [], }); diff --git a/front/src/modules/ui/data/filter/states/selectedFilterScopedState.ts b/front/src/modules/ui/data/filter/states/selectedFilterScopedState.ts new file mode 100644 index 000000000..a7e81e80d --- /dev/null +++ b/front/src/modules/ui/data/filter/states/selectedFilterScopedState.ts @@ -0,0 +1,8 @@ +import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState'; + +import { Filter } from '../types/Filter'; + +export const selectedFilterScopedState = createScopedState({ + key: 'selectedFilterScopedState', + defaultValue: undefined, +}); diff --git a/front/src/modules/ui/data/filter/states/selectedFiltersScopedState.ts b/front/src/modules/ui/data/filter/states/selectedFiltersScopedState.ts deleted file mode 100644 index 716548451..000000000 --- a/front/src/modules/ui/data/filter/states/selectedFiltersScopedState.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState'; - -import { Filter } from '../types/Filter'; - -export const selectedFiltersScopedState = createScopedState({ - key: 'selectedFiltersScopedState', - defaultValue: [], -}); diff --git a/front/src/modules/ui/data/filter/types/Filter.ts b/front/src/modules/ui/data/filter/types/Filter.ts index 3d415f596..a41fdfdb4 100644 --- a/front/src/modules/ui/data/filter/types/Filter.ts +++ b/front/src/modules/ui/data/filter/types/Filter.ts @@ -1,12 +1,12 @@ import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; -import { FilterType } from './FilterType'; +import { FilterDefinition } from './FilterDefinition'; export type Filter = { - key: string; - type: FilterType; + fieldId: string; value: string; displayValue: string; displayAvatarUrl?: string; operand: ViewFilterOperand; + definition: FilterDefinition; }; diff --git a/front/src/modules/ui/data/filter/types/FilterDefinition.ts b/front/src/modules/ui/data/filter/types/FilterDefinition.ts index 79cc6b74d..dd9bc07f5 100644 --- a/front/src/modules/ui/data/filter/types/FilterDefinition.ts +++ b/front/src/modules/ui/data/filter/types/FilterDefinition.ts @@ -3,7 +3,7 @@ import { IconComponent } from '@/ui/display/icon/types/IconComponent'; import { FilterType } from './FilterType'; export type FilterDefinition = { - key: string; + fieldId: string; label: string; Icon: IconComponent; type: FilterType; diff --git a/front/src/modules/ui/data/filter/types/FilterDefinitionByEntity.ts b/front/src/modules/ui/data/filter/types/FilterDefinitionByEntity.ts index 3449d7254..f351f9e01 100644 --- a/front/src/modules/ui/data/filter/types/FilterDefinitionByEntity.ts +++ b/front/src/modules/ui/data/filter/types/FilterDefinitionByEntity.ts @@ -1,5 +1,5 @@ import { FilterDefinition } from './FilterDefinition'; export type FilterDefinitionByEntity = FilterDefinition & { - key: keyof T; + fieldId: keyof T; }; diff --git a/front/src/modules/ui/data/filter/utils/turnFilterIntoWhereClause.ts b/front/src/modules/ui/data/filter/utils/turnFilterIntoWhereClause.ts index cc749e38c..1776b7f90 100644 --- a/front/src/modules/ui/data/filter/utils/turnFilterIntoWhereClause.ts +++ b/front/src/modules/ui/data/filter/utils/turnFilterIntoWhereClause.ts @@ -3,28 +3,39 @@ import { QueryMode } from '~/generated/graphql'; import { Filter } from '../types/Filter'; -export const turnFilterIntoWhereClause = (filter: Filter) => { +type FilterToTurnIntoWhereClause = Omit & { + definition: { + type: Filter['definition']['type']; + }; +}; + +export const turnFilterIntoWhereClause = ( + filter: FilterToTurnIntoWhereClause | undefined, +) => { + if (!filter) { + return {}; + } switch (filter.operand) { case ViewFilterOperand.IsNotNull: return { - [filter.key]: { + [filter.fieldId]: { not: null, }, }; default: - switch (filter.type) { + switch (filter.definition.type) { case 'text': switch (filter.operand) { case ViewFilterOperand.Contains: return { - [filter.key]: { + [filter.fieldId]: { contains: filter.value, mode: QueryMode.Insensitive, }, }; case ViewFilterOperand.DoesNotContain: return { - [filter.key]: { + [filter.fieldId]: { not: { contains: filter.value, mode: QueryMode.Insensitive, @@ -33,64 +44,64 @@ export const turnFilterIntoWhereClause = (filter: Filter) => { }; default: throw new Error( - `Unknown operand ${filter.operand} for ${filter.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } case 'number': switch (filter.operand) { case ViewFilterOperand.GreaterThan: return { - [filter.key]: { + [filter.fieldId]: { gte: parseFloat(filter.value), }, }; case ViewFilterOperand.LessThan: return { - [filter.key]: { + [filter.fieldId]: { lte: parseFloat(filter.value), }, }; default: throw new Error( - `Unknown operand ${filter.operand} for ${filter.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } case 'date': switch (filter.operand) { case ViewFilterOperand.GreaterThan: return { - [filter.key]: { + [filter.fieldId]: { gte: filter.value, }, }; case ViewFilterOperand.LessThan: return { - [filter.key]: { + [filter.fieldId]: { lte: filter.value, }, }; default: throw new Error( - `Unknown operand ${filter.operand} for ${filter.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } case 'entity': switch (filter.operand) { case ViewFilterOperand.Is: return { - [filter.key]: { + [filter.fieldId]: { equals: filter.value, }, }; case ViewFilterOperand.IsNot: return { - [filter.key]: { + [filter.fieldId]: { not: { equals: filter.value }, }, }; default: throw new Error( - `Unknown operand ${filter.operand} for ${filter.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } default: diff --git a/front/src/modules/ui/data/sort/components/SortDropdownButton.tsx b/front/src/modules/ui/data/sort/components/SortDropdownButton.tsx index 10e59ee64..9cfc6904e 100644 --- a/front/src/modules/ui/data/sort/components/SortDropdownButton.tsx +++ b/front/src/modules/ui/data/sort/components/SortDropdownButton.tsx @@ -35,7 +35,7 @@ export const SortDropdownButton = ({ setSelectedSortDirection('asc'); }, []); - const { availableSorts, onSortAdd, isSortSelected } = useSort(); + const { availableSortDefinitions, onSortSelect, isSortSelected } = useSort(); const { toggleDropdown } = useDropdown({ dropdownScopeId: SortDropdownId, @@ -48,8 +48,8 @@ export const SortDropdownButton = ({ const handleAddSort = (selectedSortDefinition: SortDefinition) => { toggleDropdown(); - onSortAdd?.({ - key: selectedSortDefinition.key, + onSortSelect?.({ + fieldId: selectedSortDefinition.fieldId, direction: selectedSortDirection, definition: selectedSortDefinition, }); @@ -96,7 +96,7 @@ export const SortDropdownButton = ({ - {availableSorts.map((availableSort, index) => ( + {availableSortDefinitions.map((availableSort, index) => ( { props?.sortScopeId, ); const { - availableSorts, - setAvailableSorts, + availableSortDefinitions, + setAvailableSortDefinitions, isSortSelected, setIsSortSelected, } = useSortStates(scopeId); - const { onSortAdd } = useScopeInternalContextOrThrow( + const { onSortSelect } = useScopeInternalContextOrThrow( SortScopeInternalContext, ); return { - onSortAdd, + onSortSelect, scopeId, - availableSorts, + availableSortDefinitions, isSortSelected, setIsSortSelected, - setAvailableSorts, + setAvailableSortDefinitions, }; }; diff --git a/front/src/modules/ui/data/sort/hooks/useSortStates.ts b/front/src/modules/ui/data/sort/hooks/useSortStates.ts index 78d94081e..d4662d661 100644 --- a/front/src/modules/ui/data/sort/hooks/useSortStates.ts +++ b/front/src/modules/ui/data/sort/hooks/useSortStates.ts @@ -1,13 +1,11 @@ import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedStateV2'; -import { availableSortsScopedState } from '@/views/states/availableSortsScopedState'; +import { availableSortDefinitionsScopedState } from '@/views/states/availableSortDefinitionsScopedState'; import { isSortSelectedScopedState } from '../states/isSortSelectedScopedState'; export const useSortStates = (scopeId: string) => { - const [availableSorts, setAvailableSorts] = useRecoilScopedStateV2( - availableSortsScopedState, - scopeId, - ); + const [availableSortDefinitions, setAvailableSortDefinitions] = + useRecoilScopedStateV2(availableSortDefinitionsScopedState, scopeId); const [isSortSelected, setIsSortSelected] = useRecoilScopedStateV2( isSortSelectedScopedState, @@ -15,8 +13,8 @@ export const useSortStates = (scopeId: string) => { ); return { - availableSorts, - setAvailableSorts, + availableSortDefinitions, + setAvailableSortDefinitions, isSortSelected, setIsSortSelected, }; diff --git a/front/src/modules/ui/data/sort/scopes/SortScope.tsx b/front/src/modules/ui/data/sort/scopes/SortScope.tsx index 0b511fd1b..9b5a3509c 100644 --- a/front/src/modules/ui/data/sort/scopes/SortScope.tsx +++ b/front/src/modules/ui/data/sort/scopes/SortScope.tsx @@ -9,23 +9,23 @@ import { SortScopeInternalContext } from './scope-internal-context/SortScopeInte type SortScopeProps = { children: ReactNode; sortScopeId: string; - availableSorts?: SortDefinition[]; - onSortAdd?: (sort: Sort) => void | Promise; + availableSortDefinitions?: SortDefinition[]; + onSortSelect?: (sort: Sort) => void | Promise; }; export const SortScope = ({ children, sortScopeId, - availableSorts, - onSortAdd, + availableSortDefinitions, + onSortSelect, }: SortScopeProps) => { return ( {children} diff --git a/front/src/modules/ui/data/sort/scopes/init-effect/SortScopeInitEffect.tsx b/front/src/modules/ui/data/sort/scopes/init-effect/SortScopeInitEffect.tsx index a4a41a390..7a4e8e052 100644 --- a/front/src/modules/ui/data/sort/scopes/init-effect/SortScopeInitEffect.tsx +++ b/front/src/modules/ui/data/sort/scopes/init-effect/SortScopeInitEffect.tsx @@ -6,20 +6,20 @@ import { useSortStates } from '../../hooks/useSortStates'; type SortScopeInitEffectProps = { sortScopeId: string; - availableSorts?: SortDefinition[]; + availableSortDefinitions?: SortDefinition[]; }; export const SortScopeInitEffect = ({ sortScopeId, - availableSorts, + availableSortDefinitions, }: SortScopeInitEffectProps) => { - const { setAvailableSorts } = useSortStates(sortScopeId); + const { setAvailableSortDefinitions } = useSortStates(sortScopeId); useEffect(() => { - if (availableSorts) { - setAvailableSorts(availableSorts); + if (availableSortDefinitions) { + setAvailableSortDefinitions(availableSortDefinitions); } - }, [availableSorts, setAvailableSorts]); + }, [availableSortDefinitions, setAvailableSortDefinitions]); return <>; }; diff --git a/front/src/modules/ui/data/sort/scopes/scope-internal-context/SortScopeInternalContext.ts b/front/src/modules/ui/data/sort/scopes/scope-internal-context/SortScopeInternalContext.ts index d8022b55b..01fead48c 100644 --- a/front/src/modules/ui/data/sort/scopes/scope-internal-context/SortScopeInternalContext.ts +++ b/front/src/modules/ui/data/sort/scopes/scope-internal-context/SortScopeInternalContext.ts @@ -2,11 +2,9 @@ import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/type import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; import { Sort } from '../../types/Sort'; -import { SortDefinition } from '../../types/SortDefinition'; type SortScopeInternalContextProps = ScopedStateKey & { - onSortAdd?: (sort: Sort) => void; - availableSorts?: SortDefinition[]; + onSortSelect?: (sort: Sort) => void; }; export const SortScopeInternalContext = diff --git a/front/src/modules/ui/data/sort/types/Sort.ts b/front/src/modules/ui/data/sort/types/Sort.ts index 41a28e900..2f39094b6 100644 --- a/front/src/modules/ui/data/sort/types/Sort.ts +++ b/front/src/modules/ui/data/sort/types/Sort.ts @@ -2,7 +2,7 @@ import { SortDefinition } from './SortDefinition'; import { SortDirection } from './SortDirection'; export type Sort = { - key: string; + fieldId: string; direction: SortDirection; definition: SortDefinition; }; diff --git a/front/src/modules/ui/data/sort/types/SortDefinition.ts b/front/src/modules/ui/data/sort/types/SortDefinition.ts index 5405e3bd6..83a1134e8 100644 --- a/front/src/modules/ui/data/sort/types/SortDefinition.ts +++ b/front/src/modules/ui/data/sort/types/SortDefinition.ts @@ -3,7 +3,7 @@ import { IconComponent } from '@/ui/display/icon/types/IconComponent'; import { SortDirection } from './SortDirection'; export type SortDefinition = { - key: string; + fieldId: string; label: string; Icon?: IconComponent; getOrderByTemplate?: (direction: SortDirection) => any[]; diff --git a/front/src/modules/ui/data/sort/utils/helpers.ts b/front/src/modules/ui/data/sort/utils/helpers.ts index cf17641db..028013941 100644 --- a/front/src/modules/ui/data/sort/utils/helpers.ts +++ b/front/src/modules/ui/data/sort/utils/helpers.ts @@ -10,7 +10,7 @@ export const reduceSortsToOrderBy = (sorts: Sort[]): any[] => if (sort.definition.getOrderByTemplate) { return sort.definition.getOrderByTemplate(direction); } else { - return [{ [sort.definition.key]: direction }]; + return [{ [sort.definition.fieldId]: direction }]; } }) .flat(); diff --git a/front/src/modules/ui/layout/board/components/BoardOptionsDropdown.tsx b/front/src/modules/ui/layout/board/components/BoardOptionsDropdown.tsx index 3054d96d4..c2f11e159 100644 --- a/front/src/modules/ui/layout/board/components/BoardOptionsDropdown.tsx +++ b/front/src/modules/ui/layout/board/components/BoardOptionsDropdown.tsx @@ -2,8 +2,9 @@ import { useView } from '@/views/hooks/useView'; import { Dropdown } from '../../dropdown/components/Dropdown'; import { DropdownScope } from '../../dropdown/scopes/DropdownScope'; -import { BoardScopeIds } from '../types/enums/BoardScopeIds'; +import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope'; +import { BoardOptionsDropdownId } from './constants/BoardOptionsDropdownId'; import { BoardOptionsDropdownButton } from './BoardOptionsDropdownButton'; import { BoardOptionsDropdownContent, @@ -12,26 +13,22 @@ import { type BoardOptionsDropdownProps = Pick< BoardOptionsDropdownContentProps, - 'customHotkeyScope' | 'onStageAdd' + 'onStageAdd' >; export const BoardOptionsDropdown = ({ - customHotkeyScope, onStageAdd, }: BoardOptionsDropdownProps) => { const { setViewEditMode } = useView(); return ( - + } dropdownComponents={ - + } - dropdownHotkeyScope={customHotkeyScope} + dropdownHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }} onClickOutside={() => setViewEditMode('none')} dropdownMenuWidth={170} /> diff --git a/front/src/modules/ui/layout/board/components/BoardOptionsDropdownButton.tsx b/front/src/modules/ui/layout/board/components/BoardOptionsDropdownButton.tsx index eca0811ef..d578ec5da 100644 --- a/front/src/modules/ui/layout/board/components/BoardOptionsDropdownButton.tsx +++ b/front/src/modules/ui/layout/board/components/BoardOptionsDropdownButton.tsx @@ -1,12 +1,8 @@ import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { BoardScopeIds } from '../types/enums/BoardScopeIds'; - export const BoardOptionsDropdownButton = () => { - const { isDropdownOpen, toggleDropdown } = useDropdown({ - dropdownScopeId: BoardScopeIds.OptionsDropdown, - }); + const { isDropdownOpen, toggleDropdown } = useDropdown(); const handleClick = () => { toggleDropdown(); diff --git a/front/src/modules/ui/layout/board/components/BoardOptionsDropdownContent.tsx b/front/src/modules/ui/layout/board/components/BoardOptionsDropdownContent.tsx index 498875b77..889cad684 100644 --- a/front/src/modules/ui/layout/board/components/BoardOptionsDropdownContent.tsx +++ b/front/src/modules/ui/layout/board/components/BoardOptionsDropdownContent.tsx @@ -1,5 +1,5 @@ import { useContext, useRef, useState } from 'react'; -import { useRecoilCallback, useRecoilState } from 'recoil'; +import { useRecoilState } from 'recoil'; import { Key } from 'ts-key-enum'; import { v4 } from 'uuid'; @@ -22,25 +22,20 @@ import { MenuItemNavigate } from '@/ui/navigation/menu-item/components/MenuItemN import { MenuItemToggle } from '@/ui/navigation/menu-item/components/MenuItemToggle'; import { ThemeColor } from '@/ui/theme/constants/colors'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; -import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue'; -import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopeId'; import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection'; import { useView } from '@/views/hooks/useView'; -import { useViewInternalStates } from '@/views/hooks/useViewInternalStates'; -import { viewEditModeScopedState } from '@/views/states/viewEditModeScopedState'; +import { useViewGetStates } from '@/views/hooks/useViewGetStates'; import { useBoardCardFields } from '../hooks/useBoardCardFields'; -import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState'; import { boardColumnsState } from '../states/boardColumnsState'; import { isCompactViewEnabledState } from '../states/isCompactViewEnabledState'; -import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsFamilyState'; import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector'; import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector'; import { BoardColumnDefinition } from '../types/BoardColumnDefinition'; +import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope'; export type BoardOptionsDropdownContentProps = { - customHotkeyScope: HotkeyScope; onStageAdd?: (boardColumn: BoardColumnDefinition) => void; }; @@ -54,15 +49,12 @@ type ColumnForCreate = { }; export const BoardOptionsDropdownContent = ({ - customHotkeyScope, onStageAdd, }: BoardOptionsDropdownContentProps) => { - const { setViewEditMode, createView, currentViewId } = useView(); - const { viewEditMode, currentView } = useViewInternalStates(); + const { setViewEditMode, handleViewNameSubmit } = useView(); + const { viewEditMode, currentView } = useViewGetStates(); const { BoardRecoilScopeContext } = useContext(BoardContext); - const boardRecoilScopeId = useRecoilScopeId(BoardRecoilScopeContext); - const stageInputRef = useRef(null); const viewEditInputRef = useRef(null); @@ -104,31 +96,6 @@ export const BoardOptionsDropdownContent = ({ onStageAdd?.(columnToCreate); }; - const handleViewNameSubmit = useRecoilCallback( - ({ set, snapshot }) => - async () => { - const viewEditMode = snapshot - .getLoadable(viewEditModeScopedState({ scopeId: boardRecoilScopeId })) - .getValue(); - - if (!viewEditMode) { - return; - } - - const boardCardFields = await snapshot.getPromise( - boardCardFieldsScopedState(boardRecoilScopeId), - ); - const isCreateMode = viewEditMode === 'create'; - const name = viewEditInputRef.current?.value; - - if (isCreateMode && name) { - await createView(name); - set(savedBoardCardFieldsFamilyState(currentViewId), boardCardFields); - } - }, - [boardRecoilScopeId, createView, currentViewId], - ); - const resetMenu = () => setCurrentMenu(undefined); const handleMenuNavigate = (menu: BoardOptionsMenu) => { @@ -146,43 +113,39 @@ export const BoardOptionsDropdownContent = ({ setViewEditMode('none'); closeDropdown(); }, - customHotkeyScope.scope, + BoardOptionsHotkeyScope.Dropdown, ); useScopedHotkeys( Key.Enter, () => { + const name = viewEditInputRef.current?.value; + resetMenu(); + setViewEditMode('none'); + closeDropdown(); handleStageSubmit(); - handleViewNameSubmit(); + handleViewNameSubmit(name); closeDropdown(); }, - customHotkeyScope.scope, + BoardOptionsHotkeyScope.Dropdown, ); return ( <> {!currentMenu && ( <> - {viewEditMode && ( - - )} + []; - sorts: SortDefinition[]; + filterDefinitions: FilterDefinitionByEntity[]; + sortDefinitions: SortDefinition[]; }; diff --git a/front/src/modules/ui/layout/board/types/enums/BoardScopeIds.ts b/front/src/modules/ui/layout/board/types/enums/BoardScopeIds.ts deleted file mode 100644 index be9c072fa..000000000 --- a/front/src/modules/ui/layout/board/types/enums/BoardScopeIds.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum BoardScopeIds { - OptionsDropdown = 'board-options', -} diff --git a/front/src/modules/views/components/UpdateViewButtonGroup.tsx b/front/src/modules/views/components/UpdateViewButtonGroup.tsx index f23f0ca3e..d94ee56e6 100644 --- a/front/src/modules/views/components/UpdateViewButtonGroup.tsx +++ b/front/src/modules/views/components/UpdateViewButtonGroup.tsx @@ -10,7 +10,7 @@ import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useView } from '@/views/hooks/useView'; -import { useViewInternalStates } from '../hooks/useViewInternalStates'; +import { useViewGetStates } from '../hooks/useViewGetStates'; const StyledContainer = styled.div` display: inline-flex; @@ -28,7 +28,7 @@ export const UpdateViewButtonGroup = ({ }: UpdateViewButtonGroupProps) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const { updateCurrentView, setViewEditMode } = useView(); - const { canPersistFilters, canPersistSorts } = useViewInternalStates(); + const { canPersistFilters, canPersistSorts } = useViewGetStates(); const canPersistView = canPersistFilters || canPersistSorts; diff --git a/front/src/modules/views/components/ViewBar.tsx b/front/src/modules/views/components/ViewBar.tsx index c4b3af26d..de040cd2f 100644 --- a/front/src/modules/views/components/ViewBar.tsx +++ b/front/src/modules/views/components/ViewBar.tsx @@ -9,7 +9,7 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { TopBar } from '@/ui/layout/top-bar/TopBar'; import { useView } from '../hooks/useView'; -import { useViewInternalStates } from '../hooks/useViewInternalStates'; +import { useViewGetStates } from '../hooks/useViewGetStates'; import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope'; import { UpdateViewButtonGroup } from './UpdateViewButtonGroup'; @@ -31,18 +31,20 @@ export const ViewBar = ({ const { openDropdown: openOptionsDropdownButton } = useDropdown({ dropdownScopeId: optionsDropdownScopeId, }); - const { upsertViewSort } = useView(); - const { availableFilters, availableSorts } = useViewInternalStates(); + const { upsertViewSort, upsertViewFilter } = useView(); + const { availableFilterDefinitions, availableSortDefinitions } = + useViewGetStates(); return ( } displayBottomBorder={false} diff --git a/front/src/modules/views/components/ViewBarDetails.tsx b/front/src/modules/views/components/ViewBarDetails.tsx index 6a681d95e..c8ce4c4db 100644 --- a/front/src/modules/views/components/ViewBarDetails.tsx +++ b/front/src/modules/views/components/ViewBarDetails.tsx @@ -5,9 +5,8 @@ import { AddFilterFromDropdownButton } from '@/ui/data/filter/components/AddFilt import { getOperandLabelShort } from '@/ui/data/filter/utils/getOperandLabel'; import { IconArrowDown, IconArrowUp } from '@/ui/display/icon/index'; -import { useRemoveFilter } from '../hooks/useRemoveFilter'; import { useView } from '../hooks/useView'; -import { useViewInternalStates } from '../hooks/useViewInternalStates'; +import { useViewGetStates } from '../hooks/useViewGetStates'; import SortOrFilterChip from './SortOrFilterChip'; @@ -90,43 +89,23 @@ export const ViewBarDetails = ({ }: ViewBarDetailsProps) => { const { currentViewSorts, - setCurrentViewSorts, - availableFilters, currentViewFilters, canPersistFilters, canPersistSorts, isViewBarExpanded, - } = useViewInternalStates(); + } = useViewGetStates(); - const { resetViewBar } = useView(); + const { resetViewBar, removeViewSort, removeViewFilter } = useView(); const canPersistView = canPersistFilters || canPersistSorts; - const filtersWithDefinition = currentViewFilters?.map((filter) => { - const filterDefinition = availableFilters.find((availableFilter) => { - return availableFilter.key === filter.key; - }); - - return { - ...filter, - ...filterDefinition, - }; - }); - - const removeFilter = useRemoveFilter(); - const handleCancelClick = () => { resetViewBar(); }; - const handleSortRemove = (sortKey: string) => - setCurrentViewSorts?.((previousSorts) => - previousSorts.filter((sort) => sort.key !== sortKey), - ); - const shouldExpandViewBar = canPersistView || - ((filtersWithDefinition?.length || currentViewSorts?.length) && + ((currentViewSorts?.length || currentViewFilters?.length) && isViewBarExpanded); if (!shouldExpandViewBar) { @@ -140,32 +119,32 @@ export const ViewBarDetails = ({ {currentViewSorts?.map((sort) => { return ( handleSortRemove(sort.key)} + onRemove={() => removeViewSort(sort.fieldId)} /> ); })} - {!!currentViewSorts?.length && !!filtersWithDefinition?.length && ( + {!!currentViewSorts?.length && !!currentViewFilters?.length && ( )} - {filtersWithDefinition?.map((filter) => { + {currentViewFilters?.map((filter) => { return ( { - removeFilter(filter.key); + removeViewFilter(filter.fieldId); }} /> ); diff --git a/front/src/modules/views/components/ViewBarEffect.tsx b/front/src/modules/views/components/ViewBarEffect.tsx index 8db0f1246..51a63a116 100644 --- a/front/src/modules/views/components/ViewBarEffect.tsx +++ b/front/src/modules/views/components/ViewBarEffect.tsx @@ -8,28 +8,65 @@ import { assertNotNull } from '~/utils/assert'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { useView } from '../hooks/useView'; -import { useViewInternalStates } from '../hooks/useViewInternalStates'; -import { availableFieldsScopedState } from '../states/availableFieldsScopedState'; +import { useViewGetStates } from '../hooks/useViewGetStates'; +import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState'; +import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState'; +import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState'; import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState'; +import { onViewFiltersChangeScopedState } from '../states/onViewFiltersChangeScopedState'; +import { onViewSortsChangeScopedState } from '../states/onViewSortsChangeScopedState'; import { savedViewFieldsScopedFamilyState } from '../states/savedViewFieldsScopedFamilyState'; +import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState'; +import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState'; import { viewsScopedState } from '../states/viewsScopedState'; import { View } from '../types/View'; import { ViewField } from '../types/ViewField'; +import { ViewFilter } from '../types/ViewFilter'; +import { ViewSort } from '../types/ViewSort'; export const ViewBarEffect = () => { const { scopeId: viewScopeId, setCurrentViewFields, setSavedViewFields, + setCurrentViewFilters, + setSavedViewFilters, + setCurrentViewSorts, + setSavedViewSorts, currentViewId, setViews, - changeView, + loadView, + changeViewInUrl, setCurrentViewId, } = useView(); const [searchParams] = useSearchParams(); - const { viewType, viewObjectId } = useViewInternalStates(viewScopeId); + const { viewType, viewObjectId } = useViewGetStates(viewScopeId); + + useFindManyObjects({ + objectNamePlural: 'viewsV2', + filter: { type: { eq: viewType }, objectId: { eq: viewObjectId } }, + onCompleted: useRecoilCallback( + ({ snapshot }) => + async (data: PaginatedObjectTypeResults) => { + const nextViews = data.edges.map((view) => ({ + id: view.node.id, + name: view.node.name, + objectId: view.node.objectId, + })); + const views = snapshot + .getLoadable(viewsScopedState({ scopeId: viewScopeId })) + .getValue(); + + if (!isDeeplyEqual(views, nextViews)) setViews(nextViews); + + if (!nextViews.length) return; + + if (!currentViewId) return changeViewInUrl(nextViews[0].id); + }, + ), + }); useFindManyObjects({ objectNamePlural: 'viewFieldsV2', @@ -38,7 +75,9 @@ export const ViewBarEffect = () => { ({ snapshot }) => async (data: PaginatedObjectTypeResults) => { const availableFields = snapshot - .getLoadable(availableFieldsScopedState({ scopeId: viewScopeId })) + .getLoadable( + availableFieldDefinitionsScopedState({ scopeId: viewScopeId }), + ) .getValue(); const onViewFieldsChange = snapshot @@ -74,133 +113,122 @@ export const ViewBarEffect = () => { }); useFindManyObjects({ - objectNamePlural: 'viewsV2', - filter: { type: { eq: viewType }, objectId: { eq: viewObjectId } }, + objectNamePlural: 'viewFiltersV2', + filter: { viewId: { eq: currentViewId } }, onCompleted: useRecoilCallback( ({ snapshot }) => - async (data: PaginatedObjectTypeResults) => { - const nextViews = data.edges.map((view) => ({ - id: view.node.id, - name: view.node.name, - objectId: view.node.objectId, - })); - const views = snapshot - .getLoadable(viewsScopedState({ scopeId: viewScopeId })) + async (data: PaginatedObjectTypeResults>) => { + const availableFilterDefinitions = snapshot + .getLoadable( + availableFilterDefinitionsScopedState({ scopeId: viewScopeId }), + ) .getValue(); - if (!isDeeplyEqual(views, nextViews)) setViews(nextViews); + if (!availableFilterDefinitions || !currentViewId) { + return; + } - if (!nextViews.length) return; + const savedViewFilters = snapshot + .getLoadable( + savedViewFiltersScopedFamilyState({ + scopeId: viewScopeId, + familyKey: currentViewId, + }), + ) + .getValue(); - if (!currentViewId) return changeView(nextViews[0].id); + const onViewFiltersChange = snapshot + .getLoadable( + onViewFiltersChangeScopedState({ scopeId: viewScopeId }), + ) + .getValue(); + + const queriedViewFilters = data.edges + .map(({ node }) => { + const availableFilterDefinition = availableFilterDefinitions.find( + (filterDefinition) => filterDefinition.fieldId === node.fieldId, + ); + + if (!availableFilterDefinition) return null; + + return { + ...node, + displayValue: node.displayValue ?? node.value, + definition: availableFilterDefinition, + }; + }) + .filter(assertNotNull); + + if (!isDeeplyEqual(savedViewFilters, queriedViewFilters)) { + setSavedViewFilters?.(queriedViewFilters); + setCurrentViewFilters?.(queriedViewFilters); + onViewFiltersChange?.(queriedViewFilters); + } }, ), }); - // useGetViewSortsQuery({ - // skip: !currentViewId, - // variables: { - // where: { - // viewId: { equals: currentViewId }, - // }, - // }, - // onCompleted: useRecoilCallback(({ snapshot }) => async (data) => { - // const availableSorts = snapshot - // .getLoadable(availableSortsScopedState({ scopeId: viewScopeId })) - // .getValue(); + useFindManyObjects({ + objectNamePlural: 'viewSortsV2', + filter: { viewId: { eq: currentViewId } }, + onCompleted: useRecoilCallback( + ({ snapshot }) => + async (data: PaginatedObjectTypeResults>) => { + const availableSortDefinitions = snapshot + .getLoadable( + availableSortDefinitionsScopedState({ scopeId: viewScopeId }), + ) + .getValue(); - // if (!availableSorts || !currentViewId) { - // return; - // } + if (!availableSortDefinitions || !currentViewId) { + return; + } - // const savedViewSorts = snapshot - // .getLoadable( - // savedViewSortsScopedFamilyState({ - // scopeId: viewScopeId, - // familyKey: currentViewId, - // }), - // ) - // .getValue(); + const savedViewSorts = snapshot + .getLoadable( + savedViewSortsScopedFamilyState({ + scopeId: viewScopeId, + familyKey: currentViewId, + }), + ) + .getValue(); - // const queriedViewSorts = data.viewSorts - // .map((viewSort) => { - // const foundCorrespondingSortDefinition = availableSorts.find( - // (sort) => sort.key === viewSort.key, - // ); + const onViewSortsChange = snapshot + .getLoadable(onViewSortsChangeScopedState({ scopeId: viewScopeId })) + .getValue(); - // if (foundCorrespondingSortDefinition) { - // return { - // key: viewSort.key, - // definition: foundCorrespondingSortDefinition, - // direction: viewSort.direction.toLowerCase(), - // } as Sort; - // } else { - // return undefined; - // } - // }) - // .filter((sort): sort is Sort => !!sort); + const queriedViewSorts = data.edges + .map(({ node }) => { + const availableSortDefinition = availableSortDefinitions.find( + (sort) => sort.fieldId === node.fieldId, + ); - // if (!isDeeplyEqual(savedViewSorts, queriedViewSorts)) { - // setSavedViewSorts?.(queriedViewSorts); - // setCurrentViewSorts?.(queriedViewSorts); - // } - // }), - // }); + if (!availableSortDefinition) return null; - // useGetViewFiltersQuery({ - // skip: !currentViewId, - // variables: { - // where: { - // viewId: { equals: currentViewId }, - // }, - // }, - // onCompleted: useRecoilCallback(({ snapshot }) => (data) => { - // const availableFilters = snapshot - // .getLoadable(availableFiltersScopedState({ scopeId: viewScopeId })) - // .getValue(); + return { + id: node.id, + fieldId: node.fieldId, + direction: node.direction, + definition: availableSortDefinition, + }; + }) + .filter(assertNotNull); - // if (!availableFilters || !currentViewId) { - // return; - // } - - // const savedViewFilters = snapshot - // .getLoadable( - // savedViewFiltersScopedFamilyState({ - // scopeId: viewScopeId, - // familyKey: currentViewId, - // }), - // ) - // .getValue(); - - // const queriedViewFilters = data.viewFilters - // .map(({ __typename, name: _name, ...viewFilter }) => { - // const availableFilter = availableFilters.find( - // (filter) => filter.key === viewFilter.key, - // ); - - // return availableFilter - // ? { - // ...viewFilter, - // displayValue: viewFilter.displayValue ?? viewFilter.value, - // type: availableFilter.type, - // } - // : undefined; - // }) - // .filter((filter): filter is Filter => !!filter); - - // if (!isDeeplyEqual(savedViewFilters, queriedViewFilters)) { - // setSavedViewFilters?.(queriedViewFilters); - // setCurrentViewFilters?.(queriedViewFilters); - // } - // }), - // }); + if (!isDeeplyEqual(savedViewSorts, queriedViewSorts)) { + setSavedViewSorts?.(queriedViewSorts); + setCurrentViewSorts?.(queriedViewSorts); + onViewSortsChange?.(queriedViewSorts); + } + }, + ), + }); const currentViewIdFromUrl = searchParams.get('view'); useEffect(() => { if (!currentViewIdFromUrl) return; - setCurrentViewId(currentViewIdFromUrl); - }, [currentViewIdFromUrl, setCurrentViewId]); + loadView(currentViewIdFromUrl); + }, [currentViewIdFromUrl, loadView, setCurrentViewId]); return <>; }; diff --git a/front/src/modules/views/components/ViewsDropdownButton.tsx b/front/src/modules/views/components/ViewsDropdownButton.tsx index f7a2e8ef9..86a5007dc 100644 --- a/front/src/modules/views/components/ViewsDropdownButton.tsx +++ b/front/src/modules/views/components/ViewsDropdownButton.tsx @@ -3,7 +3,6 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useRecoilCallback } from 'recoil'; -import { TableOptionsDropdownId } from '@/ui/data/data-table/constants/TableOptionsDropdownId'; import { IconChevronDown, IconList, @@ -24,7 +23,7 @@ import { assertNotNull } from '~/utils/assert'; import { ViewsDropdownId } from '../constants/ViewsDropdownId'; import { useView } from '../hooks/useView'; -import { useViewInternalStates } from '../hooks/useViewInternalStates'; +import { useViewGetStates } from '../hooks/useViewGetStates'; const StyledBoldDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)` font-weight: ${({ theme }) => theme.font.weight.regular}; @@ -60,17 +59,22 @@ const StyledViewName = styled.span` export type ViewsDropdownButtonProps = { hotkeyScope: HotkeyScope; onViewEditModeChange?: () => void; + optionsDropdownScopeId: string; }; export const ViewsDropdownButton = ({ hotkeyScope, onViewEditModeChange, + optionsDropdownScopeId, }: ViewsDropdownButtonProps) => { const theme = useTheme(); - const { scopeId, removeView, currentViewId, changeView } = useView(); + const { scopeId, removeView, currentViewId, changeViewInUrl } = useView(); - const { views, currentView, setViewEditMode, entityCountInCurrentView } = - useViewInternalStates(scopeId, currentViewId); + const { views, currentView, entityCountInCurrentView } = useViewGetStates( + scopeId, + currentViewId, + ); + const { setViewEditMode } = useView(); const { isDropdownOpen: isViewsDropdownOpen, @@ -80,16 +84,16 @@ export const ViewsDropdownButton = ({ }); const { openDropdown: openOptionsDropdown } = useDropdown({ - dropdownScopeId: TableOptionsDropdownId, + dropdownScopeId: optionsDropdownScopeId, }); const handleViewSelect = useRecoilCallback( () => async (viewId: string) => { - changeView(viewId); + changeViewInUrl(viewId); closeViewsDropdown(); }, - [changeView, closeViewsDropdown], + [changeViewInUrl, closeViewsDropdown], ); const handleAddViewButtonClick = () => { @@ -104,7 +108,7 @@ export const ViewsDropdownButton = ({ viewId: string, ) => { event.stopPropagation(); - changeView(viewId); + changeViewInUrl(viewId); setViewEditMode('edit'); onViewEditModeChange?.(); closeViewsDropdown(); diff --git a/front/src/modules/views/hooks/internal/useViewFields.ts b/front/src/modules/views/hooks/internal/useViewFields.ts index fa6100a1b..8282ceb77 100644 --- a/front/src/modules/views/hooks/internal/useViewFields.ts +++ b/front/src/modules/views/hooks/internal/useViewFields.ts @@ -8,9 +8,10 @@ import { viewObjectIdScopeState } from '@/views/states/viewObjectIdScopeState'; import { ViewField } from '@/views/types/ViewField'; export const useViewFields = (viewScopeId: string) => { - const { updateOneMutation, createOneMutation } = useFindOneMetadataObject({ - objectNameSingular: 'viewFieldV2', - }); + const { updateOneMutation, createOneMutation, findManyQuery } = + useFindOneMetadataObject({ + objectNameSingular: 'viewFieldV2', + }); const apolloClient = useApolloClient(); const persistViewFields = useRecoilCallback( @@ -49,12 +50,13 @@ export const useViewFields = (viewScopeId: string) => { variables: { input: { fieldId: viewField.fieldId, - viewId: currentViewId, + viewId: viewId, isVisible: viewField.isVisible, size: viewField.size, position: viewField.position, }, }, + refetchQueries: [findManyQuery], }), ), ); diff --git a/front/src/modules/views/hooks/internal/useViewFilters.ts b/front/src/modules/views/hooks/internal/useViewFilters.ts index 7ccc3717f..b68f42965 100644 --- a/front/src/modules/views/hooks/internal/useViewFilters.ts +++ b/front/src/modules/views/hooks/internal/useViewFilters.ts @@ -1,14 +1,26 @@ +import { useApolloClient } from '@apollo/client'; +import { produce } from 'immer'; import { useRecoilCallback } from 'recoil'; +import { useFindOneMetadataObject } from '@/metadata/hooks/useFindOneMetadataObject'; import { Filter } from '@/ui/data/filter/types/Filter'; -import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition'; -import { availableFiltersScopedState } from '@/views/states/availableFiltersScopedState'; import { currentViewFiltersScopedFamilyState } from '@/views/states/currentViewFiltersScopedFamilyState'; import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState'; +import { onViewFiltersChangeScopedState } from '@/views/states/onViewFiltersChangeScopedState'; import { savedViewFiltersScopedFamilyState } from '@/views/states/savedViewFiltersScopedFamilyState'; import { savedViewFiltersByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewFiltersByKeyScopedFamilySelector'; +import { ViewFilter } from '@/views/types/ViewFilter'; + +import { useViewSetStates } from '../useViewSetStates'; export const useViewFilters = (viewScopeId: string) => { + const { updateOneMutation, createOneMutation, findManyQuery } = + useFindOneMetadataObject({ + objectNameSingular: 'viewFilterV2', + }); + const apolloClient = useApolloClient(); + const { setCurrentViewFilters } = useViewSetStates(viewScopeId); + const persistViewFilters = useRecoilCallback( ({ snapshot, set }) => async (viewId?: string) => { @@ -19,69 +31,52 @@ export const useViewFilters = (viewScopeId: string) => { return; } - const _createViewFilters = ( - filters: Filter[], - availableFilters: FilterDefinition[] = [], - ) => { - if (!currentViewId || !filters.length) { - return; - } + const createViewFilters = (viewFiltersToCreate: ViewFilter[]) => { + if (!viewFiltersToCreate.length) return; - if (!availableFilters) { - return; - } - - // return createViewFiltersMutation({ - // variables: { - // data: filters.map((filter) => ({ - // displayValue: filter.displayValue ?? filter.value, - // key: filter.key, - // name: - // availableFilters.find(({ key }) => key === filter.key) - // ?.label ?? '', - // operand: filter.operand, - // value: filter.value, - // viewId: viewId ?? currentViewId, - // })), - // }, - // }); + return Promise.all( + viewFiltersToCreate.map((viewFilter) => + apolloClient.mutate({ + mutation: createOneMutation, + variables: { + input: { + fieldId: viewFilter.fieldId, + viewId: viewId ?? currentViewId, + value: viewFilter.value, + displayValue: viewFilter.displayValue, + operand: viewFilter.operand, + }, + }, + refetchQueries: [findManyQuery], + }), + ), + ); }; - const _updateViewFilters = (filters: Filter[]) => { - if (!currentViewId || !filters.length) return; + const updateViewFilters = (viewFiltersToUpdate: ViewFilter[]) => { + if (!viewFiltersToUpdate.length) return; - // return Promise.all( - // filters.map((filter) => - // updateViewFilterMutation({ - // variables: { - // data: { - // displayValue: filter.displayValue ?? filter.value, - // operand: filter.operand, - // value: filter.value, - // }, - // where: { - // viewId_key: { - // key: filter.key, - // viewId: viewId ?? currentViewId, - // }, - // }, - // }, - // }), - // ), - // ); + return Promise.all( + viewFiltersToUpdate.map((viewFilter) => + apolloClient.mutate({ + mutation: updateOneMutation, + variables: { + idToUpdate: viewFilter.id, + input: { + value: viewFilter.value, + displayValue: viewFilter.displayValue, + operand: viewFilter.operand, + }, + }, + }), + ), + ); }; - const _deleteViewFilters = (filterKeys: string[]) => { - if (!currentViewId || !filterKeys.length) return; + const deleteViewFilters = (viewFilterIdsToDelete: string[]) => { + if (!viewFilterIdsToDelete.length) return; - // return deleteViewFiltersMutation({ - // variables: { - // where: { - // key: { in: filterKeys }, - // viewId: { equals: viewId ?? currentViewId }, - // }, - // }, - // }); + // Todo }; const currentViewFilters = snapshot @@ -97,7 +92,7 @@ export const useViewFilters = (viewScopeId: string) => { .getLoadable( savedViewFiltersByKeyScopedFamilySelector({ scopeId: viewScopeId, - viewId: currentViewId, + viewId: viewId ?? currentViewId, }), ) .getValue(); @@ -109,32 +104,24 @@ export const useViewFilters = (viewScopeId: string) => { return; } - const availableFilters = snapshot - .getLoadable( - availableFiltersScopedState({ - scopeId: viewScopeId, - }), - ) - .getValue(); - const filtersToCreate = currentViewFilters.filter( - (filter) => !savedViewFiltersByKey[filter.key], + (filter) => !savedViewFiltersByKey[filter.fieldId], ); - await _createViewFilters(filtersToCreate, availableFilters); + await createViewFilters(filtersToCreate); const filtersToUpdate = currentViewFilters.filter( (filter) => - savedViewFiltersByKey[filter.key] && - (savedViewFiltersByKey[filter.key].operand !== filter.operand || - savedViewFiltersByKey[filter.key].value !== filter.value), + savedViewFiltersByKey[filter.fieldId] && + (savedViewFiltersByKey[filter.fieldId].operand !== filter.operand || + savedViewFiltersByKey[filter.fieldId].value !== filter.value), ); - await _updateViewFilters(filtersToUpdate); + await updateViewFilters(filtersToUpdate); - const filterKeys = currentViewFilters.map((filter) => filter.key); + const filterKeys = currentViewFilters.map((filter) => filter.fieldId); const filterKeysToDelete = Object.keys(savedViewFiltersByKey).filter( (previousFilterKey) => !filterKeys.includes(previousFilterKey), ); - await _deleteViewFilters(filterKeysToDelete); + await deleteViewFilters(filterKeysToDelete); set( savedViewFiltersScopedFamilyState({ scopeId: viewScopeId, @@ -143,8 +130,102 @@ export const useViewFilters = (viewScopeId: string) => { currentViewFilters, ); }, - [viewScopeId], + [ + apolloClient, + createOneMutation, + findManyQuery, + updateOneMutation, + viewScopeId, + ], ); - return { persistViewFilters }; + const upsertViewFilter = useRecoilCallback( + ({ snapshot }) => + (filterToUpsert: Filter) => { + const currentViewId = snapshot + .getLoadable(currentViewIdScopedState({ scopeId: viewScopeId })) + .getValue(); + + if (!currentViewId) { + return; + } + + const savedViewFiltersByKey = snapshot + .getLoadable( + savedViewFiltersByKeyScopedFamilySelector({ + scopeId: viewScopeId, + viewId: currentViewId, + }), + ) + .getValue(); + + if (!savedViewFiltersByKey) { + return; + } + + const onViewFiltersChange = snapshot + .getLoadable(onViewFiltersChangeScopedState({ scopeId: viewScopeId })) + .getValue(); + + const existingSavedFilterId = + savedViewFiltersByKey[filterToUpsert.fieldId]?.id; + + setCurrentViewFilters?.((filters) => { + const newViewFilters = produce(filters, (filtersDraft) => { + const existingFilterIndex = filtersDraft.findIndex( + (filter) => filter.fieldId === filterToUpsert.fieldId, + ); + + if (existingFilterIndex === -1) { + filtersDraft.push({ + ...filterToUpsert, + id: existingSavedFilterId, + }); + return filtersDraft; + } + + filtersDraft[existingFilterIndex] = { + ...filterToUpsert, + id: existingSavedFilterId, + }; + }); + onViewFiltersChange?.(newViewFilters); + return newViewFilters; + }); + }, + ); + + const removeViewFilter = useRecoilCallback( + ({ snapshot }) => + (fieldId: string) => { + const currentViewId = snapshot + .getLoadable(currentViewIdScopedState({ scopeId: viewScopeId })) + .getValue(); + + if (!currentViewId) { + return; + } + + const onViewFiltersChange = snapshot + .getLoadable(onViewFiltersChangeScopedState({ scopeId: viewScopeId })) + .getValue(); + + const currentViewFilters = snapshot + .getLoadable( + currentViewFiltersScopedFamilyState({ + scopeId: viewScopeId, + familyKey: currentViewId, + }), + ) + .getValue(); + + const newViewFilters = currentViewFilters.filter((filter) => { + return filter.fieldId !== fieldId; + }); + setCurrentViewFilters?.(newViewFilters); + onViewFiltersChange?.(newViewFilters); + }, + ); + + return { persistViewFilters, removeViewFilter, upsertViewFilter }; }; diff --git a/front/src/modules/views/hooks/internal/useViewSorts.ts b/front/src/modules/views/hooks/internal/useViewSorts.ts index 6ca7c3c80..75594b414 100644 --- a/front/src/modules/views/hooks/internal/useViewSorts.ts +++ b/front/src/modules/views/hooks/internal/useViewSorts.ts @@ -1,16 +1,25 @@ +import { useApolloClient } from '@apollo/client'; import { produce } from 'immer'; import { useRecoilCallback } from 'recoil'; +import { useFindOneMetadataObject } from '@/metadata/hooks/useFindOneMetadataObject'; import { Sort } from '@/ui/data/sort/types/Sort'; import { currentViewIdScopedState } from '@/views/states/currentViewIdScopedState'; import { currentViewSortsScopedFamilyState } from '@/views/states/currentViewSortsScopedFamilyState'; +import { onViewSortsChangeScopedState } from '@/views/states/onViewSortsChangeScopedState'; import { savedViewSortsScopedFamilyState } from '@/views/states/savedViewSortsScopedFamilyState'; import { savedViewSortsByKeyScopedFamilySelector } from '@/views/states/selectors/savedViewSortsByKeyScopedFamilySelector'; +import { ViewSort } from '@/views/types/ViewSort'; -import { useViewStates } from '../useViewStates'; +import { useViewSetStates } from '../useViewSetStates'; export const useViewSorts = (viewScopeId: string) => { - const { setCurrentViewSorts } = useViewStates(viewScopeId); + const { updateOneMutation, createOneMutation, findManyQuery } = + useFindOneMetadataObject({ + objectNameSingular: 'viewSortV2', + }); + const apolloClient = useApolloClient(); + const { setCurrentViewSorts } = useViewSetStates(viewScopeId); const persistViewSorts = useRecoilCallback( ({ snapshot, set }) => @@ -22,54 +31,48 @@ export const useViewSorts = (viewScopeId: string) => { return; } - const _createViewSorts = (sorts: Sort[]) => { - if (!currentViewId || !sorts.length) return; + const createViewSorts = (viewSortsToCreate: ViewSort[]) => { + if (!viewSortsToCreate.length) return; - // return createViewSortsMutation({ - // variables: { - // data: sorts.map((sort) => ({ - // key: sort.key, - // direction: sort.direction as ViewSortDirection, - // name: sort.definition.label, - // viewId: viewId ?? currentViewId, - // })), - // }, - // }); + return Promise.all( + viewSortsToCreate.map((viewSort) => + apolloClient.mutate({ + mutation: createOneMutation, + variables: { + input: { + fieldId: viewSort.fieldId, + viewId: viewId ?? currentViewId, + direction: viewSort.direction, + }, + }, + refetchQueries: [findManyQuery], + }), + ), + ); }; - const _updateViewSorts = (sorts: Sort[]) => { - if (!currentViewId || !sorts.length) return; + const updateViewSorts = (viewSortsToUpdate: ViewSort[]) => { + if (!viewSortsToUpdate.length) return; - // return Promise.all( - // sorts.map((sort) => - // updateViewSortMutation({ - // variables: { - // data: { - // direction: sort.direction as ViewSortDirection, - // }, - // where: { - // viewId_key: { - // key: sort.key, - // viewId: viewId ?? currentViewId, - // }, - // }, - // }, - // }), - // ), - // ); + return Promise.all( + viewSortsToUpdate.map((viewSort) => + apolloClient.mutate({ + mutation: updateOneMutation, + variables: { + idToUpdate: viewSort.id, + input: { + direction: viewSort.direction, + }, + }, + }), + ), + ); }; - const _deleteViewSorts = (sortKeys: string[]) => { - if (!currentViewId || !sortKeys.length) return; + const deleteViewSorts = (viewSortIdsToDelete: string[]) => { + if (!viewSortIdsToDelete.length) return; - // return deleteViewSortsMutation({ - // variables: { - // where: { - // key: { in: sortKeys }, - // viewId: { equals: viewId ?? currentViewId }, - // }, - // }, - // }); + // Todo }; const currentViewSorts = snapshot @@ -85,7 +88,7 @@ export const useViewSorts = (viewScopeId: string) => { .getLoadable( savedViewSortsByKeyScopedFamilySelector({ scopeId: viewScopeId, - viewId: currentViewId, + viewId: viewId ?? currentViewId, }), ) .getValue(); @@ -98,22 +101,23 @@ export const useViewSorts = (viewScopeId: string) => { } const sortsToCreate = currentViewSorts.filter( - (sort) => !savedViewSortsByKey[sort.key], + (sort) => !savedViewSortsByKey[sort.fieldId], ); - await _createViewSorts(sortsToCreate); + + await createViewSorts(sortsToCreate); const sortsToUpdate = currentViewSorts.filter( (sort) => - savedViewSortsByKey[sort.key] && - savedViewSortsByKey[sort.key].direction !== sort.direction, + savedViewSortsByKey[sort.fieldId] && + savedViewSortsByKey[sort.fieldId].direction !== sort.direction, ); - await _updateViewSorts(sortsToUpdate); + await updateViewSorts(sortsToUpdate); - const sortKeys = currentViewSorts.map((sort) => sort.key); + const sortKeys = currentViewSorts.map((sort) => sort.fieldId); const sortKeysToDelete = Object.keys(savedViewSortsByKey).filter( (previousSortKey) => !sortKeys.includes(previousSortKey), ); - await _deleteViewSorts(sortKeysToDelete); + await deleteViewSorts(sortKeysToDelete); set( savedViewSortsScopedFamilyState({ scopeId: viewScopeId, @@ -122,24 +126,99 @@ export const useViewSorts = (viewScopeId: string) => { currentViewSorts, ); }, - [viewScopeId], + [ + apolloClient, + createOneMutation, + findManyQuery, + updateOneMutation, + viewScopeId, + ], ); - const upsertViewSort = (sortToUpsert: Sort) => { - setCurrentViewSorts?.((sorts) => { - return produce(sorts, (sortsDraft) => { - const index = sortsDraft.findIndex( - (sort) => sort.key === sortToUpsert.key, - ); + const upsertViewSort = useRecoilCallback( + ({ snapshot }) => + (sortToUpsert: Sort) => { + const currentViewId = snapshot + .getLoadable(currentViewIdScopedState({ scopeId: viewScopeId })) + .getValue(); - if (index === -1) { - sortsDraft.push(sortToUpsert); - } else { - sortsDraft[index] = sortToUpsert; + if (!currentViewId) { + return; } - }); - }); - }; - return { persistViewSorts, upsertViewSort }; + const savedViewSortsByKey = snapshot + .getLoadable( + savedViewSortsByKeyScopedFamilySelector({ + scopeId: viewScopeId, + viewId: currentViewId, + }), + ) + .getValue(); + + if (!savedViewSortsByKey) { + return; + } + + const onViewSortsChange = snapshot + .getLoadable(onViewSortsChangeScopedState({ scopeId: viewScopeId })) + .getValue(); + + const existingSavedSortId = + savedViewSortsByKey[sortToUpsert.fieldId]?.id; + + setCurrentViewSorts?.((sorts) => { + const newViewSorts = produce(sorts, (sortsDraft) => { + const existingSortIndex = sortsDraft.findIndex( + (sort) => sort.fieldId === sortToUpsert.fieldId, + ); + + if (existingSortIndex === -1) { + sortsDraft.push({ ...sortToUpsert, id: existingSavedSortId }); + return sortsDraft; + } + + sortsDraft[existingSortIndex] = { + ...sortToUpsert, + id: existingSavedSortId, + }; + }); + onViewSortsChange?.(newViewSorts); + return newViewSorts; + }); + }, + ); + + const removeViewSort = useRecoilCallback( + ({ snapshot }) => + (fieldId: string) => { + const currentViewId = snapshot + .getLoadable(currentViewIdScopedState({ scopeId: viewScopeId })) + .getValue(); + + if (!currentViewId) { + return; + } + + const onViewSortsChange = snapshot + .getLoadable(onViewSortsChangeScopedState({ scopeId: viewScopeId })) + .getValue(); + + const currentViewSorts = snapshot + .getLoadable( + currentViewSortsScopedFamilyState({ + scopeId: viewScopeId, + familyKey: currentViewId, + }), + ) + .getValue(); + + const newViewSorts = currentViewSorts.filter((filter) => { + return filter.fieldId !== fieldId; + }); + setCurrentViewSorts?.(newViewSorts); + onViewSortsChange?.(newViewSorts); + }, + ); + + return { persistViewSorts, upsertViewSort, removeViewSort }; }; diff --git a/front/src/modules/views/hooks/internal/useViews.ts b/front/src/modules/views/hooks/internal/useViews.ts index ce838f5ff..af75cc66e 100644 --- a/front/src/modules/views/hooks/internal/useViews.ts +++ b/front/src/modules/views/hooks/internal/useViews.ts @@ -1,13 +1,21 @@ +import { useApolloClient } from '@apollo/client'; import { useRecoilCallback } from 'recoil'; +import { useFindOneMetadataObject } from '@/metadata/hooks/useFindOneMetadataObject'; import { viewObjectIdScopeState } from '@/views/states/viewObjectIdScopeState'; import { viewTypeScopedState } from '@/views/states/viewTypeScopedState'; import { View } from '@/views/types/View'; export const useViews = (scopeId: string) => { + const { updateOneMutation, createOneMutation, findManyQuery } = + useFindOneMetadataObject({ + objectNameSingular: 'viewV2', + }); + const apolloClient = useApolloClient(); + const createView = useRecoilCallback( ({ snapshot }) => - async (_view: Pick) => { + async (view: Pick) => { const viewObjectId = await snapshot .getLoadable(viewObjectIdScopeState({ scopeId })) .getValue(); @@ -19,27 +27,31 @@ export const useViews = (scopeId: string) => { if (!viewObjectId || !viewType) { return; } - // await createViewMutation({ - // variables: { - // data: { - // ...view, - // objectId: viewObjectId, - // type: viewType, - // }, - // }, - // refetchQueries: [getOperationName(GET_VIEWS) ?? ''], - // }); + await apolloClient.mutate({ + mutation: createOneMutation, + variables: { + input: { + ...view, + objectId: viewObjectId, + type: viewType, + }, + }, + refetchQueries: [findManyQuery], + }); }, ); - const updateView = async (_view: View) => { - // await updateViewMutation({ - // variables: { - // data: { name: view.name }, - // where: { id: view.id }, - // }, - // refetchQueries: [getOperationName(GET_VIEWS) ?? ''], - // }); + const updateView = async (view: View) => { + await apolloClient.mutate({ + mutation: updateOneMutation, + variables: { + idToUpdate: view.id, + input: { + ...view, + }, + }, + refetchQueries: [findManyQuery], + }); }; const deleteView = async (_viewId: string) => { diff --git a/front/src/modules/views/hooks/useRemoveFilter.ts b/front/src/modules/views/hooks/useRemoveFilter.ts deleted file mode 100644 index a61abfe1b..000000000 --- a/front/src/modules/views/hooks/useRemoveFilter.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useView } from '@/views/hooks/useView'; - -export const useRemoveFilter = () => { - const { setCurrentViewFilters } = useView(); - - const removeFilter = (filterKey: string) => { - setCurrentViewFilters?.((filters) => { - return filters.filter((filter) => { - return filter.key !== filterKey; - }); - }); - }; - - return removeFilter; -}; diff --git a/front/src/modules/views/hooks/useUpsertFilter.ts b/front/src/modules/views/hooks/useUpsertFilter.ts deleted file mode 100644 index 3222c6fb5..000000000 --- a/front/src/modules/views/hooks/useUpsertFilter.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { produce } from 'immer'; - -import { Filter } from '@/ui/data/filter/types/Filter'; -import { useView } from '@/views/hooks/useView'; - -export const useUpsertFilter = () => { - const { setCurrentViewFilters } = useView(); - - const upsertFilter = (filterToUpsert: Filter) => { - setCurrentViewFilters?.((filters) => { - return produce(filters, (filtersDraft) => { - const index = filtersDraft.findIndex( - (filter) => filter.key === filterToUpsert.key, - ); - - if (index === -1) { - filtersDraft.push(filterToUpsert); - } else { - filtersDraft[index] = filterToUpsert; - } - }); - }); - }; - - return upsertFilter; -}; diff --git a/front/src/modules/views/hooks/useView.ts b/front/src/modules/views/hooks/useView.ts index b8251a998..4e9decd6d 100644 --- a/front/src/modules/views/hooks/useView.ts +++ b/front/src/modules/views/hooks/useView.ts @@ -10,8 +10,12 @@ import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsS import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState'; import { currentViewIdScopedState } from '../states/currentViewIdScopedState'; import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState'; +import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState'; +import { onViewFiltersChangeScopedState } from '../states/onViewFiltersChangeScopedState'; +import { onViewSortsChangeScopedState } from '../states/onViewSortsChangeScopedState'; import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState'; import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState'; +import { currentViewScopedSelector } from '../states/selectors/currentViewScopedSelector'; import { viewEditModeScopedState } from '../states/viewEditModeScopedState'; import { viewsScopedState } from '../states/viewsScopedState'; @@ -19,7 +23,7 @@ import { useViewFields } from './internal/useViewFields'; import { useViewFilters } from './internal/useViewFilters'; import { useViews } from './internal/useViews'; import { useViewSorts } from './internal/useViewSorts'; -import { useViewStates } from './useViewStates'; +import { useViewSetStates } from './useViewSetStates'; type UseViewProps = { viewScopeId?: string; @@ -42,33 +46,81 @@ export const useView = (props?: UseViewProps) => { setEntityCountInCurrentView, setIsViewBarExpanded, - setAvailableSorts, + setAvailableSortDefinitions, setCurrentViewSorts, setSavedViewSorts, - setAvailableFilters, + setAvailableFilterDefinitions, setCurrentViewFilters, setSavedViewFilters, - setAvailableFields, + setAvailableFieldDefinitions, setCurrentViewFields, setSavedViewFields, - } = useViewStates(scopeId); - const { persistViewSorts, upsertViewSort } = useViewSorts(scopeId); - const { persistViewFilters } = useViewFilters(scopeId); + setOnViewFieldsChange, + setOnViewFiltersChange, + setOnViewSortsChange, + } = useViewSetStates(scopeId); + + const { persistViewSorts, upsertViewSort, removeViewSort } = + useViewSorts(scopeId); + const { persistViewFilters, upsertViewFilter, removeViewFilter } = + useViewFilters(scopeId); const { persistViewFields } = useViewFields(scopeId); - const { createView: internalCreateView, deleteView: internalDeleteView } = - useViews(scopeId); + const { + createView: internalCreateView, + updateView: internalUpdateView, + deleteView: internalDeleteView, + } = useViews(scopeId); const [_, setSearchParams] = useSearchParams(); - const changeView = useCallback( + const changeViewInUrl = useCallback( (viewId: string) => { setSearchParams({ view: viewId }); }, [setSearchParams], ); + const loadView = useRecoilCallback(({ snapshot }) => (viewId: string) => { + setCurrentViewId?.(viewId); + const currentViewFields = snapshot + .getLoadable( + currentViewFieldsScopedFamilyState({ scopeId, familyKey: viewId }), + ) + .getValue(); + + const onViewFieldsChange = snapshot + .getLoadable(onViewFieldsChangeScopedState({ scopeId })) + .getValue(); + + onViewFieldsChange?.(currentViewFields); + + const currentViewFilters = snapshot + .getLoadable( + currentViewFiltersScopedFamilyState({ scopeId, familyKey: viewId }), + ) + .getValue(); + + const onViewFiltersChange = snapshot + .getLoadable(onViewFiltersChangeScopedState({ scopeId })) + .getValue(); + + onViewFiltersChange?.(currentViewFilters); + + const currentViewSorts = snapshot + .getLoadable( + currentViewSortsScopedFamilyState({ scopeId, familyKey: viewId }), + ) + .getValue(); + + const onViewSortsChange = snapshot + .getLoadable(onViewSortsChangeScopedState({ scopeId })) + .getValue(); + + onViewSortsChange?.(currentViewSorts); + }); + const resetViewBar = useRecoilCallback(({ snapshot }) => () => { const savedViewFilters = snapshot .getLoadable( @@ -95,7 +147,6 @@ export const useView = (props?: UseViewProps) => { setCurrentViewSorts?.(savedViewSorts); } setViewEditMode?.('none'); - setIsViewBarExpanded?.(false); }); const createView = useRecoilCallback( @@ -156,10 +207,10 @@ export const useView = (props?: UseViewProps) => { await persistViewFilters(newViewId); await persistViewSorts(newViewId); - changeView(newViewId); + changeViewInUrl(newViewId); }, [ - changeView, + changeViewInUrl, currentViewId, internalCreateView, persistViewFields, @@ -195,15 +246,32 @@ export const useView = (props?: UseViewProps) => { const handleViewNameSubmit = useRecoilCallback( ({ snapshot }) => async (name?: string) => { + if (!name) { + return; + } + const viewEditMode = snapshot .getLoadable(viewEditModeScopedState({ scopeId })) .getValue(); + const currentView = snapshot + .getLoadable(currentViewScopedSelector(scopeId)) + .getValue(); + + if (!currentView) { + return; + } + if (viewEditMode === 'create' && name) { await createView(name); + } else { + await internalUpdateView({ + ...currentView, + name, + }); } }, - [createView, scopeId], + [createView, internalUpdateView, scopeId], ); return { @@ -224,20 +292,28 @@ export const useView = (props?: UseViewProps) => { setViewType, setEntityCountInCurrentView, - setAvailableSorts, + setAvailableSortDefinitions, setCurrentViewSorts, setSavedViewSorts, upsertViewSort, + removeViewSort, - setAvailableFilters, + setAvailableFilterDefinitions, setCurrentViewFilters, setSavedViewFilters, + upsertViewFilter, + removeViewFilter, - setAvailableFields, + setAvailableFieldDefinitions, setCurrentViewFields, setSavedViewFields, persistViewFields, - changeView, + changeViewInUrl, + loadView, + + setOnViewFieldsChange, + setOnViewFiltersChange, + setOnViewSortsChange, }; }; diff --git a/front/src/modules/views/hooks/useViewInternalStates.ts b/front/src/modules/views/hooks/useViewGetStates.ts similarity index 66% rename from front/src/modules/views/hooks/useViewInternalStates.ts rename to front/src/modules/views/hooks/useViewGetStates.ts index 9f08a1253..3a50e95af 100644 --- a/front/src/modules/views/hooks/useViewInternalStates.ts +++ b/front/src/modules/views/hooks/useViewGetStates.ts @@ -5,9 +5,9 @@ import { useRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/useRec import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext'; -import { availableFieldsScopedState } from '../states/availableFieldsScopedState'; -import { availableFiltersScopedState } from '../states/availableFiltersScopedState'; -import { availableSortsScopedState } from '../states/availableSortsScopedState'; +import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState'; +import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState'; +import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState'; import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState'; import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState'; import { currentViewIdScopedState } from '../states/currentViewIdScopedState'; @@ -31,17 +31,14 @@ import { viewObjectIdScopeState } from '../states/viewObjectIdScopeState'; import { viewsScopedState } from '../states/viewsScopedState'; import { viewTypeScopedState } from '../states/viewTypeScopedState'; -export const useViewInternalStates = ( - viewScopeId?: string, - viewId?: string, -) => { +export const useViewGetStates = (viewScopeId?: string, viewId?: string) => { const scopeId = useAvailableScopeIdOrThrow( ViewScopeInternalContext, viewScopeId, ); // View - const [currentViewId, setCurrentViewId] = useRecoilScopedStateV2( + const [currentViewId] = useRecoilScopedStateV2( currentViewIdScopedState, scopeId, ); @@ -50,39 +47,38 @@ export const useViewInternalStates = ( const currentView = useRecoilValue(currentViewScopedSelector(scopeId)); - const [viewEditMode, setViewEditMode] = useRecoilScopedStateV2( + const [viewEditMode] = useRecoilScopedStateV2( viewEditModeScopedState, scopeId, ); - const [views, setViews] = useRecoilScopedStateV2(viewsScopedState, scopeId); + const [views] = useRecoilScopedStateV2(viewsScopedState, scopeId); - const [viewObjectId, setViewObjectId] = useRecoilScopedStateV2( + const [viewObjectId] = useRecoilScopedStateV2( viewObjectIdScopeState, scopeId, ); - const [viewType, setViewType] = useRecoilScopedStateV2( - viewTypeScopedState, + const [viewType] = useRecoilScopedStateV2(viewTypeScopedState, scopeId); + + const [entityCountInCurrentView] = useRecoilScopedStateV2( + entityCountInCurrentViewScopedState, scopeId, ); - const [entityCountInCurrentView, setEntityCountInCurrentView] = - useRecoilScopedStateV2(entityCountInCurrentViewScopedState, scopeId); - - const [isViewBarExpanded, setIsViewBarExpanded] = useRecoilScopedStateV2( + const [isViewBarExpanded] = useRecoilScopedStateV2( isViewBarExpandedScopedState, scopeId, ); // ViewSorts - const [currentViewSorts, setCurrentViewSorts] = useRecoilScopedFamilyState( + const [currentViewSorts] = useRecoilScopedFamilyState( currentViewSortsScopedFamilyState, scopeId, familyItemId, ); - const [savedViewSorts, setSavedViewSorts] = useRecoilScopedFamilyState( + const [savedViewSorts] = useRecoilScopedFamilyState( savedViewSortsScopedFamilyState, scopeId, familyItemId, @@ -95,8 +91,8 @@ export const useViewInternalStates = ( }), ); - const [availableSorts, setAvailableSorts] = useRecoilScopedStateV2( - availableSortsScopedState, + const [availableSortDefinitions] = useRecoilScopedStateV2( + availableSortDefinitionsScopedState, scopeId, ); @@ -108,14 +104,13 @@ export const useViewInternalStates = ( ); // ViewFilters - const [currentViewFilters, setCurrentViewFilters] = - useRecoilScopedFamilyState( - currentViewFiltersScopedFamilyState, - scopeId, - familyItemId, - ); + const [currentViewFilters] = useRecoilScopedFamilyState( + currentViewFiltersScopedFamilyState, + scopeId, + familyItemId, + ); - const [savedViewFilters, setSavedViewFilters] = useRecoilScopedFamilyState( + const [savedViewFilters] = useRecoilScopedFamilyState( savedViewFiltersScopedFamilyState, scopeId, familyItemId, @@ -128,8 +123,8 @@ export const useViewInternalStates = ( }), ); - const [availableFilters, setAvailableFilters] = useRecoilScopedStateV2( - availableFiltersScopedState, + const [availableFilterDefinitions] = useRecoilScopedStateV2( + availableFilterDefinitionsScopedState, scopeId, ); @@ -141,18 +136,18 @@ export const useViewInternalStates = ( ); // ViewFields - const [availableFields, setAvailableFields] = useRecoilScopedStateV2( - availableFieldsScopedState, + const [availableFieldDefinitions] = useRecoilScopedStateV2( + availableFieldDefinitionsScopedState, scopeId, ); - const [currentViewFields, setCurrentViewFields] = useRecoilScopedFamilyState( + const [currentViewFields] = useRecoilScopedFamilyState( currentViewFieldsScopedFamilyState, scopeId, familyItemId, ); - const [savedViewFields, setSavedViewFields] = useRecoilScopedFamilyState( + const [savedViewFields] = useRecoilScopedFamilyState( savedViewFieldsScopedFamilyState, scopeId, familyItemId, @@ -166,17 +161,17 @@ export const useViewInternalStates = ( ); // ViewChangeHandlers - const [onViewSortsChange, setOnViewSortsChange] = useRecoilScopedStateV2( + const [onViewSortsChange] = useRecoilScopedStateV2( onViewSortsChangeScopedState, scopeId, ); - const [onViewFiltersChange, setOnViewFiltersChange] = useRecoilScopedStateV2( + const [onViewFiltersChange] = useRecoilScopedStateV2( onViewFiltersChangeScopedState, scopeId, ); - const [onViewFieldsChange, setOnViewFieldsChange] = useRecoilScopedStateV2( + const [onViewFieldsChange] = useRecoilScopedStateV2( onViewFieldsChangeScopedState, scopeId, ); @@ -184,52 +179,33 @@ export const useViewInternalStates = ( return { currentViewId, currentView, - setCurrentViewId, isViewBarExpanded, - setIsViewBarExpanded, views, - setViews, viewEditMode, - setViewEditMode, viewObjectId, - setViewObjectId, viewType, - setViewType, entityCountInCurrentView, - setEntityCountInCurrentView, - availableSorts, - setAvailableSorts, + availableSortDefinitions, currentViewSorts, - setCurrentViewSorts, savedViewSorts, savedViewSortsByKey, - setSavedViewSorts, canPersistSorts, - availableFilters, - setAvailableFilters, + availableFilterDefinitions, currentViewFilters, - setCurrentViewFilters, savedViewFilters, savedViewFiltersByKey, - setSavedViewFilters, canPersistFilters, - availableFields, - setAvailableFields, + availableFieldDefinitions, currentViewFields, savedViewFieldsByKey, - setCurrentViewFields, savedViewFields, - setSavedViewFields, onViewSortsChange, - setOnViewSortsChange, onViewFiltersChange, - setOnViewFiltersChange, onViewFieldsChange, - setOnViewFieldsChange, }; }; diff --git a/front/src/modules/views/hooks/useViewStates.ts b/front/src/modules/views/hooks/useViewSetStates.ts similarity index 71% rename from front/src/modules/views/hooks/useViewStates.ts rename to front/src/modules/views/hooks/useViewSetStates.ts index dcae7e196..e41e4921e 100644 --- a/front/src/modules/views/hooks/useViewStates.ts +++ b/front/src/modules/views/hooks/useViewSetStates.ts @@ -4,15 +4,18 @@ import { useSetRecoilScopedStateV2 } from '@/ui/utilities/recoil-scope/hooks/use import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { ViewScopeInternalContext } from '../scopes/scope-internal-context/ViewScopeInternalContext'; -import { availableFieldsScopedState } from '../states/availableFieldsScopedState'; -import { availableFiltersScopedState } from '../states/availableFiltersScopedState'; -import { availableSortsScopedState } from '../states/availableSortsScopedState'; +import { availableFieldDefinitionsScopedState } from '../states/availableFieldDefinitionsScopedState'; +import { availableFilterDefinitionsScopedState } from '../states/availableFilterDefinitionsScopedState'; +import { availableSortDefinitionsScopedState } from '../states/availableSortDefinitionsScopedState'; import { currentViewFieldsScopedFamilyState } from '../states/currentViewFieldsScopedFamilyState'; import { currentViewFiltersScopedFamilyState } from '../states/currentViewFiltersScopedFamilyState'; import { currentViewIdScopedState } from '../states/currentViewIdScopedState'; import { currentViewSortsScopedFamilyState } from '../states/currentViewSortsScopedFamilyState'; import { entityCountInCurrentViewScopedState } from '../states/entityCountInCurrentViewScopedState'; import { isViewBarExpandedScopedState } from '../states/isViewBarExpandedScopedState'; +import { onViewFieldsChangeScopedState } from '../states/onViewFieldsChangeScopedState'; +import { onViewFiltersChangeScopedState } from '../states/onViewFiltersChangeScopedState'; +import { onViewSortsChangeScopedState } from '../states/onViewSortsChangeScopedState'; import { savedViewFieldsScopedFamilyState } from '../states/savedViewFieldsScopedFamilyState'; import { savedViewFiltersScopedFamilyState } from '../states/savedViewFiltersScopedFamilyState'; import { savedViewSortsScopedFamilyState } from '../states/savedViewSortsScopedFamilyState'; @@ -21,7 +24,7 @@ import { viewObjectIdScopeState } from '../states/viewObjectIdScopeState'; import { viewsScopedState } from '../states/viewsScopedState'; import { viewTypeScopedState } from '../states/viewTypeScopedState'; -export const useViewStates = (viewScopeId?: string, viewId?: string) => { +export const useViewSetStates = (viewScopeId?: string, viewId?: string) => { const scopeId = useAvailableScopeIdOrThrow( ViewScopeInternalContext, viewScopeId, @@ -71,8 +74,8 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => { familyItemId, ); - const setAvailableSorts = useSetRecoilScopedStateV2( - availableSortsScopedState, + const setAvailableSortDefinitions = useSetRecoilScopedStateV2( + availableSortDefinitionsScopedState, scopeId, ); @@ -89,14 +92,14 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => { familyItemId, ); - const setAvailableFilters = useSetRecoilScopedStateV2( - availableFiltersScopedState, + const setAvailableFilterDefinitions = useSetRecoilScopedStateV2( + availableFilterDefinitionsScopedState, scopeId, ); // ViewFields - const setAvailableFields = useSetRecoilScopedStateV2( - availableFieldsScopedState, + const setAvailableFieldDefinitions = useSetRecoilScopedStateV2( + availableFieldDefinitionsScopedState, scopeId, ); @@ -112,6 +115,21 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => { familyItemId, ); + const setOnViewFieldsChange = useSetRecoilScopedStateV2( + onViewFieldsChangeScopedState, + scopeId, + ); + + const setOnViewFiltersChange = useSetRecoilScopedStateV2( + onViewFiltersChangeScopedState, + scopeId, + ); + + const setOnViewSortsChange = useSetRecoilScopedStateV2( + onViewSortsChangeScopedState, + scopeId, + ); + return { currentViewId, setCurrentViewId, @@ -123,16 +141,20 @@ export const useViewStates = (viewScopeId?: string, viewId?: string) => { setViewEditMode, setEntityCountInCurrentView, - setAvailableSorts, + setAvailableSortDefinitions, setCurrentViewSorts, setSavedViewSorts, - setAvailableFilters, + setAvailableFilterDefinitions, setCurrentViewFilters, setSavedViewFilters, - setAvailableFields, + setAvailableFieldDefinitions, setCurrentViewFields, setSavedViewFields, + + setOnViewFieldsChange, + setOnViewFiltersChange, + setOnViewSortsChange, }; }; diff --git a/front/src/modules/views/scopes/init-effect/ViewScopeInitEffect.tsx b/front/src/modules/views/scopes/init-effect/ViewScopeInitEffect.tsx index 8aa99aade..25a5147a1 100644 --- a/front/src/modules/views/scopes/init-effect/ViewScopeInitEffect.tsx +++ b/front/src/modules/views/scopes/init-effect/ViewScopeInitEffect.tsx @@ -3,7 +3,6 @@ import { useEffect } from 'react'; import { Filter } from '@/ui/data/filter/types/Filter'; import { Sort } from '@/ui/data/sort/types/Sort'; import { useView } from '@/views/hooks/useView'; -import { useViewInternalStates } from '@/views/hooks/useViewInternalStates'; import { ViewField } from '@/views/types/ViewField'; type ViewScopeInitEffectProps = { @@ -14,17 +13,15 @@ type ViewScopeInitEffectProps = { }; export const ViewScopeInitEffect = ({ - viewScopeId, onViewSortsChange, onViewFiltersChange, onViewFieldsChange, }: ViewScopeInitEffectProps) => { - const { currentViewId } = useView(); const { setOnViewSortsChange, setOnViewFieldsChange, setOnViewFiltersChange, - } = useViewInternalStates(viewScopeId, currentViewId); + } = useView(); useEffect(() => { setOnViewSortsChange(() => onViewSortsChange); diff --git a/front/src/modules/views/states/availableFieldsScopedState.ts b/front/src/modules/views/states/availableFieldDefinitionsScopedState.ts similarity index 71% rename from front/src/modules/views/states/availableFieldsScopedState.ts rename to front/src/modules/views/states/availableFieldDefinitionsScopedState.ts index 74bddf8f6..dee79887f 100644 --- a/front/src/modules/views/states/availableFieldsScopedState.ts +++ b/front/src/modules/views/states/availableFieldDefinitionsScopedState.ts @@ -2,9 +2,9 @@ import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition'; import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata'; import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState'; -export const availableFieldsScopedState = createScopedState< +export const availableFieldDefinitionsScopedState = createScopedState< ColumnDefinition[] >({ - key: 'availableFieldsScopedState', + key: 'availableFieldDefinitionsScopedState', defaultValue: [], }); diff --git a/front/src/modules/ui/data/filter/states/availableFiltersScopedState.ts b/front/src/modules/views/states/availableFilterDefinitionsScopedState.ts similarity index 64% rename from front/src/modules/ui/data/filter/states/availableFiltersScopedState.ts rename to front/src/modules/views/states/availableFilterDefinitionsScopedState.ts index 9c38f3952..d1949810c 100644 --- a/front/src/modules/ui/data/filter/states/availableFiltersScopedState.ts +++ b/front/src/modules/views/states/availableFilterDefinitionsScopedState.ts @@ -1,9 +1,9 @@ import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition'; import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState'; -export const availableFiltersScopedState = createScopedState< +export const availableFilterDefinitionsScopedState = createScopedState< FilterDefinition[] >({ - key: 'availableFiltersScopedState', + key: 'availableFilterDefinitionsScopedState', defaultValue: [], }); diff --git a/front/src/modules/views/states/availableSortsScopedState.ts b/front/src/modules/views/states/availableSortDefinitionsScopedState.ts similarity index 56% rename from front/src/modules/views/states/availableSortsScopedState.ts rename to front/src/modules/views/states/availableSortDefinitionsScopedState.ts index e5aa10d0d..f38d857fa 100644 --- a/front/src/modules/views/states/availableSortsScopedState.ts +++ b/front/src/modules/views/states/availableSortDefinitionsScopedState.ts @@ -1,7 +1,9 @@ import { SortDefinition } from '@/ui/data/sort/types/SortDefinition'; import { createScopedState } from '@/ui/utilities/recoil-scope/utils/createScopedState'; -export const availableSortsScopedState = createScopedState({ - key: 'availableSortsScopedState', +export const availableSortDefinitionsScopedState = createScopedState< + SortDefinition[] +>({ + key: 'availableSortDefinitionsScopedState', defaultValue: [], }); diff --git a/front/src/modules/views/states/currentViewFiltersScopedFamilyState.ts b/front/src/modules/views/states/currentViewFiltersScopedFamilyState.ts index 4e0ac6a08..41a8eb9e0 100644 --- a/front/src/modules/views/states/currentViewFiltersScopedFamilyState.ts +++ b/front/src/modules/views/states/currentViewFiltersScopedFamilyState.ts @@ -1,8 +1,9 @@ -import { Filter } from '@/ui/data/filter/types/Filter'; import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState'; +import { ViewFilter } from '../types/ViewFilter'; + export const currentViewFiltersScopedFamilyState = createScopedFamilyState< - Filter[], + ViewFilter[], string >({ key: 'currentViewFiltersScopedFamilyState', diff --git a/front/src/modules/views/states/currentViewSortsScopedFamilyState.ts b/front/src/modules/views/states/currentViewSortsScopedFamilyState.ts index 70ef8ec8d..d4cedc66c 100644 --- a/front/src/modules/views/states/currentViewSortsScopedFamilyState.ts +++ b/front/src/modules/views/states/currentViewSortsScopedFamilyState.ts @@ -1,8 +1,9 @@ -import { Sort } from '@/ui/data/sort/types/Sort'; import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState'; +import { ViewSort } from '../types/ViewSort'; + export const currentViewSortsScopedFamilyState = createScopedFamilyState< - Sort[], + ViewSort[], string >({ key: 'currentViewSortsScopedFamilyState', diff --git a/front/src/modules/views/states/savedViewFiltersScopedFamilyState.ts b/front/src/modules/views/states/savedViewFiltersScopedFamilyState.ts index 7b6d8ccd9..96b0cac67 100644 --- a/front/src/modules/views/states/savedViewFiltersScopedFamilyState.ts +++ b/front/src/modules/views/states/savedViewFiltersScopedFamilyState.ts @@ -1,8 +1,9 @@ -import { Filter } from '@/ui/data/filter/types/Filter'; import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState'; +import { ViewFilter } from '../types/ViewFilter'; + export const savedViewFiltersScopedFamilyState = createScopedFamilyState< - Filter[], + ViewFilter[], string >({ key: 'savedViewFiltersScopedFamilyState', diff --git a/front/src/modules/views/states/savedViewSortsScopedFamilyState.ts b/front/src/modules/views/states/savedViewSortsScopedFamilyState.ts index 1b20c4c23..14316cc7c 100644 --- a/front/src/modules/views/states/savedViewSortsScopedFamilyState.ts +++ b/front/src/modules/views/states/savedViewSortsScopedFamilyState.ts @@ -1,8 +1,9 @@ -import { Sort } from '@/ui/data/sort/types/Sort'; import { createScopedFamilyState } from '@/ui/utilities/recoil-scope/utils/createScopedFamilyState'; +import { ViewSort } from '../types/ViewSort'; + export const savedViewSortsScopedFamilyState = createScopedFamilyState< - Sort[], + ViewSort[], string >({ key: 'savedViewSortsScopedFamilyState', diff --git a/front/src/modules/views/states/selectors/savedViewFiltersByKeyScopedFamilySelector.ts b/front/src/modules/views/states/selectors/savedViewFiltersByKeyScopedFamilySelector.ts index 33b79f6cd..d7e03a0bb 100644 --- a/front/src/modules/views/states/selectors/savedViewFiltersByKeyScopedFamilySelector.ts +++ b/front/src/modules/views/states/selectors/savedViewFiltersByKeyScopedFamilySelector.ts @@ -1,6 +1,6 @@ import { selectorFamily } from 'recoil'; -import { Filter } from '@/ui/data/filter/types/Filter'; +import { ViewFilter } from '@/views/types/ViewFilter'; import { savedViewFiltersScopedFamilyState } from '../savedViewFiltersScopedFamilyState'; @@ -17,8 +17,8 @@ export const savedViewFiltersByKeyScopedFamilySelector = selectorFamily({ scopeId: scopeId, familyKey: viewId, }), - ).reduce>( - (result, filter) => ({ ...result, [filter.key]: filter }), + ).reduce>( + (result, filter) => ({ ...result, [filter.fieldId]: filter }), {}, ); }, diff --git a/front/src/modules/views/states/selectors/savedViewSortsByKeyScopedFamilySelector.ts b/front/src/modules/views/states/selectors/savedViewSortsByKeyScopedFamilySelector.ts index 619dbf513..cdfe2bfae 100644 --- a/front/src/modules/views/states/selectors/savedViewSortsByKeyScopedFamilySelector.ts +++ b/front/src/modules/views/states/selectors/savedViewSortsByKeyScopedFamilySelector.ts @@ -1,6 +1,6 @@ import { selectorFamily } from 'recoil'; -import { Sort } from '@/ui/data/sort/types/Sort'; +import { ViewSort } from '@/views/types/ViewSort'; import { savedViewSortsScopedFamilyState } from '../savedViewSortsScopedFamilyState'; @@ -17,8 +17,8 @@ export const savedViewSortsByKeyScopedFamilySelector = selectorFamily({ scopeId: scopeId, familyKey: viewId, }), - ).reduce>( - (result, sort) => ({ ...result, [sort.key]: sort }), + ).reduce>( + (result, sort) => ({ ...result, [sort.fieldId]: sort }), {}, ); }, diff --git a/front/src/modules/views/types/ViewField.ts b/front/src/modules/views/types/ViewField.ts index 1e4da2188..3b4872a21 100644 --- a/front/src/modules/views/types/ViewField.ts +++ b/front/src/modules/views/types/ViewField.ts @@ -1,7 +1,11 @@ +import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition'; +import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata'; + export type ViewField = { id: string; fieldId: string; position: number; isVisible: boolean; size: number; + definition: ColumnDefinition; }; diff --git a/front/src/modules/views/types/ViewFilter.ts b/front/src/modules/views/types/ViewFilter.ts new file mode 100644 index 000000000..e3aec8bbf --- /dev/null +++ b/front/src/modules/views/types/ViewFilter.ts @@ -0,0 +1,12 @@ +import { FilterDefinition } from '@/ui/data/filter/types/FilterDefinition'; + +import { ViewFilterOperand } from './ViewFilterOperand'; + +export type ViewFilter = { + id?: string; + fieldId: string; + operand: ViewFilterOperand; + value: string; + displayValue: string; + definition: FilterDefinition; +}; diff --git a/front/src/modules/views/types/ViewSort.ts b/front/src/modules/views/types/ViewSort.ts new file mode 100644 index 000000000..a9f4dc521 --- /dev/null +++ b/front/src/modules/views/types/ViewSort.ts @@ -0,0 +1,9 @@ +import { SortDefinition } from '@/ui/data/sort/types/SortDefinition'; +import { SortDirection } from '@/ui/data/sort/types/SortDirection'; + +export type ViewSort = { + id?: string; + fieldId: string; + direction: SortDirection; + definition: SortDefinition; +}; diff --git a/front/src/modules/views/utils/columnDefinitionToViewField.ts b/front/src/modules/views/utils/columnDefinitionToViewField.ts index f85e8c86d..9eaeac809 100644 --- a/front/src/modules/views/utils/columnDefinitionToViewField.ts +++ b/front/src/modules/views/utils/columnDefinitionToViewField.ts @@ -12,5 +12,6 @@ export const columnDefinitionsToViewFields = ( position: columnDefinition.position, size: columnDefinition.size, isVisible: columnDefinition.isVisible ?? true, + definition: columnDefinition, })); }; diff --git a/front/src/modules/views/utils/viewFieldsToBoardFieldDefinitions.ts b/front/src/modules/views/utils/viewFieldsToBoardFieldDefinitions.ts new file mode 100644 index 000000000..8fea30cb6 --- /dev/null +++ b/front/src/modules/views/utils/viewFieldsToBoardFieldDefinitions.ts @@ -0,0 +1,35 @@ +import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata'; +import { BoardFieldDefinition } from '@/ui/layout/board/types/BoardFieldDefinition'; +import { assertNotNull } from '~/utils/assert'; + +import { ViewField } from '../types/ViewField'; + +export const viewFieldsToBoardFieldDefinitions = ( + viewFields: ViewField[], + fieldsMetadata: BoardFieldDefinition[], +): BoardFieldDefinition[] => { + return viewFields + .map((viewField) => { + const correspondingFieldMetadata = fieldsMetadata.find( + ({ fieldId }) => viewField.fieldId === fieldId, + ); + + return correspondingFieldMetadata + ? { + fieldId: viewField.fieldId, + label: correspondingFieldMetadata.label, + metadata: correspondingFieldMetadata.metadata, + entityChipDisplayMapper: + correspondingFieldMetadata.entityChipDisplayMapper, + infoTooltipContent: correspondingFieldMetadata.infoTooltipContent, + basePathToShowPage: correspondingFieldMetadata.basePathToShowPage, + Icon: correspondingFieldMetadata.Icon, + type: correspondingFieldMetadata.type, + position: viewField.position, + isVisible: viewField.isVisible, + viewFieldId: viewField.id, + } + : null; + }) + .filter(assertNotNull); +}; diff --git a/front/src/modules/views/utils/viewFiltersToFilters.ts b/front/src/modules/views/utils/viewFiltersToFilters.ts new file mode 100644 index 000000000..b1ec69d02 --- /dev/null +++ b/front/src/modules/views/utils/viewFiltersToFilters.ts @@ -0,0 +1,15 @@ +import { Filter } from '@/ui/data/filter/types/Filter'; + +import { ViewFilter } from '../types/ViewFilter'; + +export const viewFiltersToFilters = (viewFilters: ViewFilter[]): Filter[] => { + return viewFilters.map((viewFilter) => { + return { + fieldId: viewFilter.fieldId, + value: viewFilter.value, + displayValue: viewFilter.displayValue, + operand: viewFilter.operand, + definition: viewFilter.definition, + }; + }); +}; diff --git a/front/src/modules/views/utils/viewSortsToSorts.ts b/front/src/modules/views/utils/viewSortsToSorts.ts new file mode 100644 index 000000000..47a587115 --- /dev/null +++ b/front/src/modules/views/utils/viewSortsToSorts.ts @@ -0,0 +1,13 @@ +import { Sort } from '@/ui/data/sort/types/Sort'; + +import { ViewSort } from '../types/ViewSort'; + +export const viewSortsToSorts = (viewSorts: ViewSort[]): Sort[] => { + return viewSorts.map((viewSort) => { + return { + fieldId: viewSort.fieldId, + direction: viewSort.direction, + definition: viewSort.definition, + }; + }); +}; diff --git a/front/src/pages/companies/CompanyShow.tsx b/front/src/pages/companies/CompanyShow.tsx index f12f7ae96..92cce9463 100644 --- a/front/src/pages/companies/CompanyShow.tsx +++ b/front/src/pages/companies/CompanyShow.tsx @@ -28,7 +28,7 @@ import { getLogoUrlFromDomainName } from '~/utils'; import { CompanyNameEditableField } from '../../modules/companies/editable-field/components/CompanyNameEditableField'; import { ShowPageContainer } from '../../modules/ui/layout/page/ShowPageContainer'; -import { companyShowFieldDefinition } from './constants/companyShowFieldDefinition'; +import { companyShowFieldDefinitions } from './constants/companyShowFieldDefinitions'; export const CompanyShow = () => { const companyId = useParams().companyId ?? ''; @@ -90,7 +90,7 @@ export const CompanyShow = () => { avatarType="squared" /> - {companyShowFieldDefinition.map((fieldDefinition) => { + {companyShowFieldDefinitions.map((fieldDefinition) => { return ( [] = [ - { - key: 'name', - label: 'Name', - Icon: IconBuildingSkyscraper, - type: 'text', - }, - { - key: 'employees', - label: 'Employees', - Icon: IconUsers, - type: 'number', - }, - { - key: 'domainName', - label: 'URL', - Icon: IconLink, - type: 'text', - }, - { - key: 'address', - label: 'Address', - Icon: IconMap, - type: 'text', - }, - { - key: 'createdAt', - label: 'Created at', - Icon: IconCalendarEvent, - type: 'date', - }, - { - key: 'accountOwnerId', - label: 'Account owner', - Icon: IconUser, - type: 'entity', - entitySelectComponent: , - }, -]; diff --git a/front/src/pages/companies/constants/companyShowFieldDefinition.tsx b/front/src/pages/companies/constants/companyShowFieldDefinitions.tsx similarity index 96% rename from front/src/pages/companies/constants/companyShowFieldDefinition.tsx rename to front/src/pages/companies/constants/companyShowFieldDefinitions.tsx index 808ace686..be330848d 100644 --- a/front/src/pages/companies/constants/companyShowFieldDefinition.tsx +++ b/front/src/pages/companies/constants/companyShowFieldDefinitions.tsx @@ -20,7 +20,7 @@ import { import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; import { User } from '~/generated/graphql'; -export const companyShowFieldDefinition: FieldDefinition[] = [ +export const companyShowFieldDefinitions: FieldDefinition[] = [ { fieldId: 'domainName', label: 'Domain name', diff --git a/front/src/pages/companies/constants/companyTableFilterDefinitions.tsx b/front/src/pages/companies/constants/companyTableFilterDefinitions.tsx new file mode 100644 index 000000000..586832af9 --- /dev/null +++ b/front/src/pages/companies/constants/companyTableFilterDefinitions.tsx @@ -0,0 +1,52 @@ +import { FilterDefinitionByEntity } from '@/ui/data/filter/types/FilterDefinitionByEntity'; +import { + IconBuildingSkyscraper, + IconCalendarEvent, + IconLink, + IconMap, + IconUser, + IconUsers, +} from '@/ui/display/icon/index'; +import { FilterDropdownUserSearchSelect } from '@/users/components/FilterDropdownUserSearchSelect'; +import { Company } from '~/generated/graphql'; + +export const companyTableFilterDefinitions: FilterDefinitionByEntity[] = + [ + { + fieldId: 'name', + label: 'Name', + Icon: IconBuildingSkyscraper, + type: 'text', + }, + { + fieldId: 'employees', + label: 'Employees', + Icon: IconUsers, + type: 'number', + }, + { + fieldId: 'domainName', + label: 'URL', + Icon: IconLink, + type: 'text', + }, + { + fieldId: 'address', + label: 'Address', + Icon: IconMap, + type: 'text', + }, + { + fieldId: 'createdAt', + label: 'Created at', + Icon: IconCalendarEvent, + type: 'date', + }, + { + fieldId: 'accountOwnerId', + label: 'Account owner', + Icon: IconUser, + type: 'entity', + entitySelectComponent: , + }, + ]; diff --git a/front/src/pages/companies/companies-sorts.tsx b/front/src/pages/companies/constants/companyTableSortDefinitions.tsx similarity index 71% rename from front/src/pages/companies/companies-sorts.tsx rename to front/src/pages/companies/constants/companyTableSortDefinitions.tsx index bbdf7d6d7..57f55d72d 100644 --- a/front/src/pages/companies/companies-sorts.tsx +++ b/front/src/pages/companies/constants/companyTableSortDefinitions.tsx @@ -7,29 +7,29 @@ import { IconUsers, } from '@/ui/display/icon/index'; -export const companyAvailableSorts: SortDefinition[] = [ +export const companyTableSortDefinitions: SortDefinition[] = [ { - key: 'name', + fieldId: 'name', label: 'Name', Icon: IconBuildingSkyscraper, }, { - key: 'employees', + fieldId: 'employees', label: 'Employees', Icon: IconUsers, }, { - key: 'domainName', + fieldId: 'domainName', label: 'Url', Icon: IconLink, }, { - key: 'address', + fieldId: 'address', label: 'Address', Icon: IconMap, }, { - key: 'createdAt', + fieldId: 'createdAt', label: 'Creation', Icon: IconCalendarEvent, }, diff --git a/front/src/pages/opportunities/Opportunities.tsx b/front/src/pages/opportunities/Opportunities.tsx index c4a14fd94..7e529baee 100644 --- a/front/src/pages/opportunities/Opportunities.tsx +++ b/front/src/pages/opportunities/Opportunities.tsx @@ -1,3 +1,5 @@ +import styled from '@emotion/styled'; + import { CompanyBoard } from '@/companies/board/components/CompanyBoard'; import { CompanyBoardRecoilScopeContext } from '@/companies/states/recoil-scope-contexts/CompanyBoardRecoilScopeContext'; import { PipelineAddButton } from '@/pipeline/components/PipelineAddButton'; @@ -11,6 +13,11 @@ import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope' import { useUpdatePipelineStageMutation } from '~/generated/graphql'; import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions'; +const StyledBoardContainer = styled.div` + display: flex; + width: 100%; +`; + export const Opportunities = () => { const { handlePipelineStageAdd, handlePipelineStageDelete } = usePipelineStages(); @@ -51,11 +58,13 @@ export const Opportunities = () => { scopeId="opportunities" CustomRecoilScopeContext={CompanyBoardRecoilScopeContext} > - + + + diff --git a/front/src/pages/opportunities/opportunities-filters.tsx b/front/src/pages/opportunities/constants/opportunityBoardFilterDefinitions.tsx similarity index 72% rename from front/src/pages/opportunities/opportunities-filters.tsx rename to front/src/pages/opportunities/constants/opportunityBoardFilterDefinitions.tsx index 719db91ed..189016c56 100644 --- a/front/src/pages/opportunities/opportunities-filters.tsx +++ b/front/src/pages/opportunities/constants/opportunityBoardFilterDefinitions.tsx @@ -8,31 +8,31 @@ import { } from '@/ui/display/icon/index'; import { PipelineProgress } from '~/generated/graphql'; -import { FilterDropdownPeopleSearchSelect } from '../../modules/people/components/FilterDropdownPeopleSearchSelect'; +import { FilterDropdownPeopleSearchSelect } from '../../../modules/people/components/FilterDropdownPeopleSearchSelect'; -export const opportunitiesFilters: FilterDefinitionByEntity[] = +export const opportunityBoardFilterDefinitions: FilterDefinitionByEntity[] = [ { - key: 'amount', + fieldId: 'amount', label: 'Amount', Icon: IconCurrencyDollar, type: 'number', }, { - key: 'closeDate', + fieldId: 'closeDate', label: 'Close date', Icon: IconCalendarEvent, type: 'date', }, { - key: 'companyId', + fieldId: 'companyId', label: 'Company', Icon: IconBuildingSkyscraper, type: 'entity', entitySelectComponent: , }, { - key: 'pointOfContactId', + fieldId: 'pointOfContactId', label: 'Point of contact', Icon: IconUser, type: 'entity', diff --git a/front/src/pages/opportunities/opportunities-sorts.tsx b/front/src/pages/opportunities/constants/opportunityBoardSortDefinitions.tsx similarity index 71% rename from front/src/pages/opportunities/opportunities-sorts.tsx rename to front/src/pages/opportunities/constants/opportunityBoardSortDefinitions.tsx index 77180bb30..de707455e 100644 --- a/front/src/pages/opportunities/opportunities-sorts.tsx +++ b/front/src/pages/opportunities/constants/opportunityBoardSortDefinitions.tsx @@ -1,19 +1,19 @@ import { SortDefinition } from '@/ui/data/sort/types/SortDefinition'; import { IconCalendarEvent, IconCurrencyDollar } from '@/ui/display/icon/index'; -export const opportunitiesSorts: SortDefinition[] = [ +export const opportunityBoardSortDefinitions: SortDefinition[] = [ { - key: 'createdAt', + fieldId: 'createdAt', label: 'Creation', Icon: IconCalendarEvent, }, { - key: 'amount', + fieldId: 'amount', label: 'Amount', Icon: IconCurrencyDollar, }, { - key: 'closeDate', + fieldId: 'closeDate', label: 'Expected close date', Icon: IconCalendarEvent, }, diff --git a/front/src/pages/opportunities/opportunitiesBoardOptions.tsx b/front/src/pages/opportunities/opportunitiesBoardOptions.tsx index 48617f5e9..1e1f00813 100644 --- a/front/src/pages/opportunities/opportunitiesBoardOptions.tsx +++ b/front/src/pages/opportunities/opportunitiesBoardOptions.tsx @@ -2,12 +2,12 @@ import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard'; import { NewCompanyProgressButton } from '@/companies/components/NewCompanyProgressButton'; import { BoardOptions } from '@/ui/layout/board/types/BoardOptions'; -import { opportunitiesFilters } from './opportunities-filters'; -import { opportunitiesSorts } from './opportunities-sorts'; +import { opportunityBoardFilterDefinitions } from './constants/opportunityBoardFilterDefinitions'; +import { opportunityBoardSortDefinitions } from './constants/opportunityBoardSortDefinitions'; export const opportunitiesBoardOptions: BoardOptions = { newCardComponent: , CardComponent: CompanyBoardCard, - filters: opportunitiesFilters, - sorts: opportunitiesSorts, + filterDefinitions: opportunityBoardFilterDefinitions, + sortDefinitions: opportunityBoardSortDefinitions, }; diff --git a/front/src/pages/people/People.tsx b/front/src/pages/people/People.tsx index 4145d29c2..53c511deb 100644 --- a/front/src/pages/people/People.tsx +++ b/front/src/pages/people/People.tsx @@ -2,7 +2,7 @@ import styled from '@emotion/styled'; import { v4 } from 'uuid'; import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect'; -import { PeopleTable } from '@/people/table/components/PeopleTable'; +import { PersonTable } from '@/people/table/components/PersonTable'; import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider'; import { DataTableActionBar } from '@/ui/data/data-table/action-bar/components/DataTableActionBar'; import { DataTableContextMenu } from '@/ui/data/data-table/context-menu/components/DataTableContextMenu'; @@ -62,7 +62,7 @@ export const People = () => { CustomRecoilScopeContext={TableRecoilScopeContext} > - + diff --git a/front/src/pages/people/PersonShow.tsx b/front/src/pages/people/PersonShow.tsx index 5ad4819c2..8c6ad3ab7 100644 --- a/front/src/pages/people/PersonShow.tsx +++ b/front/src/pages/people/PersonShow.tsx @@ -31,7 +31,7 @@ import { import { PeopleFullNameEditableField } from '../../modules/people/editable-field/components/PeopleFullNameEditableField'; import { ShowPageContainer } from '../../modules/ui/layout/page/ShowPageContainer'; -import { personShowFieldDefinition } from './constants/personShowFieldDefinition'; +import { personShowFieldDefinition } from './constants/personShowFieldDefinitions'; export const PersonShow = () => { const personId = useParams().personId ?? ''; diff --git a/front/src/pages/people/constants/personShowFieldDefinition.tsx b/front/src/pages/people/constants/personShowFieldDefinitions.tsx similarity index 100% rename from front/src/pages/people/constants/personShowFieldDefinition.tsx rename to front/src/pages/people/constants/personShowFieldDefinitions.tsx diff --git a/front/src/pages/people/constants/personTableFilterDefinitions.tsx b/front/src/pages/people/constants/personTableFilterDefinitions.tsx new file mode 100644 index 000000000..804738068 --- /dev/null +++ b/front/src/pages/people/constants/personTableFilterDefinitions.tsx @@ -0,0 +1,59 @@ +import { FilterDropdownCompanySearchSelect } from '@/companies/components/FilterDropdownCompanySearchSelect'; +import { FilterDefinitionByEntity } from '@/ui/data/filter/types/FilterDefinitionByEntity'; +import { + IconBuildingSkyscraper, + IconCalendarEvent, + IconMail, + IconMap, + IconPhone, + IconUser, +} from '@/ui/display/icon/index'; +import { Person } from '~/generated/graphql'; + +export const personTableFilterDefinitions: FilterDefinitionByEntity[] = + [ + { + fieldId: 'firstName', + label: 'First name', + Icon: IconUser, + type: 'text', + }, + { + fieldId: 'lastName', + label: 'Last name', + Icon: IconUser, + type: 'text', + }, + { + fieldId: 'email', + label: 'Email', + Icon: IconMail, + type: 'text', + }, + { + fieldId: 'companyId', + label: 'Company', + Icon: IconBuildingSkyscraper, + type: 'entity', + // TODO: replace this with a component that selects the dropdown to use based on the entity type + entitySelectComponent: , + }, + { + fieldId: 'phone', + label: 'Phone', + Icon: IconPhone, + type: 'text', + }, + { + fieldId: 'createdAt', + label: 'Created at', + Icon: IconCalendarEvent, + type: 'date', + }, + { + fieldId: 'city', + label: 'City', + Icon: IconMap, + type: 'text', + }, + ]; diff --git a/front/src/pages/people/people-sorts.tsx b/front/src/pages/people/constants/personTableSortDefinitions.tsx similarity index 80% rename from front/src/pages/people/people-sorts.tsx rename to front/src/pages/people/constants/personTableSortDefinitions.tsx index 0f7b9bf71..3d86ee4b4 100644 --- a/front/src/pages/people/people-sorts.tsx +++ b/front/src/pages/people/constants/personTableSortDefinitions.tsx @@ -9,9 +9,9 @@ import { IconUser, } from '@/ui/display/icon/index'; -export const peopleAvailableSorts: SortDefinition[] = [ +export const personTableSortDefinitions: SortDefinition[] = [ { - key: 'fullname', + fieldId: 'fullname', label: 'People', Icon: IconUser, @@ -21,7 +21,7 @@ export const peopleAvailableSorts: SortDefinition[] = [ ], }, { - key: 'company_name', + fieldId: 'company_name', label: 'Company', Icon: IconBuildingSkyscraper, getOrderByTemplate: (direction: SortDirection) => [ @@ -29,22 +29,22 @@ export const peopleAvailableSorts: SortDefinition[] = [ ], }, { - key: 'email', + fieldId: 'email', label: 'Email', Icon: IconMail, }, { - key: 'phone', + fieldId: 'phone', label: 'Phone', Icon: IconPhone, }, { - key: 'createdAt', + fieldId: 'createdAt', label: 'Created at', Icon: IconCalendarEvent, }, { - key: 'city', + fieldId: 'city', label: 'City', Icon: IconMap, }, diff --git a/front/src/pages/people/people-filters.tsx b/front/src/pages/people/people-filters.tsx deleted file mode 100644 index 82cf6e20c..000000000 --- a/front/src/pages/people/people-filters.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { FilterDropdownCompanySearchSelect } from '@/companies/components/FilterDropdownCompanySearchSelect'; -import { FilterDefinitionByEntity } from '@/ui/data/filter/types/FilterDefinitionByEntity'; -import { - IconBuildingSkyscraper, - IconCalendarEvent, - IconMail, - IconMap, - IconPhone, - IconUser, -} from '@/ui/display/icon/index'; -import { Person } from '~/generated/graphql'; - -export const peopleAvailableFilters: FilterDefinitionByEntity[] = [ - { - key: 'firstName', - label: 'First name', - Icon: IconUser, - type: 'text', - }, - { - key: 'lastName', - label: 'Last name', - Icon: IconUser, - type: 'text', - }, - { - key: 'email', - label: 'Email', - Icon: IconMail, - type: 'text', - }, - { - key: 'companyId', - label: 'Company', - Icon: IconBuildingSkyscraper, - type: 'entity', - // TODO: replace this with a component that selects the dropdown to use based on the entity type - entitySelectComponent: , - }, - { - key: 'phone', - label: 'Phone', - Icon: IconPhone, - type: 'text', - }, - { - key: 'createdAt', - label: 'Created at', - Icon: IconCalendarEvent, - type: 'date', - }, - { - key: 'city', - label: 'City', - Icon: IconMap, - type: 'text', - }, -]; diff --git a/front/src/pages/tasks/Tasks.tsx b/front/src/pages/tasks/Tasks.tsx index 32ed40e19..ab13a165b 100644 --- a/front/src/pages/tasks/Tasks.tsx +++ b/front/src/pages/tasks/Tasks.tsx @@ -4,6 +4,7 @@ import { TasksRecoilScopeContext } from '@/activities/states/recoil-scope-contex import { PageAddTaskButton } from '@/activities/tasks/components/PageAddTaskButton'; import { TaskGroups } from '@/activities/tasks/components/TaskGroups'; import { FilterDropdownButton } from '@/ui/data/filter/components/FilterDropdownButton'; +import { FilterScope } from '@/ui/data/filter/scopes/FilterScope'; import { IconArchive, IconCheck, IconCheckbox } from '@/ui/display/icon/index'; import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; import { PageBody } from '@/ui/layout/page/PageBody'; @@ -47,30 +48,35 @@ export const Tasks = () => { return ( - - - - - - - - - - } - rightComponent={ - - } - /> - - - + + + + + + + + + + + } + rightComponent={ + + } + /> + + + + ); diff --git a/front/src/pages/tasks/TasksEffect.tsx b/front/src/pages/tasks/TasksEffect.tsx index 0068339b7..039edba23 100644 --- a/front/src/pages/tasks/TasksEffect.tsx +++ b/front/src/pages/tasks/TasksEffect.tsx @@ -5,29 +5,27 @@ import { currentUserState } from '@/auth/states/currentUserState'; import { useFilter } from '@/ui/data/filter/hooks/useFilter'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; -import { tasksFilters } from './tasks-filters'; +import { tasksFilterDefinitions } from './tasks-filter-definitions'; export const TasksEffect = () => { const [currentUser] = useRecoilState(currentUserState); - const { setSelectedFilters, setAvailableFilters } = useFilter(); + const { setSelectedFilter, setAvailableFilterDefinitions } = useFilter(); useEffect(() => { - setAvailableFilters(tasksFilters); - }, [setAvailableFilters]); + setAvailableFilterDefinitions(tasksFilterDefinitions); + }, [setAvailableFilterDefinitions]); useEffect(() => { if (currentUser) { - setSelectedFilters([ - { - key: 'assigneeId', - type: 'entity', - value: currentUser.id, - operand: ViewFilterOperand.Is, - displayValue: currentUser.displayName, - displayAvatarUrl: currentUser.avatarUrl ?? undefined, - }, - ]); + setSelectedFilter({ + fieldId: 'assigneeId', + value: currentUser.id, + operand: ViewFilterOperand.Is, + displayValue: currentUser.displayName, + displayAvatarUrl: currentUser.avatarUrl ?? undefined, + definition: tasksFilterDefinitions[0], + }); } - }, [currentUser, setSelectedFilters]); + }, [currentUser, setSelectedFilter]); return <>; }; diff --git a/front/src/pages/tasks/tasks-filters.tsx b/front/src/pages/tasks/tasks-filter-definitions.tsx similarity index 83% rename from front/src/pages/tasks/tasks-filters.tsx rename to front/src/pages/tasks/tasks-filter-definitions.tsx index b4f6595e8..974019407 100644 --- a/front/src/pages/tasks/tasks-filters.tsx +++ b/front/src/pages/tasks/tasks-filter-definitions.tsx @@ -3,9 +3,9 @@ import { IconUser, IconUserCircle } from '@/ui/display/icon'; import { FilterDropdownUserSearchSelect } from '@/users/components/FilterDropdownUserSearchSelect'; import { Activity } from '~/generated/graphql'; -export const tasksFilters: FilterDefinitionByEntity[] = [ +export const tasksFilterDefinitions: FilterDefinitionByEntity[] = [ { - key: 'assigneeId', + fieldId: 'assigneeId', label: 'Assignee', Icon: IconUser, type: 'entity', diff --git a/server/src/metadata/tenant-initialisation/standard-objects/companies/companies.metadata.json b/server/src/metadata/tenant-initialisation/standard-objects/companies/companies.metadata.json index 2678b6d7d..7fe4923e4 100644 --- a/server/src/metadata/tenant-initialisation/standard-objects/companies/companies.metadata.json +++ b/server/src/metadata/tenant-initialisation/standard-objects/companies/companies.metadata.json @@ -6,7 +6,7 @@ "labelPlural": "Companies", "targetTableName": "company", "description": "A company", - "icon": "business", + "icon": "IconBuildingSkyscraper", "fields": [ { "type": "text", @@ -16,7 +16,7 @@ "value": "name" }, "description": "Name of the company", - "icon": null, + "icon": "IconBuildingSkyscraper", "isNullable": false }, { @@ -27,7 +27,7 @@ "value": "domainName" }, "description": "Domain name of the company", - "icon": "url", + "icon": "IconLink", "isNullable": true }, { @@ -38,7 +38,7 @@ "value": "address" }, "description": "Address of the company", - "icon": "location", + "icon": "IconMap", "isNullable": true }, { @@ -49,7 +49,7 @@ "value": "employees" }, "description": "Number of employees", - "icon": "people", + "icon": "IconUsers", "isNullable": true } ] diff --git a/server/src/metadata/tenant-initialisation/standard-objects/people/people.metadata.json b/server/src/metadata/tenant-initialisation/standard-objects/people/people.metadata.json deleted file mode 100644 index 9d4000349..000000000 --- a/server/src/metadata/tenant-initialisation/standard-objects/people/people.metadata.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "nameSingular": "personV2", - "namePlural": "peopleV2", - "labelSingular": "Person", - "labelPlural": "People", - "targetTableName": "person", - "description": "A person", - "icon": "people", - "fields": [ - { - "type": "text", - "name": "firstName", - "label": "First Name", - "targetColumnMap": { - "value": "firstName" - }, - "description": "First Name of the person", - "icon": null, - "isNullable": true - }, - { - "type": "text", - "name": "lastName", - "label": "Last Name", - "targetColumnMap": { - "value": "lastName" - }, - "description": "Last Name of the person", - "icon": null, - "isNullable": true - }, - { - "type": "text", - "name": "email", - "label": "Email", - "targetColumnMap": { - "value": "email" - }, - "description": "Email of the person", - "icon": null, - "isNullable": true - }, - { - "type": "phone", - "name": "phone", - "label": "Phone", - "targetColumnMap": { - "value": "phone" - }, - "description": "phone of the company", - "icon": null, - "isNullable": true - }, - { - "type": "text", - "name": "city", - "label": "City", - "targetColumnMap": { - "value": "city" - }, - "description": "City of the person", - "icon": null, - "isNullable": true - }, - { - "type": "text", - "name": "jobTitle", - "label": "Job Title", - "targetColumnMap": { - "value": "jobTitle" - }, - "description": "Job title of the person", - "icon": null, - "isNullable": true - }, - { - "type": "url", - "name": "linkedinUrl", - "label": "Linkedin URL", - "targetColumnMap": { - "text": "Linkedin URL", - "link": "linkedinUrl" - }, - "description": "Linkedin URL of the person", - "icon": "url", - "isNullable": true - }, - { - "type": "url", - "name": "xUrl", - "label": "X URL", - "targetColumnMap": { - "text": "X URL", - "link": "xUrl" - }, - "description": "X URL of the person", - "icon": "url", - "isNullable": true - }, - { - "type": "url", - "name": "avatarUrl", - "label": "Avatar URL", - "targetColumnMap": { - "text": "Avatar URL", - "link": "avatarUrl" - }, - "description": "Avatar URL of the person", - "icon": "url", - "isNullable": true - } - ] -} \ No newline at end of file diff --git a/server/src/metadata/tenant-initialisation/standard-objects/standard-object-metadata.ts b/server/src/metadata/tenant-initialisation/standard-objects/standard-object-metadata.ts index 06d6ec446..0ec54d2ba 100644 --- a/server/src/metadata/tenant-initialisation/standard-objects/standard-object-metadata.ts +++ b/server/src/metadata/tenant-initialisation/standard-objects/standard-object-metadata.ts @@ -1,11 +1,13 @@ import companyObject from './companies/companies.metadata.json'; -import personObject from './people/people.metadata.json'; import viewObject from './views/views.metadata.json'; import viewFieldObject from './view-fields/view-fields.metadata.json'; +import viewFilterObject from './view-filters/view-filters.metadata.json'; +import viewSortObject from './view-sorts/view-sorts.metadata.json'; export const standardObjectsMetadata = { companyV2: companyObject, - personV2: personObject, viewV2: viewObject, viewFieldV2: viewFieldObject, + viewFilterV2: viewFilterObject, + viewSortV2: viewSortObject, }; diff --git a/server/src/metadata/tenant-initialisation/standard-objects/view-fields/view-fields.metadata.json b/server/src/metadata/tenant-initialisation/standard-objects/view-fields/view-fields.metadata.json index 198d91bb9..ddbe9cb83 100644 --- a/server/src/metadata/tenant-initialisation/standard-objects/view-fields/view-fields.metadata.json +++ b/server/src/metadata/tenant-initialisation/standard-objects/view-fields/view-fields.metadata.json @@ -5,7 +5,7 @@ "labelPlural": "View Fields", "targetTableName": "viewField", "description": "(System) View Fields", - "icon": "arrows-sort", + "icon": "IconColumns3", "fields": [ { "type": "text", diff --git a/server/src/metadata/tenant-initialisation/standard-objects/view-filters/view-filters.metadata.json b/server/src/metadata/tenant-initialisation/standard-objects/view-filters/view-filters.metadata.json new file mode 100644 index 000000000..6bbd67bec --- /dev/null +++ b/server/src/metadata/tenant-initialisation/standard-objects/view-filters/view-filters.metadata.json @@ -0,0 +1,66 @@ +{ + "nameSingular": "viewFilterV2", + "namePlural": "viewFiltersV2", + "labelSingular": "View Filter", + "labelPlural": "View Filters", + "targetTableName": "viewFilter", + "description": "(System) View Filters", + "icon": "IconFilterBolt", + "fields": [ + { + "type": "text", + "name": "fieldId", + "label": "Field Id", + "targetColumnMap": { + "value": "fieldId" + }, + "description": "View Filter target field", + "icon": null, + "isNullable": false + }, + { + "type": "text", + "name": "viewId", + "label": "View Id", + "targetColumnMap": { + "value": "viewId" + }, + "description": "View Filter related view", + "icon": null, + "isNullable": false + }, + { + "type": "text", + "name": "operand", + "label": "Operand", + "targetColumnMap": { + "value": "operand" + }, + "description": "View Filter operand", + "icon": null, + "isNullable": false + }, + { + "type": "text", + "name": "value", + "label": "Value", + "targetColumnMap": { + "value": "value" + }, + "description": "View Filter value", + "icon": null, + "isNullable": false + }, + { + "type": "text", + "name": "displayValue", + "label": "Display Value", + "targetColumnMap": { + "value": "displayValue" + }, + "description": "View Filter Display Value", + "icon": null, + "isNullable": false + } + ] +} \ No newline at end of file diff --git a/server/src/metadata/tenant-initialisation/standard-objects/view-sorts/view-sorts.metadata.json b/server/src/metadata/tenant-initialisation/standard-objects/view-sorts/view-sorts.metadata.json new file mode 100644 index 000000000..f1bf05a6a --- /dev/null +++ b/server/src/metadata/tenant-initialisation/standard-objects/view-sorts/view-sorts.metadata.json @@ -0,0 +1,44 @@ +{ + "nameSingular": "viewSortV2", + "namePlural": "viewSortsV2", + "labelSingular": "View Sort", + "labelPlural": "View Sorts", + "targetTableName": "viewSort", + "description": "(System) View Sorts", + "icon": "IconArrowsSort", + "fields": [ + { + "type": "text", + "name": "fieldId", + "label": "Field Id", + "targetColumnMap": { + "value": "fieldId" + }, + "description": "View Sort target field", + "icon": null, + "isNullable": false + }, + { + "type": "text", + "name": "viewId", + "label": "View Id", + "targetColumnMap": { + "value": "viewId" + }, + "description": "View Sort related view", + "icon": null, + "isNullable": false + }, + { + "type": "text", + "name": "direction", + "label": "Direction", + "targetColumnMap": { + "value": "direction" + }, + "description": "View Sort direction", + "icon": null, + "isNullable": false + } + ] +} \ No newline at end of file diff --git a/server/src/metadata/tenant-initialisation/standard-objects/views/views.metadata.json b/server/src/metadata/tenant-initialisation/standard-objects/views/views.metadata.json index 57c9773be..4f8c059a2 100644 --- a/server/src/metadata/tenant-initialisation/standard-objects/views/views.metadata.json +++ b/server/src/metadata/tenant-initialisation/standard-objects/views/views.metadata.json @@ -5,7 +5,7 @@ "labelPlural": "Views", "targetTableName": "view", "description": "(System) Views", - "icon": "layout-collage", + "icon": "IconLayoutCollage", "fields": [ { "type": "text", diff --git a/server/src/metadata/tenant-migration/migrations/1697618010-addPeopleTable.ts b/server/src/metadata/tenant-migration/migrations/1697618010-addPeopleTable.ts deleted file mode 100644 index 78745a780..000000000 --- a/server/src/metadata/tenant-migration/migrations/1697618010-addPeopleTable.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { TenantMigrationTableAction } from 'src/metadata/tenant-migration/tenant-migration.entity'; - -export const addPeopleTable: TenantMigrationTableAction[] = [ - { - name: 'people', - action: 'create', - }, - { - name: 'people', - action: 'alter', - columns: [ - { - name: 'firstName', - type: 'varchar', - action: 'create', - }, - { - name: 'lastName', - type: 'varchar', - action: 'create', - }, - { - name: 'email', - type: 'varchar', - action: 'create', - }, - { - name: 'phone', - type: 'varchar', - action: 'create', - }, - { - name: 'city', - type: 'varchar', - action: 'create', - }, - { - name: 'jobTitle', - type: 'varchar', - action: 'create', - }, - { - name: 'linkedinUrl', - type: 'varchar', - action: 'create', - }, - { - name: 'xUrl', - type: 'varchar', - action: 'create', - }, - { - name: 'avatarUrl', - type: 'varchar', - action: 'create', - }, - ], - }, -]; diff --git a/server/src/metadata/tenant-migration/migrations/1697618013-addViewFilterTable.ts b/server/src/metadata/tenant-migration/migrations/1697618013-addViewFilterTable.ts new file mode 100644 index 000000000..95a298b73 --- /dev/null +++ b/server/src/metadata/tenant-migration/migrations/1697618013-addViewFilterTable.ts @@ -0,0 +1,39 @@ +import { TenantMigrationTableAction } from 'src/metadata/tenant-migration/tenant-migration.entity'; + +export const addViewFilterTable: TenantMigrationTableAction[] = [ + { + name: 'viewFilter', + action: 'create', + }, + { + name: 'viewFilter', + action: 'alter', + columns: [ + { + name: 'fieldId', + type: 'varchar', + action: 'create', + }, + { + name: 'viewId', + type: 'varchar', + action: 'create', + }, + { + name: 'operand', + type: 'varchar', + action: 'create', + }, + { + name: 'value', + type: 'varchar', + action: 'create', + }, + { + name: 'displayValue', + type: 'varchar', + action: 'create', + }, + ], + }, +]; diff --git a/server/src/metadata/tenant-migration/migrations/1697618014-addViewSortTable.ts b/server/src/metadata/tenant-migration/migrations/1697618014-addViewSortTable.ts new file mode 100644 index 000000000..89e0a4a42 --- /dev/null +++ b/server/src/metadata/tenant-migration/migrations/1697618014-addViewSortTable.ts @@ -0,0 +1,29 @@ +import { TenantMigrationTableAction } from 'src/metadata/tenant-migration/tenant-migration.entity'; + +export const addViewSortTable: TenantMigrationTableAction[] = [ + { + name: 'viewSort', + action: 'create', + }, + { + name: 'viewSort', + action: 'alter', + columns: [ + { + name: 'fieldId', + type: 'varchar', + action: 'create', + }, + { + name: 'viewId', + type: 'varchar', + action: 'create', + }, + { + name: 'direction', + type: 'varchar', + action: 'create', + }, + ], + }, +]; diff --git a/server/src/metadata/tenant-migration/standard-migrations.ts b/server/src/metadata/tenant-migration/standard-migrations.ts index a96c7aca8..054631511 100644 --- a/server/src/metadata/tenant-migration/standard-migrations.ts +++ b/server/src/metadata/tenant-migration/standard-migrations.ts @@ -1,12 +1,14 @@ import { addCompanyTable } from './migrations/1697618009-addCompanyTable'; -import { addPeopleTable } from './migrations/1697618010-addPeopleTable'; import { addViewTable } from './migrations/1697618011-addViewTable'; import { addViewFieldTable } from './migrations/1697618012-addViewFieldTable'; +import { addViewFilterTable } from './migrations/1697618013-addViewFilterTable'; +import { addViewSortTable } from './migrations/1697618014-addViewSortTable'; // TODO: read the folder and return all migrations export const standardMigrations = { '1697618009-addCompanyTable': addCompanyTable, - '1697618010-addPeopleTable': addPeopleTable, '1697618011-addViewTable': addViewTable, '1697618012-addViewFieldTable': addViewFieldTable, + '1697618013-addViewFilterTable': addViewFilterTable, + '1697618014-addViewSortTable': addViewSortTable, };