Add filter field, fix validation

This commit is contained in:
Gordon 2021-04-07 00:07:35 +01:00
parent 7ea93c6290
commit 63bf065331
6 changed files with 195 additions and 116 deletions

View File

@ -137,21 +137,29 @@ export default class KanbnTaskPanel {
// Create a task // Create a task
case 'kanbn.create': case 'kanbn.create':
// TODO create task // TODO convert dates
vscode.window.showInformationMessage('create task'); // await this._kanbn.createTask(message.taskData, message.taskData.column);
vscode.window.showInformationMessage(`Created task ${message.taskData.name}.`);
return; return;
// Update a task // Update a task
case 'kanbn.update': case 'kanbn.update':
// TODO update task // TODO convert dates
vscode.window.showInformationMessage('update task'); // await this._kanbn.updateTask(message.taskData.id, message.taskData, message.taskData.column);
vscode.window.showInformationMessage(`Updated task ${message.taskData.name}.`);
return; return;
// Delete a task // Delete a task
case 'kanbn.delete': case 'kanbn.delete':
// TODO delete task vscode.window.showInformationMessage(`Delete task ${message.taskData.name}?`, 'Yes', 'No').then(
// TODO add yes/no confirmation buttons to information message, then delete task and close task panel async value => {
vscode.window.showInformationMessage('delete task'); 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; return;
} }
}, null, this._disposables); }, null, this._disposables);

View File

@ -1,4 +1,3 @@
import Header from './Header';
import Board from './Board'; import Board from './Board';
import TaskEditor from './TaskEditor'; import TaskEditor from './TaskEditor';
import React, { useState } from "react"; import React, { useState } from "react";
@ -53,19 +52,15 @@ function App() {
<React.Fragment> <React.Fragment>
{ {
type === 'index' && type === 'index' &&
<React.Fragment> <Board
<Header
name={name} name={name}
description={description} description={description}
/>
<Board
columns={columns} columns={columns}
startedColumns={startedColumns} startedColumns={startedColumns}
completedColumns={completedColumns} completedColumns={completedColumns}
dateFormat={dateFormat} dateFormat={dateFormat}
vscode={vscode} vscode={vscode}
/> />
</React.Fragment>
} }
{ {
type === 'task' && type === 'task' &&

View File

@ -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[]>, columns: Record<string, KanbnTask[]>,
startedColumns: string[], startedColumns: string[],
completedColumns: string[], completedColumns: string[],
@ -63,7 +65,34 @@ const Board = ({ columns, startedColumns, completedColumns, dateFormat, vscode }
vscode: VSCodeApi vscode: VSCodeApi
}) => { }) => {
const [, setColumns] = useState(columns); const [, setColumns] = useState(columns);
const filterTasks = () => {
console.log();
};
return ( return (
<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}
>
<i className="codicon codicon-filter"></i>
</button>
</div>
</h1>
<p className="kanbn-header-description">
{description}
</p>
</div>
<div className="kanbn-board"> <div className="kanbn-board">
<DragDropContext <DragDropContext
onDragEnd={result => onDragEnd(result, columns, setColumns, vscode)} onDragEnd={result => onDragEnd(result, columns, setColumns, vscode)}
@ -131,6 +160,7 @@ const Board = ({ columns, startedColumns, completedColumns, dateFormat, vscode }
})} })}
</DragDropContext> </DragDropContext>
</div> </div>
</React.Fragment>
); );
}; };

View File

@ -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;

View File

@ -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 // Called when the delete task button is clicked
const handleRemoveTask = () => { const handleRemoveTask = values => {
vscode.postMessage({ vscode.postMessage({
command: 'kanbn.delete', 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 : [], subTasks: task ? task.subTasks : [],
comments: task ? task.comments : [] comments: task ? task.comments : []
}} }}
validate={(values: KanbnTaskValidationInput): KanbnTaskValidationOutput => { validate={(values: KanbnTaskValidationInput): KanbnTaskValidationOutput|{} => {
let hasErrors = false;
const errors: KanbnTaskValidationOutput = { const errors: KanbnTaskValidationOutput = {
name: '', name: '',
metadata: { metadata: {
@ -107,17 +124,20 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, vscode }
// Task name cannot be empty // Task name cannot be empty
if (!values.name) { if (!values.name) {
errors.name = 'Task name is required.'; errors.name = 'Task name is required.';
hasErrors = true;
} }
// Check if the id is already in use // Check if the id is already in use
if (values.id in tasks && tasks[values.id].uuid !== values.uuid) { if (values.id in tasks && tasks[values.id].uuid !== values.uuid) {
errors.name = 'There is already a task with the same name or id.'; errors.name = 'There is already a task with the same name or id.';
hasErrors = true;
} }
// Tag names cannot be empty // Tag names cannot be empty
for (let i = 0; i < values.metadata.tags.length; i++) { for (let i = 0; i < values.metadata.tags.length; i++) {
if (!values.metadata.tags[i]) { if (!values.metadata.tags[i]) {
errors.metadata.tags[i] = 'Tag cannot be empty.'; 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] = { errors.subTasks[i] = {
text: 'Sub-task text cannot be empty.' text: 'Sub-task text cannot be empty.'
}; };
hasErrors = true;
} }
} }
@ -136,22 +157,14 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, vscode }
errors.comments[i] = { errors.comments[i] = {
text: 'Comment text cannot be empty.' text: 'Comment text cannot be empty.'
}; };
hasErrors = true;
} }
} }
return errors; return hasErrors ? errors : {};
}} }}
onSubmit={(values, { setSubmitting }) => { onSubmit={(values, { setSubmitting }) => {
if (editing) { handleSubmit(values);
vscode.postMessage({
command: 'kanbn.update'
});
} else {
vscode.postMessage({
command: 'kanbn.create'
});
}
console.log(values);
setSubmitting(false); setSubmitting(false);
}} }}
> >
@ -385,7 +398,9 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, vscode }
type="button" type="button"
className="kanbn-task-editor-button kanbn-task-editor-button-delete" className="kanbn-task-editor-button kanbn-task-editor-button-delete"
title="Delete task" title="Delete task"
onClick={handleRemoveTask} onClick={() => {
handleRemoveTask(values);
}}
> >
<i className="codicon codicon-trash"></i>Delete <i className="codicon codicon-trash"></i>Delete
</button>} </button>}

View File

@ -12,6 +12,51 @@ body {
margin-top: 0; margin-top: 0;
padding-bottom: 0.5em; padding-bottom: 0.5em;
border-bottom: 1px var(--vscode-activityBar-inactiveForeground) solid; 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 { .kanbn-board {