Skip to content

Commit

Permalink
Issue #8: make tasks deletable
Browse files Browse the repository at this point in the history
  • Loading branch information
gilesv committed Sep 7, 2019
1 parent 4e887b8 commit 9dfb678
Show file tree
Hide file tree
Showing 15 changed files with 196 additions and 13,699 deletions.
13,620 changes: 0 additions & 13,620 deletions package-lock.json

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js",
"increase-watchers": "sudo sysctl -w fs.inotify.max_user_watches=524288"
"watchers": "sudo sysctl -w fs.inotify.max_user_watches=524288"
},
"eslintConfig": {
"extends": "react-app"
Expand Down
4 changes: 2 additions & 2 deletions src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Dashboard extends React.Component<Props> {

this.exportStory = this.exportStory.bind(this);
this.importStory = this.importStory.bind(this);
this.notify = this.notify.bind(this);
}

public componentDidMount() {
Expand All @@ -39,7 +40,6 @@ class Dashboard extends React.Component<Props> {

if (storageState) {
Trader.importStories(storageState, FileType.JSON);
this.props.dispatch(setStateClean());

this.notify(new Notification("Your work was restored!", "tick"));
}
Expand Down Expand Up @@ -113,7 +113,7 @@ class Dashboard extends React.Component<Props> {
{
stories && stories.ids && stories.ids.length > 0 ?
<main>
<StoryDetails exportStory={this.exportStory} />
<StoryDetails exportStory={this.exportStory} notify={this.notify} />
</main>
: null
}
Expand Down
5 changes: 3 additions & 2 deletions src/components/StoryDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface Props {
tasks: { [key: number]: Task },
tasksIds: TaskId[],
exportStory: (fileType: FileType) => void,
notify: () => void,
[key: string]: any
}

Expand Down Expand Up @@ -49,7 +50,7 @@ class StoryDetails extends React.Component<Props> {
}

render() {
const { selectedStory, isStateDirty } = this.props;
const { selectedStory, isStateDirty, notify } = this.props;

const exportMenu = (
<Menu>
Expand Down Expand Up @@ -87,7 +88,7 @@ class StoryDetails extends React.Component<Props> {
</div>

<div className="story-details__stories">
<TaskList story={selectedStory} />
<TaskList story={selectedStory} notify={notify} />
</div>
</> : null
}
Expand Down
3 changes: 1 addition & 2 deletions src/components/StoryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ class StoryList extends React.Component<Props> {
}

public addStory() {
const id = this.props.storiesIds.length;
const story = new Story(id, this.generateNewName());
const story = new Story(this.generateNewName());
this.props.dispatch(addStory(story));
}

Expand Down
82 changes: 65 additions & 17 deletions src/components/TaskItem.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import React from "react";
import { Task } from "../entities/task.entity";
import TaskForm from "./TaskForm";
import { Collapse, Tag, Icon } from "@blueprintjs/core";
import { CSSTransition } from "react-transition-group";
import { Collapse, Tag, Icon, Classes, Button, Popover, Position, Menu, Dialog } from "@blueprintjs/core";
import { StoryId } from "../entities/story.entity";

interface Props {
task: Task,
addTaskAtIndex: (index: number) => void,
updateTask: (task: Task) => void,
index: number
removeTask: (task: Task, storyId: StoryId) => void,
index: number,
storyId: StoryId
}

export default class TaskItem extends React.Component<Props> {
Expand All @@ -18,7 +20,10 @@ export default class TaskItem extends React.Component<Props> {
this.element = React.createRef();
this.update = this.update.bind(this);
this.updateEffort = this.updateEffort.bind(this);
this.handleHeaderClick = this.handleHeaderClick.bind(this);
this.toggleBody = this.toggleBody.bind(this);
this.handleOptionsClick = this.handleOptionsClick.bind(this);
this.deleteItem = this.deleteItem.bind(this);
this.toggleDialog = this.toggleDialog.bind(this);
}

public element: any;
Expand All @@ -28,10 +33,18 @@ export default class TaskItem extends React.Component<Props> {
}

public state = {
isBodyVisible: true
isBodyVisible: true,
isDialogVisible: false,
}

handleHeaderClick() {
toggleDialog() {
this.setState({
...this.state,
isDialogVisible: !this.state.isDialogVisible
});
}

toggleBody() {
this.setState({
...this.state,
isBodyVisible: !this.state.isBodyVisible
Expand All @@ -54,16 +67,35 @@ export default class TaskItem extends React.Component<Props> {
this.update('effort', value);
}
}

deleteItem() {
this.toggleDialog();

setTimeout(() => {
const { task, storyId } = this.props;
this.props.removeTask(task, storyId);
}, 300);
}

handleOptionsClick(e: any) {
e.stopPropagation();
}

render() {
const { task, addTaskAtIndex, index } = this.props;

const optionsMenu = (
<Menu>
<Menu.Item text="Delete item" onClick={this.toggleDialog} />
</Menu>
);

return (
<CSSTransition classNames="task" timeout={300} in={true}>
<div className="task-item" ref={this.element}>
<AddTaskButton type="before" visible={index === 0} index={index} addTask={(index: number) => addTaskAtIndex(index)} />
<div className="task-item" ref={this.element}>
<AddTaskButton type="before" visible={index === 0} index={index} addTask={(index: number) => addTaskAtIndex(index)} />

<div className="task-item__header" onClick={this.handleHeaderClick}>
<div className="task-item__header" onClick={this.toggleBody}>
<div className="task-item__header-left">
<div className="task-item__tags">
<Tag intent="primary" minimal={true}>{task.type}</Tag>
<Tag intent="primary" minimal={true}>{task.area}</Tag>
Expand All @@ -72,17 +104,33 @@ export default class TaskItem extends React.Component<Props> {

<div className="task-item__title">{task.title}</div>
</div>

<div className="task-item__header-right" onClick={this.handleOptionsClick}>
<Popover content={optionsMenu} position={Position.BOTTOM_RIGHT} minimal={true}>
<Button rightIcon="more" minimal={true}/>
</Popover>
</div>
</div>

<Collapse isOpen={this.state.isBodyVisible}>
<div className="task-item__body">
<TaskForm task={task} update={this.update} updateEffort={this.updateEffort} showDates={false} />
</div>
</Collapse>
<Collapse isOpen={this.state.isBodyVisible}>
<div className="task-item__body">
<TaskForm task={task} update={this.update} updateEffort={this.updateEffort} showDates={false} />
</div>
</Collapse>

<AddTaskButton type="after" visible={true} index={index} addTask={(index: number) => addTaskAtIndex(index)} />
<AddTaskButton type="after" visible={true} index={index} addTask={(index: number) => addTaskAtIndex(index)} />

<Dialog isOpen={this.state.isDialogVisible} onClose={this.toggleDialog} title="Delete item">
<div className={Classes.DIALOG_BODY}>
Are you really sure to delete the following item: <b>{task.title}</b>? This cannot be undone.
</div>
</CSSTransition>

<div className={`${Classes.DIALOG_FOOTER} task-item__dialog-footer`} >
<Button intent="none" onClick={this.toggleDialog}>Cancel</Button>
<Button intent="danger" onClick={this.deleteItem}>Delete</Button>
</div>
</Dialog>
</div>
);
}
}
Expand Down
19 changes: 13 additions & 6 deletions src/components/TaskList.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from "react";
import { Story } from "../entities/story.entity";
import { Story, StoryId } from "../entities/story.entity";
import { connect } from "react-redux";
import { IStore } from "../redux/reducers";
import { Task, TaskId, TaskType } from "../entities/task.entity";
import TaskItem from "./TaskItem";
import { NonIdealState, Button, Popover, Position, ButtonGroup, Menu } from "@blueprintjs/core";
import { addTask, updateTask } from "../redux/actions";
import { CSSTransition } from "react-transition-group";
import { addTask, updateTask, removeTask } from "../redux/actions";
import Notification from "../entities/notification.entity";

interface Props {
tasks: { [key: number]: Task },
Expand All @@ -21,18 +21,23 @@ class TaskList extends React.Component<Props> {

this.addTask = this.addTask.bind(this);
this.updateTask = this.updateTask.bind(this);
this.removeTask = this.removeTask.bind(this);
}

public addTask(type = TaskType.TASK, index = -1) {
const id = this.props.tasksIds.length;
const task = new Task(id, type);
const task = new Task(type);
this.props.dispatch(addTask(task, this.props.story.id, index));
}

public updateTask(task: Task) {
this.props.dispatch(updateTask(task));
}

public removeTask(task: Task, storyId: StoryId) {
this.props.dispatch(removeTask(task.id, storyId));
this.props.notify(new Notification(`"${task.title}" was deleted successfully.`, "tick"));
}

render() {
const { story } = this.props;
const addItemMenu = (
Expand Down Expand Up @@ -66,8 +71,10 @@ class TaskList extends React.Component<Props> {
key={`task#${taskId}`}
index={i}
task={task}
storyId={story.id}
updateTask={this.updateTask}
addTaskAtIndex={(index) => this.addTask(TaskType.TASK, index)} />
addTaskAtIndex={(index) => this.addTask(TaskType.TASK, index)}
removeTask={this.removeTask} />
})
: <NonIdealState
className="story-details__nothing"
Expand Down
7 changes: 4 additions & 3 deletions src/entities/story.entity.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { TaskId } from "./task.entity";
import uuid from "uuid";

export type StoryId = number;
export type StoryId = any; // TODO: change

export class Story {
constructor(id: StoryId, name: string) {
this.id = id;
constructor(name?: string) {
this.id = uuid();
this.name = name || "New Story";
this.title = "";

Expand Down
8 changes: 5 additions & 3 deletions src/entities/task.entity.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export type TaskId = number;
import uuid from "uuid";

export type TaskId = any; // TODO: change

export enum TaskType {
TASK = "TASK",
Expand All @@ -22,8 +24,8 @@ export const Assignee = {
}

export class Task {
constructor(id: TaskId, type?: TaskType) {
this.id = id;
constructor(type?: TaskType) {
this.id = uuid();
this.type = type || TaskType.TASK;
this.title = "New item";
this.priority = 1.0;
Expand Down
4 changes: 2 additions & 2 deletions src/redux/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,9 @@ export function updateTask(task: Task): IAction {
};
}

export function removeTask(taskId: TaskId): IAction {
export function removeTask(taskId: TaskId, storyId: StoryId): IAction {
return {
type: ActionType.REMOVE_TASK,
payload: { taskId }
payload: { taskId, storyId }
};
}
17 changes: 17 additions & 0 deletions src/redux/reducers/stories.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export default function storiesReducer(
case ActionType.ADD_TASK:
return addTaskToStory(state, action.payload);

case ActionType.REMOVE_TASK:
return removeTaskFromStory(state, action.payload);

default:
return state;
}
Expand Down Expand Up @@ -74,3 +77,17 @@ const addTaskToStory = (state: IEntityMap<Story>, payload: any) => {
},
};
}

const removeTaskFromStory = (state: IEntityMap<Story>, payload: any) => {
const { taskId, storyId } = payload;
const story = state.entities[storyId];
story.tasks = removeFromArray(story.tasks, taskId);

return {
...state,
entities: {
...state.entities,
[storyId]: story
},
};
}
Loading

0 comments on commit 9dfb678

Please sign in to comment.