Persist update on board drag and drop (#328)

* chore: move dnd lib comment aligned with import

* feature: add onUpdate on board

* chore: remove multi entity pipelines

* feature: add pipelineProgressableType field

* feature: fetch progressableType in board

* feature: implement on update to persist progress change
This commit is contained in:
Sammy Teillet
2023-06-20 10:56:36 +02:00
committed by GitHub
parent 950a0b77fe
commit c120903a45
50 changed files with 308 additions and 40 deletions

View File

@ -4,9 +4,10 @@ import {
Draggable,
Droppable,
OnDragEndResponder,
} from '@hello-pangea/dnd';
} 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 {
BoardItemKey,
Column,
getOptimisticlyUpdatedBoard,
Items,
@ -17,8 +18,6 @@ import {
StyledColumn,
StyledColumnTitle,
} from '../../ui/components/board/BoardColumn';
// 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 { BoardItem } from '../../ui/components/board/BoardItem';
import { NewButton } from '../../ui/components/board/BoardNewButton';
@ -27,19 +26,29 @@ import { BoardCard } from './BoardCard';
type BoardProps = {
initialBoard: Column[];
items: Items;
onUpdate?: (itemKey: BoardItemKey, columnId: Column['id']) => Promise<void>;
};
export const Board = ({ initialBoard, items }: BoardProps) => {
export const Board = ({ initialBoard, items, onUpdate }: BoardProps) => {
const [board, setBoard] = useState<Column[]>(initialBoard);
const onDragEnd: OnDragEndResponder = useCallback(
(result) => {
async (result) => {
const newBoard = getOptimisticlyUpdatedBoard(board, result);
if (!newBoard) return;
setBoard(newBoard);
// TODO implement update board mutation
try {
const draggedEntityId = items[result.draggableId]?.id;
const destinationColumnId = result.destination?.droppableId;
draggedEntityId &&
destinationColumnId &&
onUpdate &&
(await onUpdate(draggedEntityId, destinationColumnId));
} catch (e) {
console.error(e);
}
},
[board],
[board, onUpdate, items],
);
return (

View File

@ -50,8 +50,9 @@ const StyledBoardCardBody = styled.div`
`;
export const BoardCard = ({ item }: { item: Person | Company }) => {
if (item.__typename === 'Person') return <PersonBoardCard person={item} />;
if (item.__typename === 'Company') return <CompanyBoardCard company={item} />;
if (item?.__typename === 'Person') return <PersonBoardCard person={item} />;
if (item?.__typename === 'Company')
return <CompanyBoardCard company={item} />;
// @todo return card skeleton
return null;
};

View File

@ -24,38 +24,49 @@ export const useBoard = () => {
const pipelineStages = pipelines.data?.findManyPipeline[0].pipelineStages;
const initialBoard: Column[] =
pipelineStages?.map((pipelineStage) => ({
id: pipelineStage.name,
id: pipelineStage.id,
title: pipelineStage.name,
colorCode: pipelineStage.color,
itemKeys:
pipelineStage.pipelineProgresses?.map(
(item) => `item-${item.progressableId}` as BoardItemKey,
(item) => item.progressableId as BoardItemKey,
) || [],
})) || [];
const pipelineEntityIds = pipelineStages?.reduce(
(acc, pipelineStage) => [
...acc,
...(pipelineStage.pipelineProgresses?.map(
(item) => item.progressableId,
) || []),
...(pipelineStage.pipelineProgresses?.map((item) => ({
entityId: item?.progressableId,
pipelineProgressId: item?.id,
})) || []),
],
[] as string[],
[] as { entityId: string; pipelineProgressId: string }[],
);
const pipelineEntityIdsMapper = (entityId: string) => {
const pipelineProgressId = pipelineEntityIds?.find(
(item) => item.entityId === entityId,
)?.pipelineProgressId;
return pipelineProgressId;
};
const pipelineEntityType: 'Person' | 'Company' | undefined =
pipelineStages?.[0].pipelineProgresses?.[0].progressableType;
pipelines.data?.findManyPipeline[0].pipelineProgressableType;
const query =
pipelineEntityType === 'Person' ? useGetPeopleQuery : useGetCompaniesQuery;
const entitiesQueryResult = query({
variables: { where: { id: { in: pipelineEntityIds } } },
variables: {
where: { id: { in: pipelineEntityIds?.map((item) => item.entityId) } },
},
});
const indexByIdReducer = (acc: Items, entity: { id: string }) => ({
...acc,
[`item-${entity.id}`]: entity,
[entity.id]: entity,
});
const items: Items | undefined = entitiesQueryResult.data
@ -71,5 +82,6 @@ export const useBoard = () => {
items,
loading: pipelines.loading || entitiesQueryResult.loading,
error: pipelines.error || entitiesQueryResult.error,
pipelineEntityIdsMapper,
};
};

View File

@ -2,10 +2,12 @@ import { gql } from '@apollo/client';
export const GET_PIPELINES = gql`
query GetPipelines {
findManyPipeline(skip: 1) {
findManyPipeline {
id
name
pipelineProgressableType
pipelineStages {
id
name
color
pipelineProgresses {
@ -17,3 +19,14 @@ export const GET_PIPELINES = gql`
}
}
`;
export const UPDATE_PIPELINE_STAGE = gql`
mutation UpdateOnePipelineProgress($id: String, $pipelineStageId: String) {
updateOnePipelineProgress(
where: { id: $id }
data: { pipelineStage: { connect: { id: $pipelineStageId } } }
) {
id
}
}
`;

View File

@ -1,5 +1,5 @@
import styled from '@emotion/styled';
import { DropResult } from '@hello-pangea/dnd';
import { DropResult } 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
export const StyledBoard = styled.div`
display: flex;
@ -7,7 +7,7 @@ export const StyledBoard = styled.div`
height: 100%;
`;
export type BoardItemKey = `item-${number | string}`;
export type BoardItemKey = string;
export type Item = any & { id: string };
export interface Items {
[key: string]: Item;

View File

@ -1,6 +1,6 @@
import React from 'react';
import styled from '@emotion/styled';
import { DroppableProvided } from '@hello-pangea/dnd';
import { DroppableProvided } 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
export const StyledColumn = styled.div`
background-color: ${({ theme }) => theme.primaryBackground};

View File

@ -1,5 +1,5 @@
import styled from '@emotion/styled';
import { DraggableProvided } from '@hello-pangea/dnd';
import { DraggableProvided } 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
const StyledCard = styled.div`
background-color: ${({ theme }) => theme.secondaryBackground};