Browse Source

allow moving tasks between projects.

main
Gisle Aune 4 years ago
parent
commit
8313a27251
  1. 9
      api/task.go
  2. 3
      database/postgres/tasks.go
  3. 4
      models/task.go
  4. 78
      svelte-ui/src/components/ProjectSelect.svelte
  5. 7
      svelte-ui/src/forms/TaskForm.svelte
  6. 1
      svelte-ui/src/models/task.ts

9
api/task.go

@ -104,6 +104,15 @@ func Task(g *gin.RouterGroup, db database.Database) {
} }
} }
if update.ProjectID != nil && *update.ProjectID != task.ProjectID {
project, err := l.FindProject(c.Request.Context(), *update.ProjectID)
if err != nil {
return nil, slerrors.NotFound("Destination project")
}
task.ProjectID = project.ID
}
task.Update(update) task.Update(update)
if task.EndTime != nil && task.EndTime.Before(task.CreatedTime) { if task.EndTime != nil && task.EndTime.Before(task.CreatedTime) {
return nil, slerrors.BadRequest("Task end time must be later than it was created.") return nil, slerrors.BadRequest("Task end time must be later than it was created.")

3
database/postgres/tasks.go

@ -94,7 +94,8 @@ func (r *taskRepository) Update(ctx context.Context, task models.Task) error {
description = :description, description = :description,
active = :active, active = :active,
end_time = :end_time, end_time = :end_time,
status_tag = :status_tag
status_tag = :status_tag,
project_id = :project_id
WHERE task_id=:task_id WHERE task_id=:task_id
`, &task) `, &task)
if err != nil { if err != nil {

4
models/task.go

@ -49,6 +49,9 @@ func (task *Task) Update(update TaskUpdate) {
if update.ClearStatusTag { if update.ClearStatusTag {
task.StatusTag = nil task.StatusTag = nil
} }
if update.ProjectID != nil {
task.ProjectID = *update.ProjectID
}
} }
type TaskUpdate struct { type TaskUpdate struct {
@ -61,6 +64,7 @@ type TaskUpdate struct {
ClearEndTime bool `json:"clearEndTime"` ClearEndTime bool `json:"clearEndTime"`
StatusTag *string `json:"statusTag"` StatusTag *string `json:"statusTag"`
ClearStatusTag bool `json:"clearStatusTag"` ClearStatusTag bool `json:"clearStatusTag"`
ProjectID *string `json:"projectId"`
} }
type TaskResult struct { type TaskResult struct {

78
svelte-ui/src/components/ProjectSelect.svelte

@ -0,0 +1,78 @@
<script lang="ts">
import type Project from "../models/project";
import authStore from "../stores/auth";
import projectStore from "../stores/project";
interface OptGroup {
status: string
projects: Project[]
}
export let value = "";
export let name = "";
export let disabled = false;
export let optional = false;
let optGroups: OptGroup[]
$: {
if ($projectStore.stale && !$projectStore.loading) {
projectStore.load({});
}
}
$: {
optGroups = [
{
status: "Deadlines",
projects: $projectStore.projects.filter(p => p.active && p.endTime)
},
{
status: "Active",
projects: $projectStore.projects.filter(p => p.active && !p.endTime)
},
{
status: "To Do",
projects: $projectStore.projects.filter(p => !p.active && p.statusTag === "to do")
},
{
status: "On Hold",
projects: $projectStore.projects.filter(p => !p.active && p.statusTag === "on hold")
},
{
status: "Completed",
projects: $projectStore.projects.filter(p => !p.active && p.statusTag === "completed")
},
{
status: "Failed",
projects: $projectStore.projects.filter(p => !p.active && p.statusTag === "failed")
},
]
for (const group of optGroups) {
group.projects.sort((a,b) => a.name.localeCompare(b.name));
}
}
$: {
if (optGroups.length > 0 && value === "" && !optional) {
const nonEmpty = optGroups.find(g => g.projects.length > 0);
if (nonEmpty != null) {
value = nonEmpty.projects[0].id;
}
}
}
</script>
<select name={name} bind:value={value} disabled={disabled || $projectStore.loading}>
{#if optional}
<option value={""} selected={"" === value}>None</option>
{/if}
{#each optGroups as group (group.status)}
<optgroup label={group.status}>
{#each group.projects as project (project.id)}
<option value={project.id} selected={project.id === value}>{project.name}</option>
{/each}
</optgroup>
{/each}
</select>

7
svelte-ui/src/forms/TaskForm.svelte

@ -3,6 +3,7 @@
import DeadlineSelect from "../components/DeadlineSelect.svelte"; import DeadlineSelect from "../components/DeadlineSelect.svelte";
import ItemSelect from "../components/ItemSelect.svelte"; import ItemSelect from "../components/ItemSelect.svelte";
import Modal from "../components/Modal.svelte"; import Modal from "../components/Modal.svelte";
import ProjectSelect from "../components/ProjectSelect.svelte";
import StatusTagSelect from "../components/StatusTagSelect.svelte"; import StatusTagSelect from "../components/StatusTagSelect.svelte";
import { iconNames } from "../external/icons"; import { iconNames } from "../external/icons";
import type { TaskResult } from "../models/task"; import type { TaskResult } from "../models/task";
@ -50,6 +51,7 @@ import DeadlineSelect from "../components/DeadlineSelect.svelte";
let description = task.description; let description = task.description;
let itemAmount = task.itemAmount; let itemAmount = task.itemAmount;
let statusTag = task.statusTag || ""; let statusTag = task.statusTag || "";
let projectId = task.projectId;
let error = null; let error = null;
let loading = false; let loading = false;
@ -91,10 +93,11 @@ import DeadlineSelect from "../components/DeadlineSelect.svelte";
active: statusTag === "", active: statusTag === "",
statusTag: statusTag || null, statusTag: statusTag || null,
clearStatusTag: statusTag === "", clearStatusTag: statusTag === "",
projectId: (projectId !== task.projectId) ? projectId : null,
itemId, name, description, itemAmount, itemId, name, description, itemAmount,
}).then(() => { }).then(() => {
markStale("goal", "project", "task");
markStale("goal", "project", "task", "log");
modalStore.close(); modalStore.close();
}).catch(err => { }).catch(err => {
error = err.message ? err.message : err.toString(); error = err.message ? err.message : err.toString();
@ -108,7 +111,7 @@ import DeadlineSelect from "../components/DeadlineSelect.svelte";
<Modal show title="{verb} Task" error={error} closable on:close={modalStore.close}> <Modal show title="{verb} Task" error={error} closable on:close={modalStore.close}>
<form on:submit|preventDefault={onSubmit}> <form on:submit|preventDefault={onSubmit}>
<label for="projectName">Project</label> <label for="projectName">Project</label>
<input disabled name="projectName" type="text" value={task.project.name} />
<ProjectSelect disabled={deletion || creation} name="projectName" bind:value={projectId} />
<label for="name">Name</label> <label for="name">Name</label>
<input disabled={deletion} name="name" type="text" bind:value={name} /> <input disabled={deletion} name="name" type="text" bind:value={name} />
<label for="description">Description</label> <label for="description">Description</label>

1
svelte-ui/src/models/task.ts

@ -49,4 +49,5 @@ export interface TaskUpdate {
clearEndTime?: boolean clearEndTime?: boolean
statusTag?: string statusTag?: string
clearStatusTag?: boolean clearStatusTag?: boolean
projectId?: string
} }
Loading…
Cancel
Save