Add relations, custom fields and card customisation

This commit is contained in:
Gordon
2021-06-07 20:42:35 +01:00
parent 95b87052b4
commit 0aa5776af3
9 changed files with 290 additions and 113 deletions

View File

@ -18,6 +18,7 @@ function App() {
const [startedColumns, setStartedColumns] = useState([]);
const [completedColumns, setCompletedColumns] = useState([]);
const [columnSorting, setColumnSorting] = useState({});
const [customFields, setCustomFields] = useState([]);
const [dateFormat, setDateFormat] = useState('');
const [task, setTask] = useState({});
const [tasks, setTasks] = useState({});
@ -48,6 +49,7 @@ function App() {
setStartedColumns(event.data.startedColumns);
setCompletedColumns(event.data.completedColumns);
setColumnSorting(event.data.columnSorting);
setCustomFields(event.data.customFields);
setShowBurndownButton(event.data.showBurndownButton);
setShowSprintButton(event.data.showSprintButton);
@ -101,6 +103,7 @@ function App() {
startedColumns={startedColumns}
completedColumns={completedColumns}
columnSorting={columnSorting}
customFields={customFields}
dateFormat={dateFormat}
showBurndownButton={showBurndownButton}
showSprintButton={showSprintButton}

View File

@ -140,6 +140,7 @@ const Board = ({
startedColumns,
completedColumns,
columnSorting,
customFields,
dateFormat,
showBurndownButton,
showSprintButton,
@ -153,6 +154,7 @@ const Board = ({
startedColumns: string[],
completedColumns: string[],
columnSorting: { [columnName: string]: { field: string, order: 'ascending' | 'descending' }[] },
customFields: { name: string, type: 'boolean' | 'date' | 'number' | 'string' }[],
dateFormat: string,
showBurndownButton: boolean,
showSprintButton: boolean,
@ -326,6 +328,8 @@ const Board = ({
>
{column.filter(task => filterTask(task, taskFilter)).map((task, position) => <TaskItem
task={task}
columnName={columnName}
customFields={customFields}
position={position}
dateFormat={dateFormat}
vscode={vscode}

View File

@ -4,8 +4,10 @@ import formatDate from 'dateformat';
import { paramCase } from '@basementuniverse/kanbn/src/utility';
import VSCodeApi from "./VSCodeApi";
const TaskItem = ({ task, position, dateFormat, vscode }: {
const TaskItem = ({ task, columnName, customFields, position, dateFormat, vscode }: {
task: KanbnTask,
columnName: string,
customFields: { name: string, type: 'boolean' | 'date' | 'number' | 'string' }[],
position: number,
dateFormat: string,
vscode: VSCodeApi
@ -38,6 +40,9 @@ const TaskItem = ({ task, position, dateFormat, vscode }: {
{...provided.dragHandleProps}
className={[
'kanbn-task',
`kanbn-task-column-${paramCase(columnName)}`,
checkOverdue(task) ? 'kanbn-task-overdue' : null,
!!completedDate ? 'kanbn-task-completed' : null,
snapshot.isDragging ? 'drag' : null
].filter(i => i).join(' ')}
style={{
@ -45,10 +50,9 @@ const TaskItem = ({ task, position, dateFormat, vscode }: {
...provided.draggableProps.style
}}
>
<div className="kanbn-task-row">
<div className="kanbn-task-data kanbn-task-data-name">
<button
type="button"
className="kanbn-task-name"
onClick={() => {
vscode.postMessage({
command: 'kanbn.task',
@ -61,76 +65,131 @@ const TaskItem = ({ task, position, dateFormat, vscode }: {
{task.name}
</button>
</div>
<div className="kanbn-task-row">
{
'tags' in task.metadata &&
task.metadata.tags!.length > 0 &&
<div className="kanbn-task-data kanbn-task-tags">
{task.metadata.tags!.map(tag => {
return (
<span className={[
'kanbn-task-tag',
`kanbn-task-tag-${paramCase(tag)}`
].join(' ')}>
{tag}
</span>
);
})}
{
'tags' in task.metadata &&
task.metadata.tags!.length > 0 &&
<div className="kanbn-task-data kanbn-task-data-tags">
{task.metadata.tags!.map(tag => {
return (
<span className={[
'kanbn-task-tag',
`kanbn-task-tag-${paramCase(tag)}`
].join(' ')}>
{tag}
</span>
);
})}
</div>
}
{
customFields.map(customField => {
if (customField.name in task.metadata) {
return (
<div className={[
'kanbn-task-data kanbn-task-data-custom-field',
`kanbn-task-data-${paramCase(customField.name)}`
].join(' ')}>
{
customField.type === 'boolean'
? (
<>
<i className={`codicon codicon-${task.metadata[customField.name]
? 'pass-filled'
: 'circle-large-outline'}`}></i>
{customField.name}
</>
) : (
<>
<i className="codicon codicon-json"></i>
<span title={customField.name}>
{task.metadata[customField.name]}
</span>
</>
)
}
</div>
);
}
return (<></>);
})
}
{
'assigned' in task.metadata &&
!!task.metadata.assigned &&
<div className="kanbn-task-data kanbn-task-data-assigned">
<i className="codicon codicon-account"></i>{task.metadata.assigned}
</div>
}
{
createdDate &&
<div className="kanbn-task-data kanbn-task-data-created" title={`Created ${createdDate}`}>
<i className="codicon codicon-clock"></i>{createdDate}
</div>
}
{
updatedDate &&
<div className="kanbn-task-data kanbn-task-data-updated" title={`Updated ${updatedDate}`}>
<i className="codicon codicon-clock"></i>{updatedDate}
</div>
}
{
startedDate &&
<div className="kanbn-task-data kanbn-task-data-started" title={`Started ${startedDate}`}>
<i className="codicon codicon-run"></i>{startedDate}
</div>
}
{
dueDate &&
<div className="kanbn-task-data kanbn-task-data-due" title={`Due ${dueDate}`}>
<i className="codicon codicon-watch"></i>{dueDate}
</div>
}
{
completedDate &&
<div className="kanbn-task-data kanbn-task-data-completed" title={`Completed ${completedDate}`}>
<i className="codicon codicon-check"></i>{completedDate}
</div>
}
{
task.comments.length > 0 &&
<div className="kanbn-task-data kanbn-task-data-comments">
<i className="codicon codicon-comment"></i>{task.comments.length}
</div>
}
{
task.subTasks.length > 0 &&
<div className="kanbn-task-data kanbn-task-data-sub-tasks">
<i className="codicon codicon-tasklist"></i>
{task.subTasks.filter(subTask => subTask.completed).length} / {task.subTasks.length}
</div>
}
{
task.workload !== undefined &&
<div className="kanbn-task-data kanbn-task-data-workload">
<i className="codicon codicon-run"></i>{task.workload}
</div>
}
{
task.relations.length > 0 &&
task.relations.map(relation => (
<div className={[
'kanbn-task-data kanbn-task-data-relation',
relation.type ? `kanbn-task-data-relation-${relation.type}` : null,
].join(' ')}>
<i className="codicon codicon-link"></i>
<span className="kanbn-task-data-label">
{relation.type}
</span> {relation.task}
</div>
}
</div>
<div className="kanbn-task-row">
{
'assigned' in task.metadata &&
!!task.metadata.assigned &&
<div className="kanbn-task-data kanbn-task-assigned">
<i className="codicon codicon-account"></i>{task.metadata.assigned}
</div>
}
{
(createdDate || updatedDate) &&
<div className="kanbn-task-data kanbn-task-date" title={[
createdDate ? `Created ${createdDate}` : null,
updatedDate ? `Updated ${updatedDate}` : null,
startedDate ? `Started ${startedDate}` : null,
dueDate ? `Due ${dueDate}` : null,
completedDate ? `Completed ${completedDate}` : null
].filter(i => i).join('\n')}>
<i
className={[
'codicon codicon-clock',
checkOverdue(task) ? 'kanbn-task-overdue' : null
].filter(i => i).join(' ')}
></i>{updatedDate || createdDate}
</div>
}
{
task.comments.length > 0 &&
<div className="kanbn-task-data kanbn-task-comments">
<i className="codicon codicon-comment"></i>{task.comments.length}
</div>
}
{
task.subTasks.length > 0 &&
<div className="kanbn-task-data kanbn-task-sub-tasks">
<i className="codicon codicon-tasklist"></i>
{task.subTasks.filter(subTask => subTask.completed).length} / {task.subTasks.length}
</div>
}
{
task.workload !== undefined &&
<div className="kanbn-task-data kanbn-task-workload">
<i className="codicon codicon-run"></i>{task.workload}
</div>
}
{
task.workload !== undefined &&
task.progress !== undefined &&
<div className="kanbn-task-progress" style={{
width: `${Math.min(1, Math.max(0, task.progress)) * 100}%`
}}></div>
}
</div>
))
}
{
task.workload !== undefined &&
task.progress !== undefined &&
<div className="kanbn-task-progress" style={{
width: `${Math.min(1, Math.max(0, task.progress)) * 100}%`
}}></div>
}
</div>
);
}}

View File

@ -220,7 +220,33 @@ body.vscode-dark .kanbn-task {
border-color: var(--vscode-activityBar-foreground);
}
.kanbn-task-name {
.kanbn-task-data {
display: inline-block;
margin: 4px 8px 4px 0;
min-width: 30%;
opacity: 0.7;
font-size: 0.9em;
font-weight: normal;
font-style: italic;
}
.kanbn-task-data-label {
opacity: 0.4;
}
.kanbn-task-data-name {
display: block;
}
.kanbn-task-data-tags {
display: block;
}
.kanbn-task-data-relation {
display: block;
}
.kanbn-task-data-name button {
margin: 0;
padding: 0;
border: none;
@ -232,22 +258,13 @@ body.vscode-dark .kanbn-task {
text-align: left;
}
.kanbn-task-name:focus, .kanbn-task-name:hover {
.kanbn-task-data-name button:focus,
.kanbn-task-data-name button:hover {
border: none;
outline: none;
text-decoration: underline;
}
.kanbn-task-data {
display: inline-block;
margin: 4px 8px 4px 0;
min-width: 30%;
opacity: 0.7;
font-size: 0.9em;
font-weight: normal;
font-style: italic;
}
.kanbn-task div .codicon {
position: relative;
top: 1px;
@ -291,7 +308,7 @@ body.vscode-dark .kanbn-task {
background-color: #f42;
}
.kanbn-task-overdue {
.kanbn-task-overdue .kanbn-task-data-due {
color: #f22 !important;
}
@ -305,6 +322,30 @@ body.vscode-dark .kanbn-task {
transition: width .5s ease-in-out;
}
.kanbn-task-data-updated {
display: none;
}
.kanbn-task-data-started {
display: none;
}
.kanbn-task-data-due {
display: none;
}
.kanbn-task-data-completed {
display: none;
}
.kanbn-task-data-comments {
display: none;
}
.kanbn-task-data-relation {
display: none;
}
/*-----------------------------------------------------------------------------
Task editor styles
-----------------------------------------------------------------------------*/