Add task archive and restore
This commit is contained in:
parent
79b43b204b
commit
720202e194
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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`
|
||||||
|
@ -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,
|
||||||
|
@ -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
2178
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user