feature: ability to delete task groups
This commit is contained in:
@ -33,6 +33,7 @@
|
||||
"react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }],
|
||||
"react/prop-types": 0,
|
||||
"react/jsx-props-no-spreading": "off",
|
||||
"no-param-reassign": "off",
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
|
@ -39,6 +39,7 @@
|
||||
"graphql": "^15.0.0",
|
||||
"graphql-tag": "^2.10.3",
|
||||
"history": "^4.10.1",
|
||||
"immer": "^6.0.3",
|
||||
"lodash": "^4.17.15",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.12.0",
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import produce from 'immer';
|
||||
import styled from 'styled-components/macro';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import {
|
||||
@ -9,6 +10,7 @@ import {
|
||||
useUpdateTaskLocationMutation,
|
||||
useUpdateTaskGroupLocationMutation,
|
||||
useCreateTaskGroupMutation,
|
||||
useDeleteTaskGroupMutation,
|
||||
} from 'shared/generated/graphql';
|
||||
|
||||
import Navbar from 'App/Navbar';
|
||||
@ -79,6 +81,26 @@ const Project = () => {
|
||||
const [quickCardEditor, setQuickCardEditor] = useState(initialQuickCardEditorState);
|
||||
const [updateTaskLocation] = useUpdateTaskLocationMutation();
|
||||
const [updateTaskGroupLocation] = useUpdateTaskGroupLocationMutation();
|
||||
const [deleteTaskGroup] = useDeleteTaskGroupMutation({
|
||||
onCompleted: deletedTaskGroupData => {
|
||||
const nextState = produce(listsData, (draftState: State) => {
|
||||
delete draftState.columns[deletedTaskGroupData.deleteTaskGroup.taskGroup.taskGroupID];
|
||||
const filteredTasks = Object.keys(listsData.tasks)
|
||||
.filter(
|
||||
taskID =>
|
||||
listsData.tasks[taskID].taskGroup.taskGroupID !==
|
||||
deletedTaskGroupData.deleteTaskGroup.taskGroup.taskGroupID,
|
||||
)
|
||||
.reduce((obj: TaskState, key: string) => {
|
||||
obj[key] = listsData.tasks[key];
|
||||
return obj;
|
||||
}, {});
|
||||
draftState.tasks = filteredTasks;
|
||||
});
|
||||
|
||||
setListsData(nextState);
|
||||
},
|
||||
});
|
||||
const [createTaskGroup] = useCreateTaskGroupMutation({
|
||||
onCompleted: newTaskGroupData => {
|
||||
const newListsData = {
|
||||
@ -167,9 +189,14 @@ const Project = () => {
|
||||
setListsData(newListsData);
|
||||
},
|
||||
});
|
||||
const onCardDrop = (droppedTask: any) => {
|
||||
const onCardDrop = (droppedTask: Task) => {
|
||||
console.log(droppedTask);
|
||||
updateTaskLocation({
|
||||
variables: { taskID: droppedTask.taskID, taskGroupID: droppedTask.taskGroupID, position: droppedTask.position },
|
||||
variables: {
|
||||
taskID: droppedTask.taskID,
|
||||
taskGroupID: droppedTask.taskGroup.taskGroupID,
|
||||
position: droppedTask.position,
|
||||
},
|
||||
});
|
||||
const newState = {
|
||||
...listsData,
|
||||
@ -229,6 +256,14 @@ const Project = () => {
|
||||
</TitleWrapper>
|
||||
<Board>
|
||||
<Lists
|
||||
onExtraMenuOpen={(taskGroupID, pos, size) => {
|
||||
setPopupData({
|
||||
isOpen: true,
|
||||
left: pos.left,
|
||||
top: pos.top + size.height + 5,
|
||||
taskGroupID,
|
||||
});
|
||||
}}
|
||||
onQuickEditorOpen={onQuickEditorOpen}
|
||||
onCardCreate={onCardCreate}
|
||||
{...listsData}
|
||||
@ -271,7 +306,13 @@ const Project = () => {
|
||||
onClose={() => setPopupData(initialPopupState)}
|
||||
left={popupData.left}
|
||||
>
|
||||
<ListActions taskGroupID={popupData.taskGroupID} />
|
||||
<ListActions
|
||||
taskGroupID={popupData.taskGroupID}
|
||||
onArchiveTaskGroup={taskGroupID => {
|
||||
deleteTaskGroup({ variables: { taskGroupID } });
|
||||
setPopupData(initialPopupState);
|
||||
}}
|
||||
/>
|
||||
</PopupMenu>
|
||||
)}
|
||||
</>
|
||||
|
5
web/src/citadel.d.ts
vendored
5
web/src/citadel.d.ts
vendored
@ -1,3 +1,8 @@
|
||||
interface DraggableElement {
|
||||
id: string;
|
||||
position: number;
|
||||
}
|
||||
|
||||
type ContextMenuEvent = {
|
||||
left: number;
|
||||
top: number;
|
||||
|
@ -1,6 +1,8 @@
|
||||
import styled, { css } from 'styled-components';
|
||||
import TextareaAutosize from 'react-autosize-textarea/lib';
|
||||
|
||||
export const Container = styled.div``;
|
||||
|
||||
export const Wrapper = styled.div<{ editorOpen: boolean }>`
|
||||
display: inline-block;
|
||||
background-color: hsla(0, 0%, 100%, 0.24);
|
||||
|
@ -3,6 +3,7 @@ import { Plus, Cross } from 'shared/icons';
|
||||
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
||||
|
||||
import {
|
||||
Container,
|
||||
Wrapper,
|
||||
Placeholder,
|
||||
AddIconWrapper,
|
||||
@ -79,26 +80,28 @@ const AddList: React.FC<AddListProps> = ({ onSave }) => {
|
||||
useOnOutsideClick($wrapperRef, editorOpen, onOutsideClick, null);
|
||||
|
||||
return (
|
||||
<Wrapper
|
||||
ref={$wrapperRef}
|
||||
editorOpen={editorOpen}
|
||||
onClick={() => {
|
||||
if (!editorOpen) {
|
||||
setEditorOpen(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{editorOpen ? (
|
||||
<NameEditor onCancel={() => setEditorOpen(false)} onSave={onSave} />
|
||||
) : (
|
||||
<Placeholder>
|
||||
<AddIconWrapper>
|
||||
<Plus size={12} color="#c2c6dc" />
|
||||
</AddIconWrapper>
|
||||
Add another list
|
||||
</Placeholder>
|
||||
)}
|
||||
</Wrapper>
|
||||
<Container>
|
||||
<Wrapper
|
||||
ref={$wrapperRef}
|
||||
editorOpen={editorOpen}
|
||||
onClick={() => {
|
||||
if (!editorOpen) {
|
||||
setEditorOpen(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{editorOpen ? (
|
||||
<NameEditor onCancel={() => setEditorOpen(false)} onSave={onSave} />
|
||||
) : (
|
||||
<Placeholder>
|
||||
<AddIconWrapper>
|
||||
<Plus size={12} color="#c2c6dc" />
|
||||
</AddIconWrapper>
|
||||
Add another list
|
||||
</Placeholder>
|
||||
)}
|
||||
</Wrapper>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import ListActions from '.';
|
||||
|
||||
export default {
|
||||
@ -13,5 +14,5 @@ export default {
|
||||
};
|
||||
|
||||
export const Default = () => {
|
||||
return <ListActions taskGroupID="1" />;
|
||||
return <ListActions taskGroupID="1" onArchiveTaskGroup={action('on archive task group')} />;
|
||||
};
|
||||
|
@ -3,8 +3,10 @@ import { ListActionsWrapper, ListActionItemWrapper, ListActionItem, ListSeparato
|
||||
|
||||
type Props = {
|
||||
taskGroupID: string;
|
||||
|
||||
onArchiveTaskGroup: (taskGroupID: string) => void;
|
||||
};
|
||||
const LabelManager = ({ taskGroupID }: Props) => {
|
||||
const LabelManager: React.FC<Props> = ({ taskGroupID, onArchiveTaskGroup }) => {
|
||||
return (
|
||||
<>
|
||||
<ListActionsWrapper>
|
||||
@ -38,7 +40,7 @@ const LabelManager = ({ taskGroupID }: Props) => {
|
||||
</ListActionsWrapper>
|
||||
<ListSeparator />
|
||||
<ListActionsWrapper>
|
||||
<ListActionItemWrapper>
|
||||
<ListActionItemWrapper onClick={() => onArchiveTaskGroup(taskGroupID)}>
|
||||
<ListActionItem>Archive This List</ListActionItem>
|
||||
</ListActionItemWrapper>
|
||||
</ListActionsWrapper>
|
||||
|
@ -70,25 +70,28 @@ export const Default = () => {
|
||||
...listsData,
|
||||
tasks: {
|
||||
...listsData.tasks,
|
||||
[droppedTask.id]: droppedTask,
|
||||
[droppedTask.taskGroupID]: droppedTask,
|
||||
},
|
||||
};
|
||||
console.log(newState);
|
||||
setListsData(newState);
|
||||
};
|
||||
const onListDrop = (droppedColumn: any) => {
|
||||
console.log(droppedColumn);
|
||||
const newState = {
|
||||
...listsData,
|
||||
columns: {
|
||||
...listsData.columns,
|
||||
[droppedColumn.id]: droppedColumn,
|
||||
[droppedColumn.taskGroupID]: droppedColumn,
|
||||
},
|
||||
};
|
||||
console.log(newState);
|
||||
setListsData(newState);
|
||||
};
|
||||
return (
|
||||
<Lists
|
||||
{...listsData}
|
||||
onExtraMenuOpen={action('extra menu open')}
|
||||
onQuickEditorOpen={action('card composer open')}
|
||||
onCardDrop={onCardDrop}
|
||||
onListDrop={onListDrop}
|
||||
@ -203,6 +206,7 @@ export const ListsWithManyList = () => {
|
||||
onCardDrop={onCardDrop}
|
||||
onListDrop={onListDrop}
|
||||
onCreateList={action('create list')}
|
||||
onExtraMenuOpen={action('extra menu open')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,7 +1,6 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Container = styled.div`
|
||||
flex-grow: 1;
|
||||
user-select: none;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 8px;
|
||||
@ -10,4 +9,7 @@ export const Container = styled.div`
|
||||
padding-bottom: 8px;
|
||||
`;
|
||||
|
||||
export const BoardWrapper = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
export default Container;
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
getAfterDropDraggableList,
|
||||
} from 'shared/utils/draggables';
|
||||
|
||||
import { Container } from './Styles';
|
||||
import { Container, BoardWrapper } from './Styles';
|
||||
|
||||
interface Columns {
|
||||
[key: string]: TaskGroup;
|
||||
@ -23,25 +23,56 @@ interface Tasks {
|
||||
type Props = {
|
||||
columns: Columns;
|
||||
tasks: Tasks;
|
||||
onCardDrop: any;
|
||||
onListDrop: any;
|
||||
onCardDrop: (task: Task) => void;
|
||||
onListDrop: (taskGroup: TaskGroup) => void;
|
||||
onCardCreate: (taskGroupID: string, name: string) => void;
|
||||
onQuickEditorOpen: (e: ContextMenuEvent) => void;
|
||||
onCreateList: (listName: string) => void;
|
||||
onExtraMenuOpen: (taskGroupID: string, pos: ElementPosition, size: ElementSize) => void;
|
||||
};
|
||||
|
||||
const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEditorOpen, onCreateList }: Props) => {
|
||||
const Lists: React.FC<Props> = ({
|
||||
columns,
|
||||
tasks,
|
||||
onCardDrop,
|
||||
onListDrop,
|
||||
onCardCreate,
|
||||
onQuickEditorOpen,
|
||||
onCreateList,
|
||||
onExtraMenuOpen,
|
||||
}) => {
|
||||
const onDragEnd = ({ draggableId, source, destination, type }: DropResult) => {
|
||||
if (typeof destination === 'undefined') return;
|
||||
if (!isPositionChanged(source, destination)) return;
|
||||
|
||||
const isList = type === 'column';
|
||||
const isSameList = destination.droppableId === source.droppableId;
|
||||
const droppedDraggable = isList ? columns[draggableId] : tasks[draggableId];
|
||||
const droppedDraggable: DraggableElement = isList
|
||||
? {
|
||||
id: draggableId,
|
||||
position: columns[draggableId].position,
|
||||
}
|
||||
: {
|
||||
id: draggableId,
|
||||
position: tasks[draggableId].position,
|
||||
};
|
||||
const beforeDropDraggables = isList
|
||||
? getSortedDraggables(Object.values(columns))
|
||||
: getSortedDraggables(Object.values(tasks).filter((t: any) => t.taskGroupID === destination.droppableId));
|
||||
? getSortedDraggables(
|
||||
Object.values(columns).map(column => {
|
||||
return { id: column.taskGroupID, position: column.position };
|
||||
}),
|
||||
)
|
||||
: getSortedDraggables(
|
||||
Object.values(tasks)
|
||||
.filter((t: any) => t.taskGroup.taskGroupID === destination.droppableId)
|
||||
.map(task => {
|
||||
return { id: task.taskID, position: task.position };
|
||||
}),
|
||||
);
|
||||
|
||||
console.log(beforeDropDraggables);
|
||||
console.log(destination);
|
||||
console.log(droppedDraggable);
|
||||
const afterDropDraggables = getAfterDropDraggableList(
|
||||
beforeDropDraggables,
|
||||
droppedDraggable,
|
||||
@ -49,16 +80,19 @@ const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEd
|
||||
isSameList,
|
||||
destination,
|
||||
);
|
||||
console.log(afterDropDraggables);
|
||||
const newPosition = getNewDraggablePosition(afterDropDraggables, destination.index);
|
||||
|
||||
if (isList) {
|
||||
const droppedList = columns[droppedDraggable.id];
|
||||
onListDrop({
|
||||
...droppedDraggable,
|
||||
...droppedList,
|
||||
position: newPosition,
|
||||
});
|
||||
} else {
|
||||
const droppedCard = tasks[droppedDraggable.id];
|
||||
const newCard = {
|
||||
...droppedDraggable,
|
||||
...droppedCard,
|
||||
position: newPosition,
|
||||
taskGroupID: destination.droppableId,
|
||||
};
|
||||
@ -66,89 +100,96 @@ const Lists = ({ columns, tasks, onCardDrop, onListDrop, onCardCreate, onQuickEd
|
||||
}
|
||||
};
|
||||
|
||||
const orderedColumns = getSortedDraggables(Object.values(columns));
|
||||
const orderedColumns = getSortedDraggables(
|
||||
Object.values(columns).map(column => {
|
||||
return { id: column.taskGroupID, position: column.position };
|
||||
}),
|
||||
);
|
||||
|
||||
const [currentComposer, setCurrentComposer] = useState('');
|
||||
return (
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<Droppable direction="horizontal" type="column" droppableId="root">
|
||||
{provided => (
|
||||
<Container {...provided.droppableProps} ref={provided.innerRef}>
|
||||
{orderedColumns.map((column: TaskGroup, index: number) => {
|
||||
const columnCards = getSortedDraggables(
|
||||
Object.values(tasks).filter((t: Task) => t.taskGroup.taskGroupID === column.taskGroupID),
|
||||
);
|
||||
return (
|
||||
<Draggable draggableId={column.taskGroupID} key={column.taskGroupID} index={index}>
|
||||
{columnDragProvided => (
|
||||
<List
|
||||
id={column.taskGroupID}
|
||||
name={column.name}
|
||||
key={column.taskGroupID}
|
||||
onOpenComposer={id => setCurrentComposer(id)}
|
||||
isComposerOpen={currentComposer === column.taskGroupID}
|
||||
onSaveName={name => console.log(name)}
|
||||
index={index}
|
||||
tasks={columnCards}
|
||||
ref={columnDragProvided.innerRef}
|
||||
wrapperProps={columnDragProvided.draggableProps}
|
||||
headerProps={columnDragProvided.dragHandleProps}
|
||||
onExtraMenuOpen={(taskGroupID, pos, size) => console.log(taskGroupID, pos, size)}
|
||||
>
|
||||
<Droppable type="tasks" droppableId={column.taskGroupID}>
|
||||
{columnDropProvided => (
|
||||
<ListCards ref={columnDropProvided.innerRef} {...columnDropProvided.droppableProps}>
|
||||
{columnCards.map((task: Task, taskIndex: any) => {
|
||||
return (
|
||||
<Draggable key={task.taskID} draggableId={task.taskID} index={taskIndex}>
|
||||
{taskProvided => {
|
||||
return (
|
||||
<Card
|
||||
wrapperProps={{
|
||||
...taskProvided.draggableProps,
|
||||
...taskProvided.dragHandleProps,
|
||||
}}
|
||||
ref={taskProvided.innerRef}
|
||||
taskID={task.taskID}
|
||||
taskGroupID={column.taskGroupID}
|
||||
description=""
|
||||
title={task.name}
|
||||
labels={task.labels}
|
||||
onClick={e => console.log(e)}
|
||||
onContextMenu={onQuickEditorOpen}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{columnDropProvided.placeholder}
|
||||
<BoardWrapper>
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<Droppable direction="horizontal" type="column" droppableId="root">
|
||||
{provided => (
|
||||
<Container {...provided.droppableProps} ref={provided.innerRef}>
|
||||
{orderedColumns.map((columnDraggable, index: number) => {
|
||||
const column = columns[columnDraggable.id];
|
||||
const columnCards = Object.values(tasks)
|
||||
.filter((t: Task) => t.taskGroup.taskGroupID === column.taskGroupID)
|
||||
.sort((a, b) => a.position - b.position);
|
||||
return (
|
||||
<Draggable draggableId={column.taskGroupID} key={column.taskGroupID} index={index}>
|
||||
{columnDragProvided => (
|
||||
<List
|
||||
id={column.taskGroupID}
|
||||
name={column.name}
|
||||
key={column.taskGroupID}
|
||||
onOpenComposer={id => setCurrentComposer(id)}
|
||||
isComposerOpen={currentComposer === column.taskGroupID}
|
||||
onSaveName={name => console.log(name)}
|
||||
index={index}
|
||||
tasks={columnCards}
|
||||
ref={columnDragProvided.innerRef}
|
||||
wrapperProps={columnDragProvided.draggableProps}
|
||||
headerProps={columnDragProvided.dragHandleProps}
|
||||
onExtraMenuOpen={onExtraMenuOpen}
|
||||
>
|
||||
<Droppable type="tasks" droppableId={column.taskGroupID}>
|
||||
{columnDropProvided => (
|
||||
<ListCards ref={columnDropProvided.innerRef} {...columnDropProvided.droppableProps}>
|
||||
{columnCards.map((task: Task, taskIndex: any) => {
|
||||
return (
|
||||
<Draggable key={task.taskID} draggableId={task.taskID} index={taskIndex}>
|
||||
{taskProvided => {
|
||||
return (
|
||||
<Card
|
||||
wrapperProps={{
|
||||
...taskProvided.draggableProps,
|
||||
...taskProvided.dragHandleProps,
|
||||
}}
|
||||
ref={taskProvided.innerRef}
|
||||
taskID={task.taskID}
|
||||
taskGroupID={column.taskGroupID}
|
||||
description=""
|
||||
title={task.name}
|
||||
labels={task.labels}
|
||||
onClick={e => console.log(e)}
|
||||
onContextMenu={onQuickEditorOpen}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{columnDropProvided.placeholder}
|
||||
|
||||
{currentComposer === column.taskGroupID && (
|
||||
<CardComposer
|
||||
onClose={() => {
|
||||
setCurrentComposer('');
|
||||
}}
|
||||
onCreateCard={name => {
|
||||
onCardCreate(column.taskGroupID, name);
|
||||
}}
|
||||
isOpen
|
||||
/>
|
||||
)}
|
||||
</ListCards>
|
||||
)}
|
||||
</Droppable>
|
||||
</List>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{provided.placeholder}
|
||||
<AddList onSave={onCreateList} />
|
||||
</Container>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
{currentComposer === column.taskGroupID && (
|
||||
<CardComposer
|
||||
onClose={() => {
|
||||
setCurrentComposer('');
|
||||
}}
|
||||
onCreateCard={name => {
|
||||
onCardCreate(column.taskGroupID, name);
|
||||
}}
|
||||
isOpen
|
||||
/>
|
||||
)}
|
||||
</ListCards>
|
||||
)}
|
||||
</Droppable>
|
||||
</List>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{provided.placeholder}
|
||||
</Container>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
<AddList onSave={onCreateList} />
|
||||
</BoardWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -77,7 +77,7 @@ export const ListActionsPopup = () => {
|
||||
onClose={() => setPopupData(initalState)}
|
||||
left={popupData.left}
|
||||
>
|
||||
<ListActions taskGroupID="1" />
|
||||
<ListActions taskGroupID="1" onArchiveTaskGroup={action('archive task group')} />
|
||||
</PopupMenu>
|
||||
)}
|
||||
<button
|
||||
|
@ -180,6 +180,17 @@ export type NewTaskGroupLocation = {
|
||||
position: Scalars['Float'];
|
||||
};
|
||||
|
||||
export type DeleteTaskGroupInput = {
|
||||
taskGroupID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type DeleteTaskGroupPayload = {
|
||||
__typename?: 'DeleteTaskGroupPayload';
|
||||
ok: Scalars['Boolean'];
|
||||
affectedRows: Scalars['Int'];
|
||||
taskGroup: TaskGroup;
|
||||
};
|
||||
|
||||
export type Mutation = {
|
||||
__typename?: 'Mutation';
|
||||
createRefreshToken: RefreshToken;
|
||||
@ -189,11 +200,12 @@ export type Mutation = {
|
||||
createProject: Project;
|
||||
createTaskGroup: TaskGroup;
|
||||
updateTaskGroupLocation: TaskGroup;
|
||||
deleteTaskGroup: DeleteTaskGroupPayload;
|
||||
createTask: Task;
|
||||
updateTaskLocation: Task;
|
||||
logoutUser: Scalars['Boolean'];
|
||||
updateTaskName: Task;
|
||||
deleteTask: DeleteTaskPayload;
|
||||
logoutUser: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
|
||||
@ -232,6 +244,11 @@ export type MutationUpdateTaskGroupLocationArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteTaskGroupArgs = {
|
||||
input: DeleteTaskGroupInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateTaskArgs = {
|
||||
input: NewTask;
|
||||
};
|
||||
@ -242,11 +259,6 @@ export type MutationUpdateTaskLocationArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationLogoutUserArgs = {
|
||||
input: LogoutUser;
|
||||
};
|
||||
|
||||
|
||||
export type MutationUpdateTaskNameArgs = {
|
||||
input: UpdateTaskName;
|
||||
};
|
||||
@ -256,6 +268,11 @@ export type MutationDeleteTaskArgs = {
|
||||
input: DeleteTaskInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationLogoutUserArgs = {
|
||||
input: LogoutUser;
|
||||
};
|
||||
|
||||
export type CreateTaskMutationVariables = {
|
||||
taskGroupID: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
@ -303,6 +320,27 @@ export type DeleteTaskMutation = (
|
||||
) }
|
||||
);
|
||||
|
||||
export type DeleteTaskGroupMutationVariables = {
|
||||
taskGroupID: Scalars['UUID'];
|
||||
};
|
||||
|
||||
|
||||
export type DeleteTaskGroupMutation = (
|
||||
{ __typename?: 'Mutation' }
|
||||
& { deleteTaskGroup: (
|
||||
{ __typename?: 'DeleteTaskGroupPayload' }
|
||||
& Pick<DeleteTaskGroupPayload, 'ok' | 'affectedRows'>
|
||||
& { taskGroup: (
|
||||
{ __typename?: 'TaskGroup' }
|
||||
& Pick<TaskGroup, 'taskGroupID'>
|
||||
& { tasks: Array<(
|
||||
{ __typename?: 'Task' }
|
||||
& Pick<Task, 'taskID' | 'name'>
|
||||
)> }
|
||||
) }
|
||||
) }
|
||||
);
|
||||
|
||||
export type FindProjectQueryVariables = {
|
||||
projectId: Scalars['String'];
|
||||
};
|
||||
@ -494,6 +532,46 @@ export function useDeleteTaskMutation(baseOptions?: ApolloReactHooks.MutationHoo
|
||||
export type DeleteTaskMutationHookResult = ReturnType<typeof useDeleteTaskMutation>;
|
||||
export type DeleteTaskMutationResult = ApolloReactCommon.MutationResult<DeleteTaskMutation>;
|
||||
export type DeleteTaskMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteTaskMutation, DeleteTaskMutationVariables>;
|
||||
export const DeleteTaskGroupDocument = gql`
|
||||
mutation deleteTaskGroup($taskGroupID: UUID!) {
|
||||
deleteTaskGroup(input: {taskGroupID: $taskGroupID}) {
|
||||
ok
|
||||
affectedRows
|
||||
taskGroup {
|
||||
taskGroupID
|
||||
tasks {
|
||||
taskID
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type DeleteTaskGroupMutationFn = ApolloReactCommon.MutationFunction<DeleteTaskGroupMutation, DeleteTaskGroupMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useDeleteTaskGroupMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useDeleteTaskGroupMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useDeleteTaskGroupMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [deleteTaskGroupMutation, { data, loading, error }] = useDeleteTaskGroupMutation({
|
||||
* variables: {
|
||||
* taskGroupID: // value for 'taskGroupID'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useDeleteTaskGroupMutation(baseOptions?: ApolloReactHooks.MutationHookOptions<DeleteTaskGroupMutation, DeleteTaskGroupMutationVariables>) {
|
||||
return ApolloReactHooks.useMutation<DeleteTaskGroupMutation, DeleteTaskGroupMutationVariables>(DeleteTaskGroupDocument, baseOptions);
|
||||
}
|
||||
export type DeleteTaskGroupMutationHookResult = ReturnType<typeof useDeleteTaskGroupMutation>;
|
||||
export type DeleteTaskGroupMutationResult = ApolloReactCommon.MutationResult<DeleteTaskGroupMutation>;
|
||||
export type DeleteTaskGroupMutationOptions = ApolloReactCommon.BaseMutationOptions<DeleteTaskGroupMutation, DeleteTaskGroupMutationVariables>;
|
||||
export const FindProjectDocument = gql`
|
||||
query findProject($projectId: String!) {
|
||||
findProject(input: {projectId: $projectId}) {
|
||||
|
13
web/src/shared/graphql/deleteTaskGroup.graphqls
Normal file
13
web/src/shared/graphql/deleteTaskGroup.graphqls
Normal file
@ -0,0 +1,13 @@
|
||||
mutation deleteTaskGroup($taskGroupID: UUID!) {
|
||||
deleteTaskGroup(input: { taskGroupID: $taskGroupID }) {
|
||||
ok
|
||||
affectedRows
|
||||
taskGroup {
|
||||
taskGroupID
|
||||
tasks {
|
||||
taskID
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
export const moveItemWithinArray = (arr: any, item: any, newIndex: number) => {
|
||||
const arrClone = [...arr];
|
||||
const oldIndex = arrClone.indexOf(item);
|
||||
arrClone.splice(newIndex, 0, arrClone.splice(oldIndex, 1)[0]);
|
||||
return arrClone;
|
||||
};
|
||||
|
||||
export const insertItemIntoArray = (arr: any, item: any, index: number) => {
|
||||
const arrClone = [...arr];
|
||||
arrClone.splice(index, 0, item);
|
||||
return arrClone;
|
||||
};
|
||||
|
||||
export const updateArrayItemById = (arr: any, itemId: any, fields: any) => {
|
||||
const arrClone = [...arr];
|
||||
const item = arrClone.find(({ id }) => id === itemId);
|
||||
if (item) {
|
||||
const itemIndex = arrClone.indexOf(item);
|
||||
arrClone.splice(itemIndex, 1, { ...item, ...fields });
|
||||
}
|
||||
return arrClone;
|
||||
};
|
@ -1,26 +1,58 @@
|
||||
import { moveItemWithinArray, insertItemIntoArray } from 'shared/utils/arrays';
|
||||
import { DraggableLocation } from 'react-beautiful-dnd';
|
||||
|
||||
export const getNewDraggablePosition = (afterDropDraggables: any, draggableIndex: any) => {
|
||||
export const moveItemWithinArray = (arr: Array<DraggableElement>, item: DraggableElement, newIndex: number) => {
|
||||
const arrClone = [...arr];
|
||||
const oldIndex = arrClone.findIndex(i => i.id === item.id);
|
||||
arrClone.splice(newIndex, 0, arrClone.splice(oldIndex, 1)[0]);
|
||||
return arrClone;
|
||||
};
|
||||
|
||||
export const insertItemIntoArray = (arr: Array<DraggableElement>, item: DraggableElement, index: number) => {
|
||||
const arrClone = [...arr];
|
||||
arrClone.splice(index, 0, item);
|
||||
return arrClone;
|
||||
};
|
||||
|
||||
export const updateArrayItemById = (arr: Array<DraggableElement>, itemId: string, fields: any) => {
|
||||
const arrClone = [...arr];
|
||||
const item = arrClone.find(({ id }) => id === itemId);
|
||||
if (item) {
|
||||
const itemIndex = arrClone.indexOf(item);
|
||||
arrClone.splice(itemIndex, 1, { ...item, ...fields });
|
||||
}
|
||||
return arrClone;
|
||||
};
|
||||
|
||||
export const getNewDraggablePosition = (afterDropDraggables: Array<DraggableElement>, draggableIndex: number) => {
|
||||
const prevDraggable = afterDropDraggables[draggableIndex - 1];
|
||||
const nextDraggable = afterDropDraggables[draggableIndex + 1];
|
||||
if (!prevDraggable && !nextDraggable) {
|
||||
return 1;
|
||||
return 65535;
|
||||
}
|
||||
if (!prevDraggable) {
|
||||
return nextDraggable.position - 1;
|
||||
console.log(
|
||||
`in front of list [n/a : ${nextDraggable.id}]: ${nextDraggable.position} / 2.0 = ${nextDraggable.position / 2.0}`,
|
||||
);
|
||||
return nextDraggable.position / 2.0;
|
||||
}
|
||||
if (!nextDraggable) {
|
||||
return prevDraggable.position + 1;
|
||||
console.log(
|
||||
`end of list [${prevDraggable.id} : n/a] : ${prevDraggable.position} * 2.0 = ${prevDraggable.position * 2.0}`,
|
||||
);
|
||||
return prevDraggable.position * 2.0;
|
||||
}
|
||||
const newPos = (prevDraggable.position + nextDraggable.position) / 2.0;
|
||||
console.log(
|
||||
`middle of two cards [${prevDraggable.id} : ${nextDraggable.id}] : ${prevDraggable.position} + ${nextDraggable.position} / 2.0 = ${newPos}`,
|
||||
);
|
||||
return newPos;
|
||||
};
|
||||
|
||||
export const getSortedDraggables = (draggables: any) => {
|
||||
export const getSortedDraggables = (draggables: Array<DraggableElement>) => {
|
||||
return draggables.sort((a: any, b: any) => a.position - b.position);
|
||||
};
|
||||
|
||||
export const isPositionChanged = (source: any, destination: any) => {
|
||||
export const isPositionChanged = (source: DraggableLocation, destination: DraggableLocation) => {
|
||||
if (!destination) return false;
|
||||
const isSameList = destination.droppableId === source.droppableId;
|
||||
const isSamePosition = destination.index === source.index;
|
||||
@ -28,11 +60,11 @@ export const isPositionChanged = (source: any, destination: any) => {
|
||||
};
|
||||
|
||||
export const getAfterDropDraggableList = (
|
||||
beforeDropDraggables: any,
|
||||
droppedDraggable: any,
|
||||
isList: any,
|
||||
isSameList: any,
|
||||
destination: any,
|
||||
beforeDropDraggables: Array<DraggableElement>,
|
||||
droppedDraggable: DraggableElement,
|
||||
isList: boolean,
|
||||
isSameList: boolean,
|
||||
destination: DraggableLocation,
|
||||
) => {
|
||||
if (isList) {
|
||||
return moveItemWithinArray(beforeDropDraggables, droppedDraggable, destination.index);
|
||||
|
@ -8748,6 +8748,11 @@ immer@1.10.0:
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
|
||||
integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==
|
||||
|
||||
immer@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-6.0.3.tgz#94d5051cd724668160a900d66d85ec02816f29bd"
|
||||
integrity sha512-12VvNrfSrXZdm/BJgi/KDW2soq5freVSf3I1+4CLunUM8mAGx2/0Njy0xBVzi5zewQZiwM7z1/1T+8VaI7NkmQ==
|
||||
|
||||
immutable@~3.7.6:
|
||||
version "3.7.6"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b"
|
||||
|
Reference in New Issue
Block a user