Add task archive and restore

This commit is contained in:
Gordon 2021-05-27 12:49:13 +01:00
parent 79b43b204b
commit 720202e194
10 changed files with 1614 additions and 776 deletions

View File

@ -1,3 +1,8 @@
# 0.10.0
* Added task archive, tasks can now be sent to the archive folder and restored from the archive folder
* Fixed debounced updates on burndown chart
# 0.9.3 # 0.9.3
* Fixed performance issue where React app would keep adding event listeners every time the board was re-rendered * Fixed performance issue where React app would keep adding event listeners every time the board was re-rendered

View File

@ -54,6 +54,7 @@ The following commands are available:
- `Kanbn: Open board` will open open the Kanbn board. - `Kanbn: Open board` will open open the Kanbn board.
- `Kanbn: Open burndown chart` will open a burndown chart. - `Kanbn: Open burndown chart` will open a burndown chart.
- `Kanbn: Add task` will open the task editor. - `Kanbn: Add task` will open the task editor.
- `Kanbn: Restore task` will restore tasks from the archive.
## Configuration settings ## Configuration settings

View File

@ -91,6 +91,7 @@ Various Codicon icons have been used in this extension. Check [here](https://cod
- `kanbn-task-editor-comment-text` - `kanbn-task-editor-comment-text`
- `kanbn-task-editor-column-right` - `kanbn-task-editor-column-right`
- `kanbn-task-editor-button-submit` - `kanbn-task-editor-button-submit`
- `kanbn-task-editor-button-archive`
- `kanbn-task-editor-field-column` - `kanbn-task-editor-field-column`
- `kanbn-task-editor-field-assigned` - `kanbn-task-editor-field-assigned`
- `kanbn-task-editor-field-started` - `kanbn-task-editor-field-started`

View File

@ -197,6 +197,16 @@ export default class KanbnTaskPanel {
} }
}); });
return; return;
// Archive a task and close the webview panel
case 'kanbn.archive':
await this._kanbn.archiveTask(message.taskId);
KanbnTaskPanel.panels[message.panelUuid].dispose();
delete KanbnTaskPanel.panels[message.panelUuid];
if (vscode.workspace.getConfiguration("kanbn").get("showTaskNotifications")) {
vscode.window.showInformationMessage(`Archived task '${message.taskData.name}'.`);
}
return;
} }
}, },
null, null,

View File

@ -136,6 +136,68 @@ export async function activate(context: vscode.ExtensionContext) {
}) })
); );
// Register a command to restore a task from the archive.
context.subscriptions.push(
vscode.commands.registerCommand("kanbn.restoreTask", async () => {
// If no workspace folder is opened, we can't restore tasks from the archive
if (vscode.workspace.workspaceFolders === undefined) {
vscode.window.showErrorMessage("You need to open a workspace before restoring tasks from the archive.");
return;
}
// Set the node process directory and import kanbn
process.chdir(vscode.workspace.workspaceFolders[0].uri.fsPath);
const kanbn = await import("@basementuniverse/kanbn/src/main");
// Get a list of archived tasks
let archivedTasks: string[] = [];
try {
archivedTasks = await kanbn.listArchivedTasks();
} catch (e) {}
if (archivedTasks.length === 0) {
vscode.window.showInformationMessage("There are no archived tasks to restore.");
return;
}
// Prompt for a selection of tasks to restore
const restoreTaskIds = await vscode.window.showQuickPick(
archivedTasks,
{
placeHolder: 'Select tasks to restore...',
canPickMany: true,
}
);
if (restoreTaskIds !== undefined && restoreTaskIds.length > 0) {
// Load index
const index = await kanbn.getIndex();
// Prompt for a column to restore the tasks into
const restoreColumn = await vscode.window.showQuickPick(
[
'None (use original)',
...Object.keys(index.columns)
],
{
canPickMany: false
}
);
if (restoreColumn !== undefined) {
for (let restoreTaskId of restoreTaskIds) {
await kanbn.restoreTask(restoreTaskId, restoreColumn === 'None (use original)' ? null : restoreColumn);
}
KanbnBoardPanel.update();
kanbnStatusBarItem.update();
if (vscode.workspace.getConfiguration("kanbn").get("showTaskNotifications")) {
vscode.window.showInformationMessage(
`Restored ${restoreTaskIds.length} task${restoreTaskIds.length === 1 ? '' : 's'}.`
);
}
}
}
})
);
// If a workspace folder is open, add a status bar item and start watching for file changes // If a workspace folder is open, add a status bar item and start watching for file changes
if (vscode.workspace.workspaceFolders !== undefined) { if (vscode.workspace.workspaceFolders !== undefined) {
// Set the node process directory and import kanbn // Set the node process directory and import kanbn

2178
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -59,6 +59,11 @@
"command": "kanbn.burndown", "command": "kanbn.burndown",
"title": "Open burndown chart", "title": "Open burndown chart",
"category": "Kanbn" "category": "Kanbn"
},
{
"command": "kanbn.restoreTask",
"title": "Restore task",
"category": "Kanbn"
} }
], ],
"configuration": { "configuration": {
@ -88,7 +93,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@basementuniverse/kanbn": "^0.8.2", "@basementuniverse/kanbn": "file:~/Projects/kanbn",
"dateformat": "^4.5.1", "dateformat": "^4.5.1",
"formik": "^2.2.6", "formik": "^2.2.6",
"git-user-name": "^2.0.0", "git-user-name": "^2.0.0",

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useRef } from 'react';
import { ResponsiveContainer, LineChart, Line, CartesianGrid, XAxis, YAxis, Tooltip } from 'recharts'; import { ResponsiveContainer, LineChart, Line, CartesianGrid, XAxis, YAxis, Tooltip } from 'recharts';
import VSCodeApi from "./VSCodeApi"; import VSCodeApi from "./VSCodeApi";
import formatDate from 'dateformat'; import formatDate from 'dateformat';
@ -40,44 +40,96 @@ const Burndown = ({ name, sprints, burndownData, dateFormat, vscode }: {
: formatDate(burndownData.series[0].to, 'yyyy-mm-dd') : formatDate(burndownData.series[0].to, 'yyyy-mm-dd')
); );
const refreshBurndownData = debounce(500, settings => { const refreshBurndownData = useRef(debounce(500, settings => {
vscode.postMessage({ vscode.postMessage({
command: 'kanbn.refreshBurndownData', command: 'kanbn.refreshBurndownData',
...Object.assign( ...settings
});
})).current;
const handleChangeSprint = ({ target: { value }}) => {
setSprint(value);
refreshBurndownData(
Object.assign(
{ {
sprintMode, sprintMode,
sprint, sprint,
startDate, startDate,
endDate endDate
}, },
settings {
sprint: value
}
) )
}); );
});
const handleChangeSprint = ({ target: { value }}) => {
setSprint(value);
refreshBurndownData({ sprint: value });
}; };
const handleChangeStartDate = ({ target: { value }}) => { const handleChangeStartDate = ({ target: { value }}) => {
setStartDate(value); setStartDate(value);
refreshBurndownData({ startDate: value }); refreshBurndownData(
Object.assign(
{
sprintMode,
sprint,
startDate,
endDate
},
{
startDate: value
}
)
);
}; };
const handleChangeEndDate = ({ target: { value }}) => { const handleChangeEndDate = ({ target: { value }}) => {
setEndDate(value); setEndDate(value);
refreshBurndownData({ endDate: value }); refreshBurndownData(
Object.assign(
{
sprintMode,
sprint,
startDate,
endDate
},
{
endDate: value
}
)
);
}; };
const handleClickSprintMode = () => { const handleClickSprintMode = () => {
setSprintMode(true); setSprintMode(true);
refreshBurndownData({ sprintMode: true }); refreshBurndownData(
Object.assign(
{
sprintMode,
sprint,
startDate,
endDate
},
{
sprintMode: true
}
)
);
}; };
const handleClickDateMode = () => { const handleClickDateMode = () => {
setSprintMode(false); setSprintMode(false);
refreshBurndownData({ sprintMode: false }); refreshBurndownData(
Object.assign(
{
sprintMode,
sprint,
startDate,
endDate
},
{
sprintMode: false
}
)
);
}; };
const chartData = burndownData.series.length > 0 const chartData = burndownData.series.length > 0

View File

@ -107,6 +107,16 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUui
}); });
}; };
// Called when the archive task button is clicked
const handleArchiveTask = values => {
vscode.postMessage({
command: 'kanbn.archive',
taskId: task!.id,
taskData: values,
panelUuid
});
}
// Check if a task's due date is in the past // Check if a task's due date is in the past
const checkOverdue = (values: { metadata: { due?: string } }) => { const checkOverdue = (values: { metadata: { due?: string } }) => {
if ('due' in values.metadata && values.metadata.due !== undefined) { if ('due' in values.metadata && values.metadata.due !== undefined) {
@ -487,6 +497,16 @@ const TaskEditor = ({ task, tasks, columnName, columnNames, dateFormat, panelUui
> >
<i className="codicon codicon-trash"></i>Delete <i className="codicon codicon-trash"></i>Delete
</button>} </button>}
{editing && <button
type="button"
className="kanbn-task-editor-button kanbn-task-editor-button-archive"
title="Archive task"
onClick={() => {
handleArchiveTask(values);
}}
>
<i className="codicon codicon-archive"></i>Archive
</button>}
<button <button
type="submit" type="submit"
className="kanbn-task-editor-button kanbn-task-editor-button-submit" className="kanbn-task-editor-button kanbn-task-editor-button-submit"