feat: reorder kanban columns (#1699)

* kaban header options

* gql codegn

* moveColumn hook refactor

* BoardColumnContext addition

* saved board columns state

* db call hook update

* lint fix

* state change first db call second

* handleMoveTableColumn call

* codegen lint fix

* useMoveViewColumns hook

* useBoardColumns db call addition

* boardColumns state change from BoardHeader

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Aditya Pimpalkar
2023-09-27 14:59:44 +01:00
committed by GitHub
parent 1e716bf6d6
commit 46ad36061e
18 changed files with 456 additions and 107 deletions

View File

@ -1,7 +1,7 @@
import { useCallback, useContext, useState } from 'react';
import { NewButton } from '@/ui/board/components/NewButton';
import { BoardColumnIdContext } from '@/ui/board/contexts/BoardColumnIdContext';
import { BoardColumnContext } from '@/ui/board/contexts/BoardColumnContext';
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
@ -14,7 +14,9 @@ import { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompany
export const NewCompanyProgressButton = () => {
const [isCreatingCard, setIsCreatingCard] = useState(false);
const pipelineStageId = useContext(BoardColumnIdContext);
const column = useContext(BoardColumnContext);
const pipelineStageId = column?.columnDefinition.id || '';
const { enqueueSnackBar } = useSnackBar();

View File

@ -3,6 +3,7 @@ import { useRecoilCallback } from 'recoil';
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
import { boardCardIdsByColumnIdFamilyState } from '@/ui/board/states/boardCardIdsByColumnIdFamilyState';
import { boardColumnsState } from '@/ui/board/states/boardColumnsState';
import { savedBoardColumnsState } from '@/ui/board/states/savedBoardColumnsState';
import { BoardColumnDefinition } from '@/ui/board/types/BoardColumnDefinition';
import { genericEntitiesFamilyState } from '@/ui/editable-field/states/genericEntitiesFamilyState';
import { isThemeColor } from '@/ui/theme/utils/castStringAsThemeColor';
@ -114,11 +115,10 @@ export const useUpdateCompanyBoard = () =>
index: pipelineStage.index ?? 0,
};
});
if (!isDeeplyEqual(currentBoardColumns, newBoardColumns)) {
if (currentBoardColumns.length === 0) {
set(boardColumnsState, newBoardColumns);
set(savedBoardColumnsState, newBoardColumns);
}
for (const boardColumn of newBoardColumns) {
const boardCardIds = pipelineProgresses
.filter(

View File

@ -1,10 +1,10 @@
import React from 'react';
import React, { useContext } from 'react';
import styled from '@emotion/styled';
import { Tag } from '@/ui/tag/components/Tag';
import { ThemeColor } from '@/ui/theme/constants/colors';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { BoardColumnContext } from '../contexts/BoardColumnContext';
import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope';
import { BoardColumnMenu } from './BoardColumnMenu';
@ -53,28 +53,24 @@ const StyledNumChildren = styled.div`
`;
export type BoardColumnProps = {
color?: ThemeColor;
title: string;
onDelete?: (id: string) => void;
onTitleEdit: (title: string, color: string) => void;
totalAmount?: number;
children: React.ReactNode;
isFirstColumn: boolean;
numChildren: number;
stageId: string;
};
export const BoardColumn = ({
color,
title,
onDelete,
onTitleEdit,
totalAmount,
children,
isFirstColumn,
numChildren,
stageId,
}: BoardColumnProps) => {
const boardColumn = useContext(BoardColumnContext);
const [isBoardColumnMenuOpen, setIsBoardColumnMenuOpen] =
React.useState(false);
@ -95,10 +91,18 @@ export const BoardColumn = ({
setIsBoardColumnMenuOpen(false);
};
if (!boardColumn) return <></>;
const { isFirstColumn, columnDefinition } = boardColumn;
return (
<StyledColumn isFirstColumn={isFirstColumn}>
<StyledHeader onClick={handleTitleClick}>
<Tag color={color ?? 'gray'} text={title} />
<StyledHeader>
<Tag
onClick={handleTitleClick}
color={columnDefinition.colorCode ?? 'gray'}
text={columnDefinition.title}
/>
{!!totalAmount && <StyledAmount>${totalAmount}</StyledAmount>}
<StyledNumChildren>{numChildren}</StyledNumChildren>
</StyledHeader>
@ -107,8 +111,6 @@ export const BoardColumn = ({
onClose={handleClose}
onDelete={onDelete}
onTitleEdit={onTitleEdit}
title={title}
color={color ?? 'gray'}
stageId={stageId}
/>
)}

View File

@ -1,4 +1,4 @@
import { useCallback, useRef, useState } from 'react';
import { useCallback, useContext, useRef, useState } from 'react';
import styled from '@emotion/styled';
import { useRecoilState } from 'recoil';
import { Key } from 'ts-key-enum';
@ -7,50 +7,53 @@ import { useCreateCompanyProgress } from '@/companies/hooks/useCreateCompanyProg
import { useFilteredSearchCompanyQuery } from '@/companies/hooks/useFilteredSearchCompanyQuery';
import { StyledDropdownMenu } from '@/ui/dropdown/components/StyledDropdownMenu';
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
import { IconPencil, IconPlus, IconTrash } from '@/ui/icon';
import {
IconArrowLeft,
IconArrowRight,
IconPencil,
IconPlus,
IconTrash,
} from '@/ui/icon';
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar';
import { ThemeColor } from '@/ui/theme/constants/colors';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { BoardColumnContext } from '../contexts/BoardColumnContext';
import { useBoardColumns } from '../hooks/useBoardColumns';
import { boardColumnsState } from '../states/boardColumnsState';
import { BoardColumnHotkeyScope } from '../types/BoardColumnHotkeyScope';
import { BoardColumnEditTitleMenu } from './BoardColumnEditTitleMenu';
const StyledMenuContainer = styled.div`
position: absolute;
width: 200px;
z-index: 1;
`;
type OwnProps = {
color: ThemeColor;
type BoardColumnMenuProps = {
onClose: () => void;
onDelete?: (id: string) => void;
onTitleEdit: (title: string, color: string) => void;
stageId: string;
title: string;
};
type Menu = 'actions' | 'add' | 'title';
export const BoardColumnMenu = ({
color,
onClose,
onDelete,
onTitleEdit,
stageId,
title,
}: OwnProps) => {
}: BoardColumnMenuProps) => {
const [currentMenu, setCurrentMenu] = useState('actions');
const column = useContext(BoardColumnContext);
const [, setBoardColumns] = useRecoilState(boardColumnsState);
@ -58,6 +61,7 @@ export const BoardColumnMenu = ({
const { enqueueSnackBar } = useSnackBar();
const createCompanyProgress = useCreateCompanyProgress();
const { handleMoveBoardColumn } = useBoardColumns();
const handleCompanySelected = (
selectedCompany: EntityForSelect | null | undefined,
@ -125,6 +129,26 @@ export const BoardColumnMenu = ({
[],
);
if (!column) return <></>;
const { isFirstColumn, isLastColumn, columnDefinition } = column;
const handleColumnMoveLeft = () => {
closeMenu();
if (isFirstColumn) {
return;
}
handleMoveBoardColumn('left', columnDefinition);
};
const handleColumnMoveRight = () => {
closeMenu();
if (isLastColumn) {
return;
}
handleMoveBoardColumn('right', columnDefinition);
};
return (
<StyledMenuContainer ref={boardColumnMenuRef}>
<StyledDropdownMenu data-select-disable>
@ -135,6 +159,16 @@ export const BoardColumnMenu = ({
LeftIcon={IconPencil}
text="Rename"
/>
<MenuItem
LeftIcon={IconArrowLeft}
onClick={handleColumnMoveLeft}
text="Move left"
/>
<MenuItem
LeftIcon={IconArrowRight}
onClick={handleColumnMoveRight}
text="Move right"
/>
<MenuItem
onClick={handleDelete}
LeftIcon={IconTrash}
@ -149,10 +183,10 @@ export const BoardColumnMenu = ({
)}
{currentMenu === 'title' && (
<BoardColumnEditTitleMenu
color={color}
color={columnDefinition.colorCode ?? 'gray'}
onClose={closeMenu}
onTitleEdit={onTitleEdit}
title={title}
title={columnDefinition.title}
/>
)}
{currentMenu === 'add' && (

View File

@ -12,9 +12,12 @@ import { ViewBarContext } from '@/ui/view-bar/contexts/ViewBarContext';
import { currentViewIdScopedState } from '@/ui/view-bar/states/currentViewIdScopedState';
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
import { boardColumnsState } from '../states/boardColumnsState';
import { savedBoardCardFieldsFamilyState } from '../states/savedBoardCardFieldsFamilyState';
import { savedBoardColumnsState } from '../states/savedBoardColumnsState';
import { canPersistBoardCardFieldsScopedFamilySelector } from '../states/selectors/canPersistBoardCardFieldsScopedFamilySelector';
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
import { canPersistBoardColumnsSelector } from '../states/selectors/canPersistBoardColumnsSelector';
import type { BoardColumnDefinition } from '../types/BoardColumnDefinition';
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
import { BoardOptionsHotkeyScope } from '../types/BoardOptionsHotkeyScope';
@ -47,6 +50,8 @@ export const BoardHeader = ({ className, onStageAdd }: BoardHeaderProps) => {
viewId: currentViewId,
}),
);
const canPersistBoardColumns = useRecoilValue(canPersistBoardColumnsSelector);
const [boardCardFields, setBoardCardFields] = useRecoilScopedState(
boardCardFieldsScopedState,
BoardRecoilScopeContext,
@ -55,7 +60,15 @@ export const BoardHeader = ({ className, onStageAdd }: BoardHeaderProps) => {
savedBoardCardFieldsFamilyState(currentViewId),
);
const handleViewBarReset = () => setBoardCardFields(savedBoardCardFields);
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
const [, setSavedBoardColumns] = useRecoilState(savedBoardColumnsState);
const savedBoardColumns = useRecoilValue(savedBoardColumnsState);
const handleViewBarReset = () => {
setBoardCardFields(savedBoardCardFields);
setBoardColumns(savedBoardColumns);
};
const handleViewSelect = useRecoilCallback(
({ set, snapshot }) =>
@ -75,16 +88,21 @@ export const BoardHeader = ({ className, onStageAdd }: BoardHeaderProps) => {
if (canPersistBoardCardFields) {
setSavedBoardCardFields(boardCardFields);
}
if (canPersistBoardColumns) {
setSavedBoardColumns(boardColumns);
}
await onCurrentViewSubmit?.();
};
const canPersistView = canPersistBoardCardFields || canPersistBoardColumns;
return (
<RecoilScope CustomRecoilScopeContext={DropdownRecoilScopeContext}>
<ViewBarContext.Provider
value={{
...viewBarContextProps,
canPersistViewFields: canPersistBoardCardFields,
canPersistViewFields: canPersistView,
onCurrentViewSubmit: handleCurrentViewSubmit,
onViewBarReset: handleViewBarReset,
onViewSelect: handleViewSelect,

View File

@ -2,13 +2,13 @@ import { useCallback, useRef } from 'react';
import { getOperationName } from '@apollo/client/utilities';
import styled from '@emotion/styled';
import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
import { useRecoilState } from 'recoil';
import { useRecoilValue } from 'recoil';
import { GET_PIPELINE_PROGRESS } from '@/pipeline/graphql/queries/getPipelineProgress';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { BoardHeader } from '@/ui/board/components/BoardHeader';
import { StyledBoard } from '@/ui/board/components/StyledBoard';
import { BoardColumnIdContext } from '@/ui/board/contexts/BoardColumnIdContext';
import { BoardColumnContext } from '@/ui/board/contexts/BoardColumnContext';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useListenClickOutsideByClassName } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
@ -54,7 +54,7 @@ export const EntityBoard = ({
onColumnDelete,
onEditColumnTitle,
}: EntityBoardProps) => {
const [boardColumns] = useRecoilState(boardColumnsState);
const boardColumns = useRecoilValue(boardColumnsState);
const setCardSelected = useSetCardSelected();
const [updatePipelineProgressStage] =
@ -151,19 +151,26 @@ export const EntityBoard = ({
<StyledBoard ref={boardRef}>
<DragDropContext onDragEnd={onDragEnd}>
{sortedBoardColumns.map((column) => (
<BoardColumnIdContext.Provider value={column.id} key={column.id}>
<BoardColumnContext.Provider
key={column.id}
value={{
id: column.id,
columnDefinition: column,
isFirstColumn: column.index === 0,
isLastColumn: column.index === sortedBoardColumns.length - 1,
}}
>
<RecoilScope
CustomRecoilScopeContext={BoardColumnRecoilScopeContext}
key={column.id}
>
<EntityBoardColumn
boardOptions={boardOptions}
column={column}
onDelete={onColumnDelete}
onTitleEdit={onEditColumnTitle}
/>
</RecoilScope>
</BoardColumnIdContext.Provider>
</BoardColumnContext.Provider>
))}
</DragDropContext>
</StyledBoard>

View File

@ -5,8 +5,7 @@ import { useRecoilValue } from 'recoil';
import { BoardColumn } from '@/ui/board/components/BoardColumn';
import { BoardCardIdContext } from '@/ui/board/contexts/BoardCardIdContext';
import { BoardColumnIdContext } from '@/ui/board/contexts/BoardColumnIdContext';
import { BoardColumnDefinition } from '@/ui/board/types/BoardColumnDefinition';
import { BoardColumnContext } from '@/ui/board/contexts/BoardColumnContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { boardCardIdsByColumnIdFamilyState } from '../states/boardCardIdsByColumnIdFamilyState';
@ -29,13 +28,21 @@ const StyledColumnCardsContainer = styled.div`
flex-direction: column;
`;
type BoardColumnCardsContainerProps = {
children: React.ReactNode;
droppableProvided: DroppableProvided;
};
type EntityBoardColumnProps = {
boardOptions: BoardOptions;
onDelete?: (columnId: string) => void;
onTitleEdit: (columnId: string, title: string, color: string) => void;
};
const BoardColumnCardsContainer = ({
children,
droppableProvided,
}: {
children: React.ReactNode;
droppableProvided: DroppableProvided;
}) => {
}: BoardColumnCardsContainerProps) => {
return (
<StyledColumnCardsContainer
ref={droppableProvided?.innerRef}
@ -49,39 +56,34 @@ const BoardColumnCardsContainer = ({
export const EntityBoardColumn = ({
boardOptions,
column,
onDelete,
onTitleEdit,
}: {
boardOptions: BoardOptions;
column: BoardColumnDefinition;
onDelete?: (columnId: string) => void;
onTitleEdit: (columnId: string, title: string, color: string) => void;
}) => {
const boardColumnId = useContext(BoardColumnIdContext) ?? '';
}: EntityBoardColumnProps) => {
const column = useContext(BoardColumnContext);
const boardColumnId = column?.id || '';
const boardColumnTotal = useRecoilValue(
boardColumnTotalsFamilySelector(column.id),
boardColumnTotalsFamilySelector(boardColumnId),
);
const cardIds = useRecoilValue(
boardCardIdsByColumnIdFamilyState(boardColumnId ?? ''),
boardCardIdsByColumnIdFamilyState(boardColumnId),
);
const handleTitleEdit = (title: string, color: string) => {
onTitleEdit(boardColumnId, title, color);
};
if (!column) return <></>;
return (
<Droppable droppableId={column.id}>
{(droppableProvided) => (
<BoardColumn
onTitleEdit={handleTitleEdit}
onDelete={onDelete}
title={column.title}
color={column.colorCode}
totalAmount={boardColumnTotal}
isFirstColumn={column.index === 0}
numChildren={cardIds.length}
stageId={column.id}
>

View File

@ -0,0 +1,12 @@
import { createContext } from 'react';
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
type BoardColumn = {
id: string;
columnDefinition: BoardColumnDefinition;
isFirstColumn: boolean;
isLastColumn: boolean;
};
export const BoardColumnContext = createContext<BoardColumn | null>(null);

View File

@ -1,3 +0,0 @@
import { createContext } from 'react';
export const BoardColumnIdContext = createContext<string | null>(null);

View File

@ -0,0 +1,53 @@
import { useRecoilState } from 'recoil';
import { useMoveViewColumns } from '@/views/hooks/useMoveViewColumns';
import { useUpdatePipelineStageMutation } from '~/generated/graphql';
import { boardColumnsState } from '../states/boardColumnsState';
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
export const useBoardColumns = () => {
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
const { handleColumnMove } = useMoveViewColumns();
const [updatePipelineStageMutation] = useUpdatePipelineStageMutation();
const updatedPipelineStages = (stages: BoardColumnDefinition[]) => {
if (!stages.length) return;
return Promise.all(
stages.map((stage) =>
updatePipelineStageMutation({
variables: {
data: {
index: stage.index,
},
id: stage.id,
},
}),
),
);
};
const persistBoardColumns = async () => {
await updatedPipelineStages(boardColumns);
};
const handleMoveBoardColumn = (
direction: 'left' | 'right',
column: BoardColumnDefinition,
) => {
const currentColumnArrayIndex = boardColumns.findIndex(
(tableColumn) => tableColumn.id === column.id,
);
const columns = handleColumnMove(
direction,
currentColumnArrayIndex,
boardColumns,
);
setBoardColumns(columns);
};
return { handleMoveBoardColumn, persistBoardColumns };
};

View File

@ -0,0 +1,8 @@
import { atom } from 'recoil';
import { BoardColumnDefinition } from '../types/BoardColumnDefinition';
export const savedBoardColumnsState = atom<BoardColumnDefinition[]>({
key: 'savedBoardColumnsState',
default: [],
});

View File

@ -0,0 +1,12 @@
import { selector } from 'recoil';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { boardColumnsState } from '../boardColumnsState';
import { savedBoardColumnsState } from '../savedBoardColumnsState';
export const canPersistBoardColumnsSelector = selector<boolean>({
key: 'canPersistBoardCardFieldsScopedFamilySelector',
get: ({ get }) =>
!isDeeplyEqual(get(boardColumnsState), get(savedBoardColumnsState)),
});

View File

@ -22,11 +22,8 @@ export const TableColumnDropdownMenu = ({
isLastColumn,
primaryColumnKey,
}: EntityTableHeaderOptionsProps) => {
const {
handleColumnVisibilityChange,
handleColumnLeftMove,
handleColumnRightMove,
} = useTableColumns();
const { handleColumnVisibilityChange, handleMoveTableColumn } =
useTableColumns();
const { closeDropdownButton } = useDropdownButton({
dropdownId: ColumnHeadDropdownId,
@ -37,7 +34,7 @@ export const TableColumnDropdownMenu = ({
if (isFirstColumn) {
return;
}
handleColumnLeftMove(column);
handleMoveTableColumn('left', column);
};
const handleColumnMoveRight = () => {
@ -45,7 +42,7 @@ export const TableColumnDropdownMenu = ({
if (isLastColumn) {
return;
}
handleColumnRightMove(column);
handleMoveTableColumn('right', column);
};
const handleColumnVisibility = () => {

View File

@ -5,6 +5,7 @@ import { 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 { useMoveViewColumns } from '@/views/hooks/useMoveViewColumns';
import { TableContext } from '../contexts/TableContext';
import { TableRecoilScopeContext } from '../states/recoil-scope-contexts/TableRecoilScopeContext';
@ -32,6 +33,8 @@ export const useTableColumns = () => {
TableRecoilScopeContext,
);
const { handleColumnMove } = useMoveViewColumns();
const handleColumnsChange = useCallback(
async (columns: ColumnDefinition<ViewFieldMetadata>[]) => {
setSavedTableColumns(columns);
@ -71,54 +74,28 @@ export const useTableColumns = () => {
[tableColumnsByKey, tableColumns, handleColumnsChange],
);
const handleColumnMove = useCallback(
async (direction: string, column: ColumnDefinition<ViewFieldMetadata>) => {
const handleMoveTableColumn = useCallback(
(
direction: 'left' | 'right',
column: ColumnDefinition<ViewFieldMetadata>,
) => {
const currentColumnArrayIndex = tableColumns.findIndex(
(tableColumn) => tableColumn.key === column.key,
);
const targetColumnArrayIndex =
direction === 'left'
? currentColumnArrayIndex - 1
: currentColumnArrayIndex + 1;
const columns = handleColumnMove(
direction,
currentColumnArrayIndex,
tableColumns,
);
if (currentColumnArrayIndex >= 0) {
const currentColumn = tableColumns[currentColumnArrayIndex];
const targetColumn = tableColumns[targetColumnArrayIndex];
const newTableColumns = [...tableColumns];
newTableColumns[currentColumnArrayIndex] = {
...targetColumn,
index: currentColumn.index,
};
newTableColumns[targetColumnArrayIndex] = {
...currentColumn,
index: targetColumn.index,
};
await handleColumnsChange(newTableColumns);
}
setTableColumns(columns);
},
[tableColumns, handleColumnsChange],
);
const handleColumnLeftMove = useCallback(
(column: ColumnDefinition<ViewFieldMetadata>) => {
handleColumnMove('left', column);
},
[handleColumnMove],
);
const handleColumnRightMove = useCallback(
(column: ColumnDefinition<ViewFieldMetadata>) => {
handleColumnMove('right', column);
},
[handleColumnMove],
[tableColumns, setTableColumns, handleColumnMove],
);
return {
handleColumnVisibilityChange,
handleColumnLeftMove,
handleColumnRightMove,
handleMoveTableColumn,
handleColumnReorder,
handleColumnsChange,
};

View File

@ -5,7 +5,7 @@ import { availableBoardCardFieldsScopedState } from '@/ui/board/states/available
import { boardCardFieldsScopedState } from '@/ui/board/states/boardCardFieldsScopedState';
import { savedBoardCardFieldsFamilyState } from '@/ui/board/states/savedBoardCardFieldsFamilyState';
import { savedBoardCardFieldsByKeyFamilySelector } from '@/ui/board/states/selectors/savedBoardCardFieldsByKeyFamilySelector';
import {
import type {
ViewFieldDefinition,
ViewFieldMetadata,
} from '@/ui/editable-field/types/ViewField';

View File

@ -1,4 +1,5 @@
import { RecoilScopeContext } from '@/types/RecoilScopeContext';
import { useBoardColumns } from '@/ui/board/hooks/useBoardColumns';
import { boardCardFieldsScopedState } from '@/ui/board/states/boardCardFieldsScopedState';
import {
ViewFieldDefinition,
@ -50,6 +51,8 @@ export const useBoardViews = ({
RecoilScopeContext,
});
const { persistBoardColumns } = useBoardColumns();
const { createViewFilters, persistFilters } = useViewFilters({
skipFetch: isFetchingViews,
RecoilScopeContext,
@ -62,6 +65,7 @@ export const useBoardViews = ({
const submitCurrentView = async () => {
await persistCardFields();
await persistBoardColumns();
await persistFilters();
await persistSorts();
};

View File

@ -0,0 +1,35 @@
export const useMoveViewColumns = () => {
const handleColumnMove = <T extends { index: number }>(
direction: 'left' | 'right',
currentArrayindex: number,
targetArray: T[],
) => {
const targetArrayIndex =
direction === 'left' ? currentArrayindex - 1 : currentArrayindex + 1;
const targetArraySize = targetArray.length - 1;
if (
currentArrayindex >= 0 &&
targetArrayIndex >= 0 &&
currentArrayindex <= targetArraySize &&
targetArrayIndex <= targetArraySize
) {
const currentEntity = targetArray[currentArrayindex];
const targetEntity = targetArray[targetArrayIndex];
const newArray = [...targetArray];
newArray[currentArrayindex] = {
...targetEntity,
index: currentEntity.index,
};
newArray[targetArrayIndex] = {
...currentEntity,
index: targetEntity.index,
};
return newArray;
}
return targetArray;
};
return { handleColumnMove };
};