Files
Umbrella/frontend/src/routes/task/ListTask.svelte
T
2026-05-07 18:26:37 +02:00

170 lines
5.3 KiB
Svelte

<script>
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { dragged } from './dragndrop.svelte';
import { api, drop, patch, post } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import { timetrack } from '../../user.svelte';
import { now } from '../../time.svelte';
import TaskList from './TaskList.svelte';
import LineEditor from '../../Components/LineEditor.svelte';
let {
est_time,
lastEvent,
show_closed,
siblings,
states = {},
task
} = $props();
let children = $state(null);
let deleted = $state(false);
const router = useTinyRouter();
let start = 0;
function addSubtask(){
router.navigate(`/task/${task.id}/add_subtask`);
}
async function addTime(){
const url = api(`time/track_task/${task.id}`);
const resp = await fetch(url,{
credentials : 'include',
method : 'POST',
body : now()
}); // create new time or return time with assigned tasks
if (resp.ok) {
const track = await resp.json();
timetrack.running = track;
} else {
error(resp);
}
}
async function deleteTask(){
if (confirm(t('confirm_delete',{element:task.name}))){
const url = api(`task/${task.id}`);
const resp = await drop(url);
if (resp.ok){
deleted = true;
} else {
error(resp);
}
}
}
function ondragstart(ev){
ev.stopPropagation();
dragged.element = task;
dragged.siblings = siblings;
}
async function ondrop(ev){
ev.stopPropagation();
if (dragged.element.id == task.id) return;
const url = api(`task/${dragged.element.id}`);
const resp = await patch(url, { parent_task_id : task.id});
if (resp.ok) {
yikes();
} else {
error(resp);
}
return false;
}
async function loadChildren(){
const url = api('task/list');
const data = {
parent_task_id : +task.id,
show_closed : show_closed
};
if (task.show_closed) data.show_closed = true;
const resp = await post(url,data);
if (resp.ok){
children = await resp.json();
yikes();
} else {
error(resp);
}
}
function openTask(e){
e.preventDefault();
let href = e.target.getAttribute('href');
if (href) router.navigate(href);
return false;
}
async function patchTask(changeset){
const url = api(`task/${task.id}`);
const resp = await patch(url,changeset);
if (resp.ok){
task = await resp.json();
return true;
} else {
error(resp);
return false;
}
}
async function setName(newName){
return await patchTask({name:newName});
}
function map_state(tx){
for (let [code,name] of Object.entries(states)){
if (tx == name) return {status:+code};
}
return {state:0};
}
if (task.est_time){
est_time.sum += task.est_time;
}
$effect(() => {
if (children && lastEvent && lastEvent.task) {
if (lastEvent.event == 'delete' || lastEvent.task.parent_task_id != task.id){
delete children[lastEvent.task.id];
} else children[lastEvent.task.id] = lastEvent.task;
}
});
onMount(loadChildren);
</script>
{#if !deleted}
<li draggable="true" {ondrop} ondragover={e => e.preventDefault()} {ondragstart} class="task {states[task.status]?.toLowerCase()}">
<LineEditor bind:value={task.name} onclick={openTask} editable={true} onSet={setName} wrapper="a" href={`/task/${task.id}/view`} />
{#if task.est_time}
<span class="estimated_time">({+task.est_time}&nbsp;h)</span>
{/if}
<button class="symbol" title={t('drag_n_drop')}></button>
{#if states[task.status] != 'PENDING'}
<button class="symbol" title={t('postpone')} onclick={() => patchTask(map_state('PENDING'))}></button>
{/if}
{#if states[task.status] != 'OPEN'}
<button class="symbol" title={t('do_open')} onclick={() => patchTask(map_state('OPEN'))}></button>
{/if}
{#if states[task.status] != 'STARTED'}
<button class="symbol" title={t('started')} onclick={() => patchTask(map_state('STARTED'))}></button>
{/if}
{#if states[task.status] != 'COMPLETE'}
<button class="symbol" title={t('complete')} onclick={() => patchTask(map_state('COMPLETE'))}></button>
{/if}
{#if states[task.status] != 'CANCELLED'}
<button class="symbol" title={t('abort')} onclick={() => patchTask(map_state('CANCELLED'))} ></button>
{/if}
<button class="symbol" title={t('delete_object',{object:t('subtask')})} onclick={deleteTask} ></button>
<button class="symbol" title={t('add_object',{object:t('subtask')})} onclick={addSubtask}></button>
<button class="symbol" title={t('timetracking')} onclick={addTime}></button>
{#if children}
<TaskList {states} {lastEvent} tasks={children} {est_time} {show_closed} />
{/if}
</li>
{/if}