feat: toggle board field visibilities (#1547)
Closes #1537, Closes #1539
This commit is contained in:
@ -1,11 +1,16 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { Meta, StoryObj } from '@storybook/react';
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
|
import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
|
||||||
|
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
|
||||||
import { BoardCardIdContext } from '@/ui/board/contexts/BoardCardIdContext';
|
import { BoardCardIdContext } from '@/ui/board/contexts/BoardCardIdContext';
|
||||||
|
import { boardCardFieldsScopedState } from '@/ui/board/states/boardCardFieldsScopedState';
|
||||||
import { BoardColumnRecoilScopeContext } from '@/ui/board/states/recoil-scope-contexts/BoardColumnRecoilScopeContext';
|
import { BoardColumnRecoilScopeContext } from '@/ui/board/states/recoil-scope-contexts/BoardColumnRecoilScopeContext';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||||
|
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
import { mockedPipelineProgressData } from '~/testing/mock-data/pipeline-progress';
|
import { mockedPipelineProgressData } from '~/testing/mock-data/pipeline-progress';
|
||||||
|
|
||||||
@ -16,26 +21,43 @@ const meta: Meta<typeof CompanyBoardCard> = {
|
|||||||
title: 'Modules/Companies/CompanyBoardCard',
|
title: 'Modules/Companies/CompanyBoardCard',
|
||||||
component: CompanyBoardCard,
|
component: CompanyBoardCard,
|
||||||
decorators: [
|
decorators: [
|
||||||
(Story) => (
|
(Story, context) => {
|
||||||
<RecoilScope SpecificContext={CompanyBoardRecoilScopeContext}>
|
const [, setBoardCardFields] = useRecoilScopedState(
|
||||||
<HooksCompanyBoard />
|
boardCardFieldsScopedState,
|
||||||
<RecoilScope SpecificContext={BoardColumnRecoilScopeContext}>
|
context.parameters.recoilScopeContext,
|
||||||
<BoardCardIdContext.Provider value={mockedPipelineProgressData[1].id}>
|
);
|
||||||
<MemoryRouter>
|
|
||||||
<Story />
|
useEffect(() => {
|
||||||
</MemoryRouter>
|
setBoardCardFields(pipelineAvailableFieldDefinitions);
|
||||||
</BoardCardIdContext.Provider>
|
}, [setBoardCardFields]);
|
||||||
</RecoilScope>
|
|
||||||
</RecoilScope>
|
return (
|
||||||
),
|
<>
|
||||||
|
<HooksCompanyBoard />
|
||||||
|
<RecoilScope SpecificContext={BoardColumnRecoilScopeContext}>
|
||||||
|
<BoardCardIdContext.Provider
|
||||||
|
value={mockedPipelineProgressData[1].id}
|
||||||
|
>
|
||||||
|
<MemoryRouter>
|
||||||
|
<Story />
|
||||||
|
</MemoryRouter>
|
||||||
|
</BoardCardIdContext.Provider>
|
||||||
|
</RecoilScope>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
ComponentWithRecoilScopeDecorator,
|
||||||
ComponentDecorator,
|
ComponentDecorator,
|
||||||
],
|
],
|
||||||
|
args: { scopeContext: CompanyBoardRecoilScopeContext },
|
||||||
|
argTypes: { scopeContext: { control: false } },
|
||||||
parameters: {
|
parameters: {
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
recoilScopeContext: CompanyBoardRecoilScopeContext,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof CompanyBoardCard>;
|
type Story = StoryObj<typeof CompanyBoardCard>;
|
||||||
|
|
||||||
export const CompanyCompanyBoardCard: Story = {};
|
export const Default: Story = {};
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
|
||||||
import {
|
import {
|
||||||
EntityBoard,
|
EntityBoard,
|
||||||
type EntityBoardProps,
|
type EntityBoardProps,
|
||||||
@ -21,6 +22,7 @@ export const CompanyBoard = ({ ...props }: CompanyBoardProps) => {
|
|||||||
availableSorts: opportunitiesBoardOptions.sorts,
|
availableSorts: opportunitiesBoardOptions.sorts,
|
||||||
objectId: 'company',
|
objectId: 'company',
|
||||||
scopeContext: CompanyBoardRecoilScopeContext,
|
scopeContext: CompanyBoardRecoilScopeContext,
|
||||||
|
fieldDefinitions: pipelineAvailableFieldDefinitions,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
import { ReactNode, useContext } from 'react';
|
import { type Context, type ReactNode, useContext } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { BoardCardIdContext } from '@/ui/board/contexts/BoardCardIdContext';
|
import { BoardCardIdContext } from '@/ui/board/contexts/BoardCardIdContext';
|
||||||
import { useCurrentCardSelected } from '@/ui/board/hooks/useCurrentCardSelected';
|
import { useCurrentCardSelected } from '@/ui/board/hooks/useCurrentCardSelected';
|
||||||
import { viewFieldsDefinitionsState } from '@/ui/board/states/viewFieldsDefinitionsState';
|
import { visibleBoardCardFieldsScopedSelector } from '@/ui/board/states/selectors/visibleBoardCardFieldsScopedSelector';
|
||||||
import { EntityChipVariant } from '@/ui/chip/components/EntityChip';
|
import { EntityChipVariant } from '@/ui/chip/components/EntityChip';
|
||||||
import { GenericEditableField } from '@/ui/editable-field/components/GenericEditableField';
|
import { GenericEditableField } from '@/ui/editable-field/components/GenericEditableField';
|
||||||
import { EditableFieldDefinitionContext } from '@/ui/editable-field/contexts/EditableFieldDefinitionContext';
|
import { EditableFieldDefinitionContext } from '@/ui/editable-field/contexts/EditableFieldDefinitionContext';
|
||||||
import { EditableFieldEntityIdContext } from '@/ui/editable-field/contexts/EditableFieldEntityIdContext';
|
import { EditableFieldEntityIdContext } from '@/ui/editable-field/contexts/EditableFieldEntityIdContext';
|
||||||
import { EditableFieldMutationContext } from '@/ui/editable-field/contexts/EditableFieldMutationContext';
|
import { EditableFieldMutationContext } from '@/ui/editable-field/contexts/EditableFieldMutationContext';
|
||||||
import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
|
import { Checkbox, CheckboxVariant } from '@/ui/input/components/Checkbox';
|
||||||
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql';
|
import { useUpdateOnePipelineProgressMutation } from '~/generated/graphql';
|
||||||
import { getLogoUrlFromDomainName } from '~/utils';
|
import { getLogoUrlFromDomainName } from '~/utils';
|
||||||
|
|
||||||
@ -18,6 +19,10 @@ import { companyProgressesFamilyState } from '../states/companyProgressesFamilyS
|
|||||||
|
|
||||||
import { CompanyChip } from './CompanyChip';
|
import { CompanyChip } from './CompanyChip';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
scopeContext: Context<string | null>;
|
||||||
|
};
|
||||||
|
|
||||||
const StyledBoardCard = styled.div<{ selected: boolean }>`
|
const StyledBoardCard = styled.div<{ selected: boolean }>`
|
||||||
background-color: ${({ theme, selected }) =>
|
background-color: ${({ theme, selected }) =>
|
||||||
selected ? theme.accent.quaternary : theme.background.secondary};
|
selected ? theme.accent.quaternary : theme.background.secondary};
|
||||||
@ -98,7 +103,7 @@ const StyledFieldContainer = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function CompanyBoardCard() {
|
export function CompanyBoardCard({ scopeContext }: OwnProps) {
|
||||||
const { currentCardSelected, setCurrentCardSelected } =
|
const { currentCardSelected, setCurrentCardSelected } =
|
||||||
useCurrentCardSelected();
|
useCurrentCardSelected();
|
||||||
const boardCardId = useContext(BoardCardIdContext);
|
const boardCardId = useContext(BoardCardIdContext);
|
||||||
@ -108,7 +113,10 @@ export function CompanyBoardCard() {
|
|||||||
);
|
);
|
||||||
const { pipelineProgress, company } = companyProgress ?? {};
|
const { pipelineProgress, company } = companyProgress ?? {};
|
||||||
|
|
||||||
const viewFieldsDefinitions = useRecoilValue(viewFieldsDefinitionsState);
|
const visibleBoardCardFields = useRecoilScopedValue(
|
||||||
|
visibleBoardCardFieldsScopedSelector,
|
||||||
|
scopeContext,
|
||||||
|
);
|
||||||
|
|
||||||
// boardCardId check can be moved to a wrapper to avoid unnecessary logic above
|
// boardCardId check can be moved to a wrapper to avoid unnecessary logic above
|
||||||
if (!company || !pipelineProgress || !boardCardId) {
|
if (!company || !pipelineProgress || !boardCardId) {
|
||||||
@ -157,23 +165,21 @@ export function CompanyBoardCard() {
|
|||||||
value={useUpdateOnePipelineProgressMutation}
|
value={useUpdateOnePipelineProgressMutation}
|
||||||
>
|
>
|
||||||
<EditableFieldEntityIdContext.Provider value={boardCardId}>
|
<EditableFieldEntityIdContext.Provider value={boardCardId}>
|
||||||
{viewFieldsDefinitions.map((viewField) => {
|
{visibleBoardCardFields.map((viewField) => (
|
||||||
return (
|
<PreventSelectOnClickContainer key={viewField.key}>
|
||||||
<PreventSelectOnClickContainer key={viewField.key}>
|
<EditableFieldDefinitionContext.Provider
|
||||||
<EditableFieldDefinitionContext.Provider
|
value={{
|
||||||
value={{
|
key: viewField.key,
|
||||||
key: viewField.key,
|
name: viewField.name,
|
||||||
name: viewField.name,
|
Icon: viewField.Icon,
|
||||||
Icon: viewField.Icon,
|
type: viewField.metadata.type,
|
||||||
type: viewField.metadata.type,
|
metadata: viewField.metadata,
|
||||||
metadata: viewField.metadata,
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<GenericEditableField />
|
||||||
<GenericEditableField />
|
</EditableFieldDefinitionContext.Provider>
|
||||||
</EditableFieldDefinitionContext.Provider>
|
</PreventSelectOnClickContainer>
|
||||||
</PreventSelectOnClickContainer>
|
))}
|
||||||
);
|
|
||||||
})}
|
|
||||||
</EditableFieldEntityIdContext.Provider>
|
</EditableFieldEntityIdContext.Provider>
|
||||||
</EditableFieldMutationContext.Provider>
|
</EditableFieldMutationContext.Provider>
|
||||||
</StyledBoardCardBody>
|
</StyledBoardCardBody>
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
|
|
||||||
import { useBoardActionBarEntries } from '@/ui/board/hooks/useBoardActionBarEntries';
|
import { useBoardActionBarEntries } from '@/ui/board/hooks/useBoardActionBarEntries';
|
||||||
import { useBoardContextMenuEntries } from '@/ui/board/hooks/useBoardContextMenuEntries';
|
import { useBoardContextMenuEntries } from '@/ui/board/hooks/useBoardContextMenuEntries';
|
||||||
import { isBoardLoadedState } from '@/ui/board/states/isBoardLoadedState';
|
import { isBoardLoadedState } from '@/ui/board/states/isBoardLoadedState';
|
||||||
import { viewFieldsDefinitionsState } from '@/ui/board/states/viewFieldsDefinitionsState';
|
|
||||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import { availableFiltersScopedState } from '@/ui/view-bar/states/availableFiltersScopedState';
|
import { availableFiltersScopedState } from '@/ui/view-bar/states/availableFiltersScopedState';
|
||||||
@ -26,9 +24,6 @@ import { useUpdateCompanyBoard } from '../hooks/useUpdateCompanyBoardColumns';
|
|||||||
import { CompanyBoardRecoilScopeContext } from '../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
|
import { CompanyBoardRecoilScopeContext } from '../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
|
||||||
|
|
||||||
export function HooksCompanyBoard() {
|
export function HooksCompanyBoard() {
|
||||||
const setFieldsDefinitionsState = useSetRecoilState(
|
|
||||||
viewFieldsDefinitionsState,
|
|
||||||
);
|
|
||||||
const [, setAvailableFilters] = useRecoilScopedState(
|
const [, setAvailableFilters] = useRecoilScopedState(
|
||||||
availableFiltersScopedState,
|
availableFiltersScopedState,
|
||||||
CompanyBoardRecoilScopeContext,
|
CompanyBoardRecoilScopeContext,
|
||||||
@ -36,7 +31,6 @@ export function HooksCompanyBoard() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAvailableFilters(opportunitiesBoardOptions.filters);
|
setAvailableFilters(opportunitiesBoardOptions.filters);
|
||||||
setFieldsDefinitionsState(pipelineAvailableFieldDefinitions);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState);
|
const [, setIsBoardLoaded] = useRecoilState(isBoardLoadedState);
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export const pipelineAvailableFieldDefinitions: ViewFieldDefinition<ViewFieldMet
|
|||||||
key: 'closeDate',
|
key: 'closeDate',
|
||||||
name: 'Close Date',
|
name: 'Close Date',
|
||||||
Icon: IconCalendarEvent,
|
Icon: IconCalendarEvent,
|
||||||
|
index: 0,
|
||||||
metadata: {
|
metadata: {
|
||||||
type: 'date',
|
type: 'date',
|
||||||
fieldName: 'closeDate',
|
fieldName: 'closeDate',
|
||||||
@ -30,6 +31,7 @@ export const pipelineAvailableFieldDefinitions: ViewFieldDefinition<ViewFieldMet
|
|||||||
key: 'amount',
|
key: 'amount',
|
||||||
name: 'Amount',
|
name: 'Amount',
|
||||||
Icon: IconCurrencyDollar,
|
Icon: IconCurrencyDollar,
|
||||||
|
index: 1,
|
||||||
metadata: {
|
metadata: {
|
||||||
type: 'number',
|
type: 'number',
|
||||||
fieldName: 'amount',
|
fieldName: 'amount',
|
||||||
@ -40,6 +42,7 @@ export const pipelineAvailableFieldDefinitions: ViewFieldDefinition<ViewFieldMet
|
|||||||
key: 'probability',
|
key: 'probability',
|
||||||
name: 'Probability',
|
name: 'Probability',
|
||||||
Icon: IconProgressCheck,
|
Icon: IconProgressCheck,
|
||||||
|
index: 2,
|
||||||
metadata: {
|
metadata: {
|
||||||
type: 'probability',
|
type: 'probability',
|
||||||
fieldName: 'probability',
|
fieldName: 'probability',
|
||||||
@ -50,6 +53,7 @@ export const pipelineAvailableFieldDefinitions: ViewFieldDefinition<ViewFieldMet
|
|||||||
key: 'pointOfContact',
|
key: 'pointOfContact',
|
||||||
name: 'Point of Contact',
|
name: 'Point of Contact',
|
||||||
Icon: IconUser,
|
Icon: IconUser,
|
||||||
|
index: 3,
|
||||||
metadata: {
|
metadata: {
|
||||||
type: 'relation',
|
type: 'relation',
|
||||||
fieldName: 'pointOfContact',
|
fieldName: 'pointOfContact',
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { type ComponentProps, useCallback } from 'react';
|
import type { ComponentProps } from 'react';
|
||||||
|
|
||||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||||
@ -29,18 +29,6 @@ export function BoardHeader<SortField>({
|
|||||||
availableSorts,
|
availableSorts,
|
||||||
defaultViewName,
|
defaultViewName,
|
||||||
}: BoardHeaderProps<SortField>) {
|
}: BoardHeaderProps<SortField>) {
|
||||||
const OptionsDropdownButton = useCallback(
|
|
||||||
() => (
|
|
||||||
<BoardOptionsDropdown
|
|
||||||
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
|
||||||
onStageAdd={onStageAdd}
|
|
||||||
onViewsChange={onViewsChange}
|
|
||||||
scopeContext={scopeContext}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[onStageAdd, onViewsChange, scopeContext],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
|
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
|
||||||
<ViewBar
|
<ViewBar
|
||||||
@ -48,8 +36,15 @@ export function BoardHeader<SortField>({
|
|||||||
defaultViewName={defaultViewName}
|
defaultViewName={defaultViewName}
|
||||||
onViewsChange={onViewsChange}
|
onViewsChange={onViewsChange}
|
||||||
onViewSubmit={onViewSubmit}
|
onViewSubmit={onViewSubmit}
|
||||||
|
optionsDropdownButton={
|
||||||
|
<BoardOptionsDropdown
|
||||||
|
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
|
||||||
|
onStageAdd={onStageAdd}
|
||||||
|
onViewsChange={onViewsChange}
|
||||||
|
scopeContext={scopeContext}
|
||||||
|
/>
|
||||||
|
}
|
||||||
optionsDropdownKey={BoardOptionsDropdownKey}
|
optionsDropdownKey={BoardOptionsDropdownKey}
|
||||||
OptionsDropdownButton={OptionsDropdownButton}
|
|
||||||
scopeContext={scopeContext}
|
scopeContext={scopeContext}
|
||||||
/>
|
/>
|
||||||
</RecoilScope>
|
</RecoilScope>
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import {
|
|||||||
IconLayoutKanban,
|
IconLayoutKanban,
|
||||||
IconPlus,
|
IconPlus,
|
||||||
IconSettings,
|
IconSettings,
|
||||||
|
IconTag,
|
||||||
} from '@/ui/icon';
|
} from '@/ui/icon';
|
||||||
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||||
import { MenuItemNavigate } from '@/ui/menu-item/components/MenuItemNavigate';
|
import { MenuItemNavigate } from '@/ui/menu-item/components/MenuItemNavigate';
|
||||||
@ -23,12 +24,16 @@ import { ThemeColor } from '@/ui/theme/constants/colors';
|
|||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
import { ViewFieldsVisibilityDropdownSection } from '@/ui/view-bar/components/ViewFieldsVisibilityDropdownSection';
|
||||||
import { useUpsertView } from '@/ui/view-bar/hooks/useUpsertView';
|
import { useUpsertView } from '@/ui/view-bar/hooks/useUpsertView';
|
||||||
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
|
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||||
import type { View } from '@/ui/view-bar/types/View';
|
import type { View } from '@/ui/view-bar/types/View';
|
||||||
|
|
||||||
|
import { useBoardCardFields } from '../hooks/useBoardCardFields';
|
||||||
import { boardColumnsState } from '../states/boardColumnsState';
|
import { boardColumnsState } from '../states/boardColumnsState';
|
||||||
|
import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector';
|
||||||
|
import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector';
|
||||||
import type { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
import type { BoardColumnDefinition } from '../types/BoardColumnDefinition';
|
||||||
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
|
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
|
||||||
|
|
||||||
@ -43,10 +48,7 @@ const StyledIconSettings = styled(IconSettings)`
|
|||||||
margin-right: ${({ theme }) => theme.spacing(1)};
|
margin-right: ${({ theme }) => theme.spacing(1)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
enum BoardOptionsMenu {
|
type BoardOptionsMenu = 'fields' | 'stage-creation' | 'stages';
|
||||||
StageCreation = 'StageCreation',
|
|
||||||
Stages = 'Stages',
|
|
||||||
}
|
|
||||||
|
|
||||||
type ColumnForCreate = {
|
type ColumnForCreate = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -72,14 +74,22 @@ export function BoardOptionsDropdownContent({
|
|||||||
|
|
||||||
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
|
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
|
||||||
|
|
||||||
|
const hiddenBoardCardFields = useRecoilScopedValue(
|
||||||
|
hiddenBoardCardFieldsScopedSelector,
|
||||||
|
scopeContext,
|
||||||
|
);
|
||||||
|
const hasHiddenFields = hiddenBoardCardFields.length > 0;
|
||||||
|
const visibleBoardCardFields = useRecoilScopedValue(
|
||||||
|
visibleBoardCardFieldsScopedSelector,
|
||||||
|
scopeContext,
|
||||||
|
);
|
||||||
|
const hasVisibleFields = visibleBoardCardFields.length > 0;
|
||||||
|
|
||||||
const viewsById = useRecoilScopedValue(viewsByIdScopedSelector, scopeContext);
|
const viewsById = useRecoilScopedValue(viewsByIdScopedSelector, scopeContext);
|
||||||
const viewEditMode = useRecoilValue(viewEditModeState);
|
const viewEditMode = useRecoilValue(viewEditModeState);
|
||||||
|
|
||||||
const handleStageSubmit = () => {
|
const handleStageSubmit = () => {
|
||||||
if (
|
if (currentMenu !== 'stage-creation' || !stageInputRef?.current?.value)
|
||||||
currentMenu !== BoardOptionsMenu.StageCreation ||
|
|
||||||
!stageInputRef?.current?.value
|
|
||||||
)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const columnToCreate: ColumnForCreate = {
|
const columnToCreate: ColumnForCreate = {
|
||||||
@ -113,6 +123,8 @@ export function BoardOptionsDropdownContent({
|
|||||||
setCurrentMenu(menu);
|
setCurrentMenu(menu);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { handleFieldVisibilityChange } = useBoardCardFields({ scopeContext });
|
||||||
|
|
||||||
const { closeDropdownButton } = useDropdownButton({
|
const { closeDropdownButton } = useDropdownButton({
|
||||||
key: BoardOptionsDropdownKey,
|
key: BoardOptionsDropdownKey,
|
||||||
});
|
});
|
||||||
@ -161,14 +173,19 @@ export function BoardOptionsDropdownContent({
|
|||||||
<StyledDropdownMenuSeparator />
|
<StyledDropdownMenuSeparator />
|
||||||
<StyledDropdownMenuItemsContainer>
|
<StyledDropdownMenuItemsContainer>
|
||||||
<MenuItemNavigate
|
<MenuItemNavigate
|
||||||
onClick={() => handleMenuNavigate(BoardOptionsMenu.Stages)}
|
onClick={() => handleMenuNavigate('stages')}
|
||||||
LeftIcon={IconLayoutKanban}
|
LeftIcon={IconLayoutKanban}
|
||||||
text="Stages"
|
text="Stages"
|
||||||
/>
|
/>
|
||||||
|
<MenuItemNavigate
|
||||||
|
onClick={() => handleMenuNavigate('fields')}
|
||||||
|
LeftIcon={IconTag}
|
||||||
|
text="Fields"
|
||||||
|
/>
|
||||||
</StyledDropdownMenuItemsContainer>
|
</StyledDropdownMenuItemsContainer>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{currentMenu === BoardOptionsMenu.Stages && (
|
{currentMenu === 'stages' && (
|
||||||
<>
|
<>
|
||||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
||||||
Stages
|
Stages
|
||||||
@ -176,20 +193,45 @@ export function BoardOptionsDropdownContent({
|
|||||||
<StyledDropdownMenuSeparator />
|
<StyledDropdownMenuSeparator />
|
||||||
<StyledDropdownMenuItemsContainer>
|
<StyledDropdownMenuItemsContainer>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => setCurrentMenu(BoardOptionsMenu.StageCreation)}
|
onClick={() => setCurrentMenu('stage-creation')}
|
||||||
LeftIcon={IconPlus}
|
LeftIcon={IconPlus}
|
||||||
text="Add stage"
|
text="Add stage"
|
||||||
/>
|
/>
|
||||||
</StyledDropdownMenuItemsContainer>
|
</StyledDropdownMenuItemsContainer>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{currentMenu === BoardOptionsMenu.StageCreation && (
|
{currentMenu === 'stage-creation' && (
|
||||||
<DropdownMenuInput
|
<DropdownMenuInput
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder="New stage"
|
placeholder="New stage"
|
||||||
ref={stageInputRef}
|
ref={stageInputRef}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{currentMenu === 'fields' && (
|
||||||
|
<>
|
||||||
|
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
||||||
|
Fields
|
||||||
|
</DropdownMenuHeader>
|
||||||
|
<StyledDropdownMenuSeparator />
|
||||||
|
{hasVisibleFields && (
|
||||||
|
<ViewFieldsVisibilityDropdownSection
|
||||||
|
title="Visible"
|
||||||
|
fields={visibleBoardCardFields}
|
||||||
|
onVisibilityChange={handleFieldVisibilityChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{hasVisibleFields && hasHiddenFields && (
|
||||||
|
<StyledDropdownMenuSeparator />
|
||||||
|
)}
|
||||||
|
{hasHiddenFields && (
|
||||||
|
<ViewFieldsVisibilityDropdownSection
|
||||||
|
title="Hidden"
|
||||||
|
fields={hiddenBoardCardFields}
|
||||||
|
onVisibilityChange={handleFieldVisibilityChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</StyledDropdownMenu>
|
</StyledDropdownMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -160,8 +160,9 @@ export function EntityBoard({
|
|||||||
<EntityBoardColumn
|
<EntityBoardColumn
|
||||||
boardOptions={boardOptions}
|
boardOptions={boardOptions}
|
||||||
column={column}
|
column={column}
|
||||||
onTitleEdit={onEditColumnTitle}
|
|
||||||
onDelete={onColumnDelete}
|
onDelete={onColumnDelete}
|
||||||
|
onTitleEdit={onEditColumnTitle}
|
||||||
|
scopeContext={scopeContext}
|
||||||
/>
|
/>
|
||||||
</RecoilScope>
|
</RecoilScope>
|
||||||
</BoardColumnIdContext.Provider>
|
</BoardColumnIdContext.Provider>
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import type { Context } from 'react';
|
||||||
import { Draggable } from '@hello-pangea/dnd';
|
import { Draggable } from '@hello-pangea/dnd';
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
@ -11,10 +12,12 @@ export function EntityBoardCard({
|
|||||||
boardOptions,
|
boardOptions,
|
||||||
cardId,
|
cardId,
|
||||||
index,
|
index,
|
||||||
|
scopeContext,
|
||||||
}: {
|
}: {
|
||||||
boardOptions: BoardOptions;
|
boardOptions: BoardOptions;
|
||||||
cardId: string;
|
cardId: string;
|
||||||
index: number;
|
index: number;
|
||||||
|
scopeContext: Context<string | null>;
|
||||||
}) {
|
}) {
|
||||||
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
|
||||||
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
|
||||||
@ -43,7 +46,7 @@ export function EntityBoardCard({
|
|||||||
data-select-disable
|
data-select-disable
|
||||||
onContextMenu={handleContextMenu}
|
onContextMenu={handleContextMenu}
|
||||||
>
|
>
|
||||||
{boardOptions.cardComponent}
|
{<boardOptions.CardComponent scopeContext={scopeContext} />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Draggable>
|
</Draggable>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useContext } from 'react';
|
import { type Context, useContext } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Draggable, Droppable, DroppableProvided } from '@hello-pangea/dnd';
|
import { Draggable, Droppable, DroppableProvided } from '@hello-pangea/dnd';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
@ -48,15 +48,17 @@ const BoardColumnCardsContainer = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function EntityBoardColumn({
|
export function EntityBoardColumn({
|
||||||
column,
|
|
||||||
boardOptions,
|
boardOptions,
|
||||||
|
column,
|
||||||
onDelete,
|
onDelete,
|
||||||
onTitleEdit,
|
onTitleEdit,
|
||||||
|
scopeContext,
|
||||||
}: {
|
}: {
|
||||||
column: BoardColumnDefinition;
|
|
||||||
boardOptions: BoardOptions;
|
boardOptions: BoardOptions;
|
||||||
|
column: BoardColumnDefinition;
|
||||||
onDelete?: (columnId: string) => void;
|
onDelete?: (columnId: string) => void;
|
||||||
onTitleEdit: (columnId: string, title: string, color: string) => void;
|
onTitleEdit: (columnId: string, title: string, color: string) => void;
|
||||||
|
scopeContext: Context<string | null>;
|
||||||
}) {
|
}) {
|
||||||
const boardColumnId = useContext(BoardColumnIdContext) ?? '';
|
const boardColumnId = useContext(BoardColumnIdContext) ?? '';
|
||||||
|
|
||||||
@ -92,6 +94,7 @@ export function EntityBoardColumn({
|
|||||||
index={index}
|
index={index}
|
||||||
cardId={cardId}
|
cardId={cardId}
|
||||||
boardOptions={boardOptions}
|
boardOptions={boardOptions}
|
||||||
|
scopeContext={scopeContext}
|
||||||
/>
|
/>
|
||||||
</BoardCardIdContext.Provider>
|
</BoardCardIdContext.Provider>
|
||||||
))}
|
))}
|
||||||
|
|||||||
42
front/src/modules/ui/board/hooks/useBoardCardFields.ts
Normal file
42
front/src/modules/ui/board/hooks/useBoardCardFields.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import type { Context } from 'react';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
|
||||||
|
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
|
||||||
|
import { boardCardFieldsByKeyScopedSelector } from '../states/selectors/boardCardFieldsByKeyScopedSelector';
|
||||||
|
|
||||||
|
export const useBoardCardFields = ({
|
||||||
|
scopeContext,
|
||||||
|
}: {
|
||||||
|
scopeContext: Context<string | null>;
|
||||||
|
}) => {
|
||||||
|
const [boardCardFields, setBoardCardFields] = useRecoilScopedState(
|
||||||
|
boardCardFieldsScopedState,
|
||||||
|
scopeContext,
|
||||||
|
);
|
||||||
|
const boardCardFieldsByKey = useRecoilScopedValue(
|
||||||
|
boardCardFieldsByKeyScopedSelector,
|
||||||
|
scopeContext,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFieldVisibilityChange = (
|
||||||
|
field: ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
|
) => {
|
||||||
|
const nextFields = boardCardFieldsByKey[field.key]
|
||||||
|
? boardCardFields.map((previousField) =>
|
||||||
|
previousField.key === field.key
|
||||||
|
? { ...previousField, isVisible: !field.isVisible }
|
||||||
|
: previousField,
|
||||||
|
)
|
||||||
|
: [...boardCardFields, { ...field, isVisible: true }];
|
||||||
|
|
||||||
|
setBoardCardFields(nextFields);
|
||||||
|
};
|
||||||
|
|
||||||
|
return { handleFieldVisibilityChange };
|
||||||
|
};
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
|
|
||||||
|
export const availableBoardCardFieldsScopedState = atomFamily<
|
||||||
|
ViewFieldDefinition<ViewFieldMetadata>[],
|
||||||
|
string
|
||||||
|
>({
|
||||||
|
key: 'availableBoardCardFieldsScopedState',
|
||||||
|
default: [],
|
||||||
|
});
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { atomFamily } from 'recoil';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
|
|
||||||
|
export const boardCardFieldsScopedState = atomFamily<
|
||||||
|
ViewFieldDefinition<ViewFieldMetadata>[],
|
||||||
|
string
|
||||||
|
>({
|
||||||
|
key: 'boardCardFieldsScopedState',
|
||||||
|
default: [],
|
||||||
|
});
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
|
|
||||||
|
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
|
||||||
|
|
||||||
|
export const boardCardFieldsByKeyScopedSelector = selectorFamily({
|
||||||
|
key: 'boardCardFieldsByKeyScopedSelector',
|
||||||
|
get:
|
||||||
|
(scopeId: string) =>
|
||||||
|
({ get }) =>
|
||||||
|
get(boardCardFieldsScopedState(scopeId)).reduce<
|
||||||
|
Record<string, ViewFieldDefinition<ViewFieldMetadata>>
|
||||||
|
>((result, field) => ({ ...result, [field.key]: field }), {}),
|
||||||
|
});
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { availableBoardCardFieldsScopedState } from '../availableBoardCardFieldsScopedState';
|
||||||
|
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
|
||||||
|
|
||||||
|
export const hiddenBoardCardFieldsScopedSelector = selectorFamily({
|
||||||
|
key: 'hiddenBoardCardFieldsScopedSelector',
|
||||||
|
get:
|
||||||
|
(scopeId: string) =>
|
||||||
|
({ get }) => {
|
||||||
|
const fields = get(boardCardFieldsScopedState(scopeId));
|
||||||
|
const fieldKeys = fields.map(({ key }) => key);
|
||||||
|
const otherAvailableKeys = get(
|
||||||
|
availableBoardCardFieldsScopedState(scopeId),
|
||||||
|
).filter(({ key }) => !fieldKeys.includes(key));
|
||||||
|
|
||||||
|
return [
|
||||||
|
...fields.filter((field) => !field.isVisible),
|
||||||
|
...otherAvailableKeys,
|
||||||
|
];
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { selectorFamily } from 'recoil';
|
||||||
|
|
||||||
|
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
|
||||||
|
|
||||||
|
export const visibleBoardCardFieldsScopedSelector = selectorFamily({
|
||||||
|
key: 'visibleBoardCardFieldsScopedSelector',
|
||||||
|
get:
|
||||||
|
(scopeId: string) =>
|
||||||
|
({ get }) =>
|
||||||
|
get(boardCardFieldsScopedState(scopeId)).filter(
|
||||||
|
(field) => field.isVisible,
|
||||||
|
),
|
||||||
|
});
|
||||||
@ -1,13 +0,0 @@
|
|||||||
import { atom } from 'recoil';
|
|
||||||
|
|
||||||
import type {
|
|
||||||
ViewFieldDefinition,
|
|
||||||
ViewFieldMetadata,
|
|
||||||
} from '../../editable-field/types/ViewField';
|
|
||||||
|
|
||||||
export const viewFieldsDefinitionsState = atom<
|
|
||||||
ViewFieldDefinition<ViewFieldMetadata>[]
|
|
||||||
>({
|
|
||||||
key: 'viewFieldsDefinitionState',
|
|
||||||
default: [],
|
|
||||||
});
|
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
import type { ComponentType, Context } from 'react';
|
||||||
|
|
||||||
import { FilterDefinitionByEntity } from '@/ui/view-bar/types/FilterDefinitionByEntity';
|
import { FilterDefinitionByEntity } from '@/ui/view-bar/types/FilterDefinitionByEntity';
|
||||||
import { SortType } from '@/ui/view-bar/types/interface';
|
import { SortType } from '@/ui/view-bar/types/interface';
|
||||||
import { PipelineProgress } from '~/generated/graphql';
|
import { PipelineProgress } from '~/generated/graphql';
|
||||||
@ -5,7 +7,7 @@ import { PipelineProgressOrderByWithRelationInput as PipelineProgresses_Order_By
|
|||||||
|
|
||||||
export type BoardOptions = {
|
export type BoardOptions = {
|
||||||
newCardComponent: React.ReactNode;
|
newCardComponent: React.ReactNode;
|
||||||
cardComponent: React.ReactNode;
|
CardComponent: ComponentType<{ scopeContext: Context<string | null> }>;
|
||||||
filters: FilterDefinitionByEntity<PipelineProgress>[];
|
filters: FilterDefinitionByEntity<PipelineProgress>[];
|
||||||
sorts: Array<SortType<PipelineProgresses_Order_By>>;
|
sorts: Array<SortType<PipelineProgresses_Order_By>>;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -49,6 +49,7 @@ export function FloatingIconButtonGroup({
|
|||||||
applyBlur={false}
|
applyBlur={false}
|
||||||
applyShadow={false}
|
applyShadow={false}
|
||||||
Icon={Icon}
|
Icon={Icon}
|
||||||
|
key={index}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
position={position}
|
position={position}
|
||||||
size={size}
|
size={size}
|
||||||
|
|||||||
@ -117,11 +117,12 @@ export type ViewFieldMetadata = { type: ViewFieldType } & (
|
|||||||
);
|
);
|
||||||
|
|
||||||
export type ViewFieldDefinition<T extends ViewFieldMetadata | unknown> = {
|
export type ViewFieldDefinition<T extends ViewFieldMetadata | unknown> = {
|
||||||
key: string;
|
|
||||||
name: string;
|
|
||||||
Icon?: IconComponent;
|
Icon?: IconComponent;
|
||||||
|
index: number;
|
||||||
isVisible?: boolean;
|
isVisible?: boolean;
|
||||||
|
key: string;
|
||||||
metadata: T;
|
metadata: T;
|
||||||
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ViewFieldTextValue = string;
|
export type ViewFieldTextValue = string;
|
||||||
|
|||||||
@ -12,25 +12,25 @@ import { IconChevronLeft, IconFileImport, IconTag } from '@/ui/icon';
|
|||||||
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||||
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
import { ViewFieldsVisibilityDropdownSection } from '@/ui/view-bar/components/ViewFieldsVisibilityDropdownSection';
|
||||||
import { useUpsertView } from '@/ui/view-bar/hooks/useUpsertView';
|
import { useUpsertView } from '@/ui/view-bar/hooks/useUpsertView';
|
||||||
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
|
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
|
||||||
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
|
||||||
import type { View } from '@/ui/view-bar/types/View';
|
import type { View } from '@/ui/view-bar/types/View';
|
||||||
|
|
||||||
|
import { useTableColumns } from '../../hooks/useTableColumns';
|
||||||
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
|
||||||
import { hiddenTableColumnsScopedSelector } from '../../states/selectors/hiddenTableColumnsScopedSelector';
|
import { hiddenTableColumnsScopedSelector } from '../../states/selectors/hiddenTableColumnsScopedSelector';
|
||||||
import { visibleTableColumnsScopedSelector } from '../../states/selectors/visibleTableColumnsScopedSelector';
|
import { visibleTableColumnsScopedSelector } from '../../states/selectors/visibleTableColumnsScopedSelector';
|
||||||
import { TableOptionsDropdownKey } from '../../types/TableOptionsDropdownKey';
|
import { TableOptionsDropdownKey } from '../../types/TableOptionsDropdownKey';
|
||||||
import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
|
import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
|
||||||
|
|
||||||
import { TableOptionsDropdownColumnVisibility } from './TableOptionsDropdownSection';
|
|
||||||
|
|
||||||
type TableOptionsDropdownButtonProps = {
|
type TableOptionsDropdownButtonProps = {
|
||||||
onViewsChange?: (views: View[]) => void | Promise<void>;
|
onViewsChange?: (views: View[]) => void | Promise<void>;
|
||||||
onImport?: () => void;
|
onImport?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TableOptionsMenu = 'properties';
|
type TableOptionsMenu = 'fields';
|
||||||
|
|
||||||
export function TableOptionsDropdownContent({
|
export function TableOptionsDropdownContent({
|
||||||
onViewsChange,
|
onViewsChange,
|
||||||
@ -40,9 +40,9 @@ export function TableOptionsDropdownContent({
|
|||||||
key: TableOptionsDropdownKey,
|
key: TableOptionsDropdownKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [selectedMenu, setSelectedMenu] = useState<
|
const [currentMenu, setCurrentMenu] = useState<TableOptionsMenu | undefined>(
|
||||||
TableOptionsMenu | undefined
|
undefined,
|
||||||
>(undefined);
|
);
|
||||||
|
|
||||||
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
const viewEditInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@ -65,6 +65,8 @@ export function TableOptionsDropdownContent({
|
|||||||
scopeContext: TableRecoilScopeContext,
|
scopeContext: TableRecoilScopeContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { handleColumnVisibilityChange } = useTableColumns();
|
||||||
|
|
||||||
const handleViewNameSubmit = async () => {
|
const handleViewNameSubmit = async () => {
|
||||||
const name = viewEditInputRef.current?.value;
|
const name = viewEditInputRef.current?.value;
|
||||||
await upsertView(name);
|
await upsertView(name);
|
||||||
@ -72,10 +74,10 @@ export function TableOptionsDropdownContent({
|
|||||||
|
|
||||||
const handleSelectMenu = (option: TableOptionsMenu) => {
|
const handleSelectMenu = (option: TableOptionsMenu) => {
|
||||||
handleViewNameSubmit();
|
handleViewNameSubmit();
|
||||||
setSelectedMenu(option);
|
setCurrentMenu(option);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetMenu = () => setSelectedMenu(undefined);
|
const resetMenu = () => setCurrentMenu(undefined);
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
Key.Escape,
|
Key.Escape,
|
||||||
@ -97,7 +99,7 @@ export function TableOptionsDropdownContent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledDropdownMenu>
|
<StyledDropdownMenu>
|
||||||
{!selectedMenu && (
|
{!currentMenu && (
|
||||||
<>
|
<>
|
||||||
{!!viewEditMode.mode ? (
|
{!!viewEditMode.mode ? (
|
||||||
<DropdownMenuInput
|
<DropdownMenuInput
|
||||||
@ -118,9 +120,9 @@ export function TableOptionsDropdownContent({
|
|||||||
<StyledDropdownMenuSeparator />
|
<StyledDropdownMenuSeparator />
|
||||||
<StyledDropdownMenuItemsContainer>
|
<StyledDropdownMenuItemsContainer>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => handleSelectMenu('properties')}
|
onClick={() => handleSelectMenu('fields')}
|
||||||
LeftIcon={IconTag}
|
LeftIcon={IconTag}
|
||||||
text="Properties"
|
text="Fields"
|
||||||
/>
|
/>
|
||||||
{onImport && (
|
{onImport && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@ -132,22 +134,24 @@ export function TableOptionsDropdownContent({
|
|||||||
</StyledDropdownMenuItemsContainer>
|
</StyledDropdownMenuItemsContainer>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{selectedMenu === 'properties' && (
|
{currentMenu === 'fields' && (
|
||||||
<>
|
<>
|
||||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
|
||||||
Properties
|
Fields
|
||||||
</DropdownMenuHeader>
|
</DropdownMenuHeader>
|
||||||
<StyledDropdownMenuSeparator />
|
<StyledDropdownMenuSeparator />
|
||||||
<TableOptionsDropdownColumnVisibility
|
<ViewFieldsVisibilityDropdownSection
|
||||||
title="Visible"
|
title="Visible"
|
||||||
columns={visibleTableColumns}
|
fields={visibleTableColumns}
|
||||||
|
onVisibilityChange={handleColumnVisibilityChange}
|
||||||
/>
|
/>
|
||||||
{hiddenTableColumns.length > 0 && (
|
{hiddenTableColumns.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<StyledDropdownMenuSeparator />
|
<StyledDropdownMenuSeparator />
|
||||||
<TableOptionsDropdownColumnVisibility
|
<ViewFieldsVisibilityDropdownSection
|
||||||
title="Hidden"
|
title="Hidden"
|
||||||
columns={hiddenTableColumns}
|
fields={hiddenTableColumns}
|
||||||
|
onVisibilityChange={handleColumnVisibilityChange}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -1,41 +0,0 @@
|
|||||||
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
|
|
||||||
import { StyledDropdownMenuSubheader } from '@/ui/dropdown/components/StyledDropdownMenuSubheader';
|
|
||||||
import type { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField';
|
|
||||||
import { IconMinus, IconPlus } from '@/ui/icon';
|
|
||||||
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
|
||||||
|
|
||||||
import { useTableColumns } from '../../hooks/useTableColumns';
|
|
||||||
import type { ColumnDefinition } from '../../types/ColumnDefinition';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
title: string;
|
|
||||||
columns: ColumnDefinition<ViewFieldMetadata>[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export function TableOptionsDropdownColumnVisibility({
|
|
||||||
title,
|
|
||||||
columns,
|
|
||||||
}: OwnProps) {
|
|
||||||
const { handleColumnVisibilityChange } = useTableColumns();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<StyledDropdownMenuSubheader>{title}</StyledDropdownMenuSubheader>
|
|
||||||
<StyledDropdownMenuItemsContainer>
|
|
||||||
{columns.map((column) => (
|
|
||||||
<MenuItem
|
|
||||||
key={column.key}
|
|
||||||
LeftIcon={column.Icon}
|
|
||||||
iconButtons={[
|
|
||||||
{
|
|
||||||
Icon: column.isVisible ? IconMinus : IconPlus,
|
|
||||||
onClick: () => handleColumnVisibilityChange(column),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
text={column.name}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</StyledDropdownMenuItemsContainer>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
import { useCallback } from 'react';
|
|
||||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
|
||||||
@ -70,17 +69,6 @@ export function TableHeader<SortField>({
|
|||||||
await onViewSubmit?.();
|
await onViewSubmit?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
const OptionsDropdownButton = useCallback(
|
|
||||||
() => (
|
|
||||||
<TableOptionsDropdown
|
|
||||||
onImport={onImport}
|
|
||||||
onViewsChange={onViewsChange}
|
|
||||||
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
[onImport, onViewsChange],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
|
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
|
||||||
<ViewBar
|
<ViewBar
|
||||||
@ -89,7 +77,13 @@ export function TableHeader<SortField>({
|
|||||||
onReset={handleViewBarReset}
|
onReset={handleViewBarReset}
|
||||||
onViewSelect={handleViewSelect}
|
onViewSelect={handleViewSelect}
|
||||||
onViewSubmit={handleViewSubmit}
|
onViewSubmit={handleViewSubmit}
|
||||||
OptionsDropdownButton={OptionsDropdownButton}
|
optionsDropdownButton={
|
||||||
|
<TableOptionsDropdown
|
||||||
|
onImport={onImport}
|
||||||
|
onViewsChange={onViewsChange}
|
||||||
|
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
optionsDropdownKey={TableOptionsDropdownKey}
|
optionsDropdownKey={TableOptionsDropdownKey}
|
||||||
scopeContext={TableRecoilScopeContext}
|
scopeContext={TableRecoilScopeContext}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -6,5 +6,4 @@ import type {
|
|||||||
export type ColumnDefinition<T extends ViewFieldMetadata | unknown> =
|
export type ColumnDefinition<T extends ViewFieldMetadata | unknown> =
|
||||||
ViewFieldDefinition<T> & {
|
ViewFieldDefinition<T> & {
|
||||||
size: number;
|
size: number;
|
||||||
index: number;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { ComponentProps, ComponentType, Context } from 'react';
|
import type { ComponentProps, Context, ReactNode } from 'react';
|
||||||
|
|
||||||
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
|
||||||
import { TopBar } from '@/ui/top-bar/TopBar';
|
import { TopBar } from '@/ui/top-bar/TopBar';
|
||||||
@ -22,7 +22,7 @@ import {
|
|||||||
} from './ViewsDropdownButton';
|
} from './ViewsDropdownButton';
|
||||||
|
|
||||||
export type ViewBarProps<SortField> = ComponentProps<'div'> & {
|
export type ViewBarProps<SortField> = ComponentProps<'div'> & {
|
||||||
OptionsDropdownButton: ComponentType;
|
optionsDropdownButton: ReactNode;
|
||||||
optionsDropdownKey: string;
|
optionsDropdownKey: string;
|
||||||
scopeContext: Context<string | null>;
|
scopeContext: Context<string | null>;
|
||||||
} & Pick<
|
} & Pick<
|
||||||
@ -41,7 +41,7 @@ export const ViewBar = <SortField,>({
|
|||||||
onViewsChange,
|
onViewsChange,
|
||||||
onViewSelect,
|
onViewSelect,
|
||||||
onViewSubmit,
|
onViewSubmit,
|
||||||
OptionsDropdownButton,
|
optionsDropdownButton,
|
||||||
optionsDropdownKey,
|
optionsDropdownKey,
|
||||||
scopeContext,
|
scopeContext,
|
||||||
...props
|
...props
|
||||||
@ -76,7 +76,7 @@ export const ViewBar = <SortField,>({
|
|||||||
hotkeyScope={FiltersHotkeyScope.FilterDropdownButton}
|
hotkeyScope={FiltersHotkeyScope.FilterDropdownButton}
|
||||||
isPrimaryButton
|
isPrimaryButton
|
||||||
/>
|
/>
|
||||||
<OptionsDropdownButton />
|
{optionsDropdownButton}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
bottomComponent={
|
bottomComponent={
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
|
||||||
|
import { StyledDropdownMenuSubheader } from '@/ui/dropdown/components/StyledDropdownMenuSubheader';
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
|
import { IconMinus, IconPlus } from '@/ui/icon';
|
||||||
|
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
|
||||||
|
|
||||||
|
type OwnProps<Field> = {
|
||||||
|
fields: Field[];
|
||||||
|
onVisibilityChange: (field: Field) => void;
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ViewFieldsVisibilityDropdownSection<
|
||||||
|
Field extends ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
|
>({ fields, onVisibilityChange, title }: OwnProps<Field>) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StyledDropdownMenuSubheader>{title}</StyledDropdownMenuSubheader>
|
||||||
|
<StyledDropdownMenuItemsContainer>
|
||||||
|
{fields.map((field) => (
|
||||||
|
<MenuItem
|
||||||
|
key={field.key}
|
||||||
|
LeftIcon={field.Icon}
|
||||||
|
iconButtons={[
|
||||||
|
{
|
||||||
|
Icon: field.isVisible ? IconMinus : IconPlus,
|
||||||
|
onClick: () => onVisibilityChange(field),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
text={field.name}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</StyledDropdownMenuItemsContainer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
113
front/src/modules/views/hooks/useBoardViewFields.ts
Normal file
113
front/src/modules/views/hooks/useBoardViewFields.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import { type Context } from 'react';
|
||||||
|
|
||||||
|
import { availableBoardCardFieldsScopedState } from '@/ui/board/states/availableBoardCardFieldsScopedState';
|
||||||
|
import { boardCardFieldsScopedState } from '@/ui/board/states/boardCardFieldsScopedState';
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
|
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||||
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
|
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
|
||||||
|
import {
|
||||||
|
SortOrder,
|
||||||
|
useCreateViewFieldsMutation,
|
||||||
|
useGetViewFieldsQuery,
|
||||||
|
} from '~/generated/graphql';
|
||||||
|
import { assertNotNull } from '~/utils/assert';
|
||||||
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||||
|
|
||||||
|
const toViewFieldInput = (
|
||||||
|
objectId: 'company' | 'person',
|
||||||
|
fieldDefinition: ViewFieldDefinition<ViewFieldMetadata>,
|
||||||
|
) => ({
|
||||||
|
key: fieldDefinition.key,
|
||||||
|
name: fieldDefinition.name,
|
||||||
|
index: fieldDefinition.index,
|
||||||
|
isVisible: fieldDefinition.isVisible ?? true,
|
||||||
|
objectId,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useBoardViewFields = ({
|
||||||
|
objectId,
|
||||||
|
fieldDefinitions,
|
||||||
|
scopeContext,
|
||||||
|
skipFetch,
|
||||||
|
}: {
|
||||||
|
objectId: 'company' | 'person';
|
||||||
|
fieldDefinitions: ViewFieldDefinition<ViewFieldMetadata>[];
|
||||||
|
scopeContext: Context<string | null>;
|
||||||
|
skipFetch?: boolean;
|
||||||
|
}) => {
|
||||||
|
const currentViewId = useRecoilScopedValue(
|
||||||
|
currentViewIdScopedState,
|
||||||
|
scopeContext,
|
||||||
|
);
|
||||||
|
const [availableBoardCardFields, setAvailableBoardCardFields] =
|
||||||
|
useRecoilScopedState(availableBoardCardFieldsScopedState, scopeContext);
|
||||||
|
const [boardCardFields, setBoardCardFields] = useRecoilScopedState(
|
||||||
|
boardCardFieldsScopedState,
|
||||||
|
scopeContext,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [createViewFieldsMutation] = useCreateViewFieldsMutation();
|
||||||
|
|
||||||
|
const createViewFields = (
|
||||||
|
fields: ViewFieldDefinition<ViewFieldMetadata>[],
|
||||||
|
viewId = currentViewId,
|
||||||
|
) => {
|
||||||
|
if (!viewId || !fields.length) return;
|
||||||
|
|
||||||
|
return createViewFieldsMutation({
|
||||||
|
variables: {
|
||||||
|
data: fields.map((field) => ({
|
||||||
|
...toViewFieldInput(objectId, field),
|
||||||
|
viewId,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { refetch } = useGetViewFieldsQuery({
|
||||||
|
skip: !currentViewId || skipFetch,
|
||||||
|
variables: {
|
||||||
|
orderBy: { index: SortOrder.Asc },
|
||||||
|
where: {
|
||||||
|
viewId: { equals: currentViewId },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
onCompleted: async (data) => {
|
||||||
|
if (!data.viewFields.length) {
|
||||||
|
// Populate if empty
|
||||||
|
await createViewFields(fieldDefinitions);
|
||||||
|
return refetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextFields = data.viewFields
|
||||||
|
.map<ViewFieldDefinition<ViewFieldMetadata> | null>((viewField) => {
|
||||||
|
const fieldDefinition = fieldDefinitions.find(
|
||||||
|
({ key }) => viewField.key === key,
|
||||||
|
);
|
||||||
|
|
||||||
|
return fieldDefinition
|
||||||
|
? {
|
||||||
|
...fieldDefinition,
|
||||||
|
key: viewField.key,
|
||||||
|
name: viewField.name,
|
||||||
|
index: viewField.index,
|
||||||
|
isVisible: viewField.isVisible,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
})
|
||||||
|
.filter<ViewFieldDefinition<ViewFieldMetadata>>(assertNotNull);
|
||||||
|
|
||||||
|
if (!isDeeplyEqual(boardCardFields, nextFields)) {
|
||||||
|
setBoardCardFields(nextFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!availableBoardCardFields.length) {
|
||||||
|
setAvailableBoardCardFields(fieldDefinitions);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -1,5 +1,9 @@
|
|||||||
import { type Context } from 'react';
|
import type { Context } from 'react';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ViewFieldDefinition,
|
||||||
|
ViewFieldMetadata,
|
||||||
|
} from '@/ui/editable-field/types/ViewField';
|
||||||
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
|
||||||
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
|
import { filtersScopedState } from '@/ui/view-bar/states/filtersScopedState';
|
||||||
import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
|
import { sortsScopedState } from '@/ui/view-bar/states/sortsScopedState';
|
||||||
@ -7,6 +11,7 @@ import type { FilterDefinitionByEntity } from '@/ui/view-bar/types/FilterDefinit
|
|||||||
import type { SortType } from '@/ui/view-bar/types/interface';
|
import type { SortType } from '@/ui/view-bar/types/interface';
|
||||||
import { ViewType } from '~/generated/graphql';
|
import { ViewType } from '~/generated/graphql';
|
||||||
|
|
||||||
|
import { useBoardViewFields } from './useBoardViewFields';
|
||||||
import { useViewFilters } from './useViewFilters';
|
import { useViewFilters } from './useViewFilters';
|
||||||
import { useViews } from './useViews';
|
import { useViews } from './useViews';
|
||||||
import { useViewSorts } from './useViewSorts';
|
import { useViewSorts } from './useViewSorts';
|
||||||
@ -14,11 +19,13 @@ import { useViewSorts } from './useViewSorts';
|
|||||||
export const useBoardViews = <Entity, SortField>({
|
export const useBoardViews = <Entity, SortField>({
|
||||||
availableFilters,
|
availableFilters,
|
||||||
availableSorts,
|
availableSorts,
|
||||||
|
fieldDefinitions,
|
||||||
objectId,
|
objectId,
|
||||||
scopeContext,
|
scopeContext,
|
||||||
}: {
|
}: {
|
||||||
availableFilters: FilterDefinitionByEntity<Entity>[];
|
availableFilters: FilterDefinitionByEntity<Entity>[];
|
||||||
availableSorts: SortType<SortField>[];
|
availableSorts: SortType<SortField>[];
|
||||||
|
fieldDefinitions: ViewFieldDefinition<ViewFieldMetadata>[];
|
||||||
objectId: 'company';
|
objectId: 'company';
|
||||||
scopeContext: Context<string | null>;
|
scopeContext: Context<string | null>;
|
||||||
}) => {
|
}) => {
|
||||||
@ -31,6 +38,12 @@ export const useBoardViews = <Entity, SortField>({
|
|||||||
type: ViewType.Pipeline,
|
type: ViewType.Pipeline,
|
||||||
scopeContext,
|
scopeContext,
|
||||||
});
|
});
|
||||||
|
useBoardViewFields({
|
||||||
|
objectId,
|
||||||
|
fieldDefinitions,
|
||||||
|
scopeContext,
|
||||||
|
skipFetch: isFetchingViews,
|
||||||
|
});
|
||||||
const { createViewFilters, persistFilters } = useViewFilters({
|
const { createViewFilters, persistFilters } = useViewFilters({
|
||||||
availableFilters,
|
availableFilters,
|
||||||
scopeContext,
|
scopeContext,
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { opportunitiesSorts } from './opportunities-sorts';
|
|||||||
|
|
||||||
export const opportunitiesBoardOptions: BoardOptions = {
|
export const opportunitiesBoardOptions: BoardOptions = {
|
||||||
newCardComponent: <NewCompanyProgressButton />,
|
newCardComponent: <NewCompanyProgressButton />,
|
||||||
cardComponent: <CompanyBoardCard />,
|
CardComponent: CompanyBoardCard,
|
||||||
filters: opportunitiesFilters,
|
filters: opportunitiesFilters,
|
||||||
sorts: opportunitiesSorts,
|
sorts: opportunitiesSorts,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import {
|
|||||||
import { mockedActivities, mockedTasks } from './mock-data/activities';
|
import { mockedActivities, mockedTasks } from './mock-data/activities';
|
||||||
import {
|
import {
|
||||||
mockedCompaniesData,
|
mockedCompaniesData,
|
||||||
|
mockedCompanyBoardCardFields,
|
||||||
mockedCompanyBoardViews,
|
mockedCompanyBoardViews,
|
||||||
mockedCompanyTableColumns,
|
mockedCompanyTableColumns,
|
||||||
mockedCompanyTableViews,
|
mockedCompanyTableViews,
|
||||||
@ -264,7 +265,9 @@ export const graphqlMocks = [
|
|||||||
return res(
|
return res(
|
||||||
ctx.data({
|
ctx.data({
|
||||||
viewFields:
|
viewFields:
|
||||||
viewId === mockedCompanyTableViews[0].id
|
viewId === mockedCompanyBoardViews[0].id
|
||||||
|
? mockedCompanyBoardCardFields
|
||||||
|
: viewId === mockedCompanyTableViews[0].id
|
||||||
? mockedCompanyTableColumns
|
? mockedCompanyTableColumns
|
||||||
: mockedPersonTableColumns,
|
: mockedPersonTableColumns,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { companiesAvailableColumnDefinitions } from '@/companies/constants/companiesAvailableColumnDefinitions';
|
import { companiesAvailableColumnDefinitions } from '@/companies/constants/companiesAvailableColumnDefinitions';
|
||||||
|
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
|
||||||
import {
|
import {
|
||||||
Company,
|
Company,
|
||||||
Favorite,
|
Favorite,
|
||||||
@ -168,6 +169,19 @@ export const mockedCompanyBoardViews: View[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const mockedCompanyBoardCardFields =
|
||||||
|
pipelineAvailableFieldDefinitions.map<Omit<ViewField, 'view'>>(
|
||||||
|
(viewFieldDefinition) => ({
|
||||||
|
__typename: 'ViewField',
|
||||||
|
name: viewFieldDefinition.name,
|
||||||
|
index: viewFieldDefinition.index,
|
||||||
|
isVisible: true,
|
||||||
|
key: viewFieldDefinition.key,
|
||||||
|
objectId: 'company',
|
||||||
|
viewId: mockedCompanyBoardViews[0].id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
export const mockedCompanyTableViews: View[] = [
|
export const mockedCompanyTableViews: View[] = [
|
||||||
{
|
{
|
||||||
__typename: 'View',
|
__typename: 'View',
|
||||||
|
|||||||
Reference in New Issue
Block a user