OpenSource Projekt-Management-Software
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

150 lines
5.0 KiB

<script>
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte.js';
import { t } from '../../translations.svelte.js';
let error = $state(null);
let projects = $state({});
let router = useTinyRouter();
let tasks = $state(null);
let bounds = $state({offset:0,limit:256});
let loading = $state(true);
let map = $state({});
let hidden = $state({});
async function changeState(tid,state){
const task = tasks[tid];
const prj = projects[task.project_id];
const stat = Object.keys(prj.allowed_states).find(k => prj.allowed_states[k] === state);
const url = api(`task/${tid}`);
const resp = await fetch(url,{
credentials : 'include',
method : 'PATCH',
body : JSON.stringify({status:stat})
});
if (resp.ok){
tasks[tid] = await resp.json();
} else {
error = await resp.text();
}
}
function abort(tid){
changeState(tid,'CANCELLED');
}
function complete(tid){
changeState(tid,'COMPLETE');
}
function edit(tid){
router.navigate(`/task/${tid}/edit`);
}
function go(module, id){
router.navigate(`/${module}/${id}/view`);
}
async function loadProject(pid){
const url = api(`project/${pid}`);
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
projects[pid] = await resp.json();
} else {
error = await resp.text();
}
}
async function load(){
const url = api(`task?offset=${bounds.offset}&limit=${bounds.limit}`);
const resp = await fetch(url,{credentials:'include'});
if (resp.ok){
let newTasks = await resp.json();
if (bounds.offset == 0) {
tasks = newTasks;
} else tasks = tasks.concat(newTasks);
loading = newTasks.length;
if (loading){
for (let task of newTasks) {
map[task.id] = task;
if (task.parent_task_id) hidden[task.parent_task_id] = true;
let pid = task.project_id;
if (pid && !projects[pid]) await loadProject(pid);
}
bounds.offset += bounds.limit;
load();
}
} else {
error = await resp.text();
}
}
function open(tid){
changeState(tid,'OPEN');
}
function postpone(tid){
changeState(tid,'PENDING');
}
function start(tid){
changeState(tid,'STARTED');
}
onMount(load);
</script>
<fieldset>
<legend>{loading ? t('loading_object',{object:t('task_list')}) : t('task_list')}</legend>
{#if error}
<soan class="error">{error}</soan>
{/if}
{#if tasks}
<table>
<thead>
<tr>
<th>{t('name')}</th>
<th>{t('project')} ({t('parent_task')})</th>
<th>{t('state')}</th>
<th>{t('start_date')}</th>
<th>{t('due_date')}</th>
<th>{t('actions')}</th>
</tr>
</thead>
<tbody>
{#each tasks as task (task.id)}
{#if task.status > 10 && task.status < 60 && !task.no_index && projects[task.project_id]?.status < 60 && !hidden[task.id]}
<tr>
<td onclick={() => go('task',task.id)}>{task.name}</td>
<td>
<a href="#" onclick={() => go('project',task.project_id)}> {projects[task.project_id]?.name}</a>
{#if task.parent_task_id}
: <a href="#" onclick={() => go('task',task.parent_task_id)}>{map[task.parent_task_id]?.name}</a>
{/if}
</td>
<td>
{task.status % 10 ? projects[task.project_id]?.allowed_states[task.status] : t('state_'+projects[task.project_id]?.allowed_states[task.status]?.toLowerCase())}
</td>
<td>
{task.start_date}
</td>
<td>
{task.due_date}
</td>
<td>
<button class="symbol" onclick={() => edit(task.id)} title={t('edit')} ></button>
<button class="symbol" onclick={() => postpone(task.id)} title={t('postpone')} ></button>
<button class="symbol" onclick={() => open(task.id)} title={t('open')}></button>
<button class="symbol" onclick={() => start(task.id)} title={t('start')} ></button>
<button class="symbol" onclick={() => complete(task.id)} title={t('complete')} ></button>
<button class="symbol" onclick={() => abort(task.id)} title={t('abort')} ></button>
</td>
</tr>
{/if}
{/each}
</tbody>
</table>
{/if}
</fieldset>