Add filter field, fix validation
This commit is contained in:
parent
7ea93c6290
commit
63bf065331
@ -137,21 +137,29 @@ export default class KanbnTaskPanel {
|
||||
|
||||
// Create a task
|
||||
case 'kanbn.create':
|
||||
// TODO create task
|
||||
vscode.window.showInformationMessage('create task');
|
||||
// TODO convert dates
|
||||
// await this._kanbn.createTask(message.taskData, message.taskData.column);
|
||||
vscode.window.showInformationMessage(`Created task ${message.taskData.name}.`);
|
||||
return;
|
||||
|
||||
// Update a task
|
||||
case 'kanbn.update':
|
||||
// TODO update task
|
||||
vscode.window.showInformationMessage('update task');
|
||||
// TODO convert dates
|
||||
// await this._kanbn.updateTask(message.taskData.id, message.taskData, message.taskData.column);
|
||||
vscode.window.showInformationMessage(`Updated task ${message.taskData.name}.`);
|
||||
return;
|
||||
|
||||
// Delete a task
|
||||
case 'kanbn.delete':
|
||||
// TODO delete task
|
||||
// TODO add yes/no confirmation buttons to information message, then delete task and close task panel
|
||||
vscode.window.showInformationMessage('delete task');
|
||||
vscode.window.showInformationMessage(`Delete task ${message.taskData.name}?`, 'Yes', 'No').then(
|
||||
async value => {
|
||||
if (value === 'Yes') {
|
||||
// await this._kanbn.deleteTask(message.taskId, true);
|
||||
vscode.window.showInformationMessage(`Deleted task ${message.taskData.name}.`);
|
||||
// TODO close panel, will need to generate uuid for each panel
|
||||
}
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
}, null, this._disposables);
|
||||
|
23
src/App.tsx
23
src/App.tsx
@ -1,4 +1,3 @@
|
||||
import Header from './Header';
|
||||
import Board from './Board';
|
||||
import TaskEditor from './TaskEditor';
|
||||
import React, { useState } from "react";
|
||||
@ -53,19 +52,15 @@ function App() {
|
||||
<React.Fragment>
|
||||
{
|
||||
type === 'index' &&
|
||||
<React.Fragment>
|
||||
<Header
|
||||
name={name}
|
||||
description={description}
|
||||
/>
|
||||
<Board
|
||||
columns={columns}
|
||||
startedColumns={startedColumns}
|
||||
completedColumns={completedColumns}
|
||||
dateFormat={dateFormat}
|
||||
vscode={vscode}
|
||||
/>
|
||||
</React.Fragment>
|
||||
<Board
|
||||
name={name}
|
||||
description={description}
|
||||
columns={columns}
|
||||
startedColumns={startedColumns}
|
||||
completedColumns={completedColumns}
|
||||
dateFormat={dateFormat}
|
||||
vscode={vscode}
|
||||
/>
|
||||
}
|
||||
{
|
||||
type === 'task' &&
|
||||
|
162
src/Board.tsx
162
src/Board.tsx
@ -55,7 +55,9 @@ const onDragEnd = (result, columns, setColumns, vscode: VSCodeApi) => {
|
||||
});
|
||||
};
|
||||
|
||||
const Board = ({ columns, startedColumns, completedColumns, dateFormat, vscode }: {
|
||||
const Board = ({ name, description, columns, startedColumns, completedColumns, dateFormat, vscode }: {
|
||||
name: string,
|
||||
description: string,
|
||||
columns: Record<string, KanbnTask[]>,
|
||||
startedColumns: string[],
|
||||
completedColumns: string[],
|
||||
@ -63,74 +65,102 @@ const Board = ({ columns, startedColumns, completedColumns, dateFormat, vscode }
|
||||
vscode: VSCodeApi
|
||||
}) => {
|
||||
const [, setColumns] = useState(columns);
|
||||
|
||||
const filterTasks = () => {
|
||||
console.log();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="kanbn-board">
|
||||
<DragDropContext
|
||||
onDragEnd={result => onDragEnd(result, columns, setColumns, vscode)}
|
||||
>
|
||||
{Object.entries(columns).map(([columnName, column]) => {
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
'kanbn-column',
|
||||
`kanbn-column-${paramCase(columnName)}`
|
||||
].join(' ')}
|
||||
key={columnName}
|
||||
<React.Fragment>
|
||||
<div className="kanbn-header">
|
||||
<h1 className="kanbn-header-name">
|
||||
<p>{name}</p>
|
||||
<div className="kanbn-filter">
|
||||
<input
|
||||
className="kanbn-filter-input"
|
||||
placeholder="Filter tasks"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="kanbn-filter-button"
|
||||
onClick={filterTasks}
|
||||
>
|
||||
<h2 className="kanbn-column-name">
|
||||
{
|
||||
startedColumns.indexOf(columnName) > -1 &&
|
||||
<i className="codicon codicon-chevron-right"></i>
|
||||
}
|
||||
{
|
||||
completedColumns.indexOf(columnName) > -1 &&
|
||||
<i className="codicon codicon-check"></i>
|
||||
}
|
||||
{columnName}
|
||||
<span className="kanbn-column-count">{column.length || ''}</span>
|
||||
<button
|
||||
type="button"
|
||||
className="kanbn-create-task-button"
|
||||
title={`Create task in ${columnName}`}
|
||||
onClick={() => {
|
||||
vscode.postMessage({
|
||||
command: 'kanbn.addTask',
|
||||
columnName
|
||||
})
|
||||
}}
|
||||
>
|
||||
<i className="codicon codicon-add"></i>
|
||||
</button>
|
||||
</h2>
|
||||
<div>
|
||||
<Droppable droppableId={columnName} key={columnName}>
|
||||
{(provided, snapshot) => {
|
||||
return (
|
||||
<div
|
||||
{...provided.droppableProps}
|
||||
ref={provided.innerRef}
|
||||
className={[
|
||||
'kanbn-column-task-list',
|
||||
snapshot.isDraggingOver ? 'drag-over' : null
|
||||
].filter(i => i).join(' ')}
|
||||
>
|
||||
{column.map((task, position) => <TaskItem
|
||||
task={task}
|
||||
position={position}
|
||||
dateFormat={dateFormat}
|
||||
vscode={vscode}
|
||||
/>)}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Droppable>
|
||||
<i className="codicon codicon-filter"></i>
|
||||
</button>
|
||||
</div>
|
||||
</h1>
|
||||
<p className="kanbn-header-description">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
<div className="kanbn-board">
|
||||
<DragDropContext
|
||||
onDragEnd={result => onDragEnd(result, columns, setColumns, vscode)}
|
||||
>
|
||||
{Object.entries(columns).map(([columnName, column]) => {
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
'kanbn-column',
|
||||
`kanbn-column-${paramCase(columnName)}`
|
||||
].join(' ')}
|
||||
key={columnName}
|
||||
>
|
||||
<h2 className="kanbn-column-name">
|
||||
{
|
||||
startedColumns.indexOf(columnName) > -1 &&
|
||||
<i className="codicon codicon-chevron-right"></i>
|
||||
}
|
||||
{
|
||||
completedColumns.indexOf(columnName) > -1 &&
|
||||
<i className="codicon codicon-check"></i>
|
||||
}
|
||||
{columnName}
|
||||
<span className="kanbn-column-count">{column.length || ''}</span>
|
||||
<button
|
||||
type="button"
|
||||
className="kanbn-create-task-button"
|
||||
title={`Create task in ${columnName}`}
|
||||
onClick={() => {
|
||||
vscode.postMessage({
|
||||
command: 'kanbn.addTask',
|
||||
columnName
|
||||
})
|
||||
}}
|
||||
>
|
||||
<i className="codicon codicon-add"></i>
|
||||
</button>
|
||||
</h2>
|
||||
<div>
|
||||
<Droppable droppableId={columnName} key={columnName}>
|
||||
{(provided, snapshot) => {
|
||||
return (
|
||||
<div
|
||||
{...provided.droppableProps}
|
||||
ref={provided.innerRef}
|
||||
className={[
|
||||
'kanbn-column-task-list',
|
||||
snapshot.isDraggingOver ? 'drag-over' : null
|
||||
].filter(i => i).join(' ')}
|
||||
>
|
||||
{column.map((task, position) => <TaskItem
|
||||
task={task}
|
||||
position={position}
|
||||
dateFormat={dateFormat}
|
||||
vscode={vscode}
|
||||
/>)}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</Droppable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</DragDropContext>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</DragDropContext>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
import React from "react";
|
||||
|
||||
const Header = ({ name, description }: { name: string, description: string }) => {
|
||||
return (
|
||||
<div className="kanbn-header">
|
||||
<h1 className="kanbn-header-name">{name}</h1>
|
||||
<p className="kanbn-header-description">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Header;
|
@ -46,11 +46,27 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, vscode }
|
||||
});
|
||||
};
|
||||
|
||||
// Called when the form is submitted
|
||||
const handleSubmit = values => {
|
||||
if (editing) {
|
||||
vscode.postMessage({
|
||||
command: 'kanbn.update',
|
||||
taskData: values
|
||||
});
|
||||
} else {
|
||||
vscode.postMessage({
|
||||
command: 'kanbn.create',
|
||||
taskData: values
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Called when the delete task button is clicked
|
||||
const handleRemoveTask = () => {
|
||||
const handleRemoveTask = values => {
|
||||
vscode.postMessage({
|
||||
command: 'kanbn.delete',
|
||||
taskId: task!.id
|
||||
taskId: task!.id,
|
||||
taskData: values
|
||||
});
|
||||
};
|
||||
|
||||
@ -94,7 +110,8 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, vscode }
|
||||
subTasks: task ? task.subTasks : [],
|
||||
comments: task ? task.comments : []
|
||||
}}
|
||||
validate={(values: KanbnTaskValidationInput): KanbnTaskValidationOutput => {
|
||||
validate={(values: KanbnTaskValidationInput): KanbnTaskValidationOutput|{} => {
|
||||
let hasErrors = false;
|
||||
const errors: KanbnTaskValidationOutput = {
|
||||
name: '',
|
||||
metadata: {
|
||||
@ -107,17 +124,20 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, vscode }
|
||||
// Task name cannot be empty
|
||||
if (!values.name) {
|
||||
errors.name = 'Task name is required.';
|
||||
hasErrors = true;
|
||||
}
|
||||
|
||||
// Check if the id is already in use
|
||||
if (values.id in tasks && tasks[values.id].uuid !== values.uuid) {
|
||||
errors.name = 'There is already a task with the same name or id.';
|
||||
hasErrors = true;
|
||||
}
|
||||
|
||||
// Tag names cannot be empty
|
||||
for (let i = 0; i < values.metadata.tags.length; i++) {
|
||||
if (!values.metadata.tags[i]) {
|
||||
errors.metadata.tags[i] = 'Tag cannot be empty.';
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,6 +147,7 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, vscode }
|
||||
errors.subTasks[i] = {
|
||||
text: 'Sub-task text cannot be empty.'
|
||||
};
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,22 +157,14 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, vscode }
|
||||
errors.comments[i] = {
|
||||
text: 'Comment text cannot be empty.'
|
||||
};
|
||||
hasErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
return hasErrors ? errors : {};
|
||||
}}
|
||||
onSubmit={(values, { setSubmitting }) => {
|
||||
if (editing) {
|
||||
vscode.postMessage({
|
||||
command: 'kanbn.update'
|
||||
});
|
||||
} else {
|
||||
vscode.postMessage({
|
||||
command: 'kanbn.create'
|
||||
});
|
||||
}
|
||||
console.log(values);
|
||||
handleSubmit(values);
|
||||
setSubmitting(false);
|
||||
}}
|
||||
>
|
||||
@ -385,7 +398,9 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, vscode }
|
||||
type="button"
|
||||
className="kanbn-task-editor-button kanbn-task-editor-button-delete"
|
||||
title="Delete task"
|
||||
onClick={handleRemoveTask}
|
||||
onClick={() => {
|
||||
handleRemoveTask(values);
|
||||
}}
|
||||
>
|
||||
<i className="codicon codicon-trash"></i>Delete
|
||||
</button>}
|
||||
|
@ -12,6 +12,51 @@ body {
|
||||
margin-top: 0;
|
||||
padding-bottom: 0.5em;
|
||||
border-bottom: 1px var(--vscode-activityBar-inactiveForeground) solid;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.kanbn-header-name p {
|
||||
display: inline-block;
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.kanbn-filter {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.kanbn-filter-input {
|
||||
box-sizing: border-box;
|
||||
width: 80%;
|
||||
padding: 8px;
|
||||
background-color: var(--vscode-input-background);
|
||||
color: var(--vscode-input-foreground);
|
||||
border: 1px transparent solid;
|
||||
}
|
||||
|
||||
.kanbn-filter-input:hover, .kanbn-filter-input:focus {
|
||||
border-color: var(--vscode-input-border);
|
||||
}
|
||||
|
||||
.kanbn-filter-button {
|
||||
outline: none;
|
||||
border: none;
|
||||
background-color: var(--vscode-button-background);
|
||||
color: var(--vscode-button-foreground);
|
||||
padding: 9px 1em;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.kanbn-filter-button .codicon {
|
||||
font-size: 11px !important;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.kanbn-filter-button:hover, .kanbn-filter-button:focus {
|
||||
background-color: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
.kanbn-board {
|
||||
|
Loading…
x
Reference in New Issue
Block a user