|
|
<script> |
|
|
import { onMount } from 'svelte'; |
|
|
import { useTinyRouter } from 'svelte-tiny-router'; |
|
|
|
|
|
import { api } from '../../urls.svelte.js'; |
|
|
import { t } from '../../translations.svelte.js'; |
|
|
|
|
|
const router = useTinyRouter(); |
|
|
let error = $state(null); |
|
|
let projects = $state(null); |
|
|
let companies = $state(null); |
|
|
let showClosed = $state(router.query.closed == "show"); |
|
|
|
|
|
let sortedProjects = $derived.by(() => Object.values(projects).sort((a, b) => a.name.localeCompare(b.name))); |
|
|
|
|
|
async function loadProjects(){ |
|
|
let url = api('company/list'); |
|
|
let resp = await fetch(url,{credentials:'include'}); |
|
|
if (resp.ok){ |
|
|
companies = await resp.json(); |
|
|
url = `${location.protocol}//${location.host.replace('5173','8080')}/api/project/list`; |
|
|
resp = await fetch(url,{ |
|
|
credentials : 'include', |
|
|
method : 'POST', |
|
|
body : JSON.stringify({show_closed:showClosed}) |
|
|
}); |
|
|
if (resp.ok){ |
|
|
projects = await resp.json(); |
|
|
} else { |
|
|
error = await resp.text(); |
|
|
} |
|
|
} else { |
|
|
error = await resp.text(); |
|
|
} |
|
|
} |
|
|
|
|
|
async function setState(pid,state_name){ |
|
|
const url = api(`project/${pid}`); |
|
|
const resp = await fetch(url,{ |
|
|
credentials : 'include', |
|
|
method : 'PATCH', |
|
|
body : JSON.stringify({status:state_name}) |
|
|
}); |
|
|
if (resp.ok){ |
|
|
var prj = await resp.json(); |
|
|
projects[prj.id].status = prj.status; |
|
|
} else { |
|
|
error = await resp.text(); |
|
|
} |
|
|
} |
|
|
|
|
|
function show(pid){ |
|
|
router.navigate(`/project/${pid}/view`) |
|
|
} |
|
|
|
|
|
function toggleClosed(){ |
|
|
router.navigate(showClosed?'/project':'/project?closed=show'); |
|
|
showClosed = !showClosed; |
|
|
loadProjects(); |
|
|
} |
|
|
|
|
|
onMount(loadProjects); |
|
|
</script> |
|
|
|
|
|
{#if error} |
|
|
<span class="error">{error}</span> |
|
|
{/if} |
|
|
<fieldset> |
|
|
<legend> |
|
|
{t('projects')} |
|
|
<button onclick={() => router.navigate('/project/add')}><span class="symbol"></span> {t('create_new_project')}</button> |
|
|
<button onclick={toggleClosed}><span class="symbol"></span> {t(showClosed?'hide_closed':'show_closed')}</button> |
|
|
</legend> |
|
|
{#if projects} |
|
|
<table class="project list"> |
|
|
<thead> |
|
|
<tr> |
|
|
<th>{t('name')}</th> |
|
|
<th>{t('company')}</th> |
|
|
<th>{t('state')}</th> |
|
|
<th>{t('members')}</th> |
|
|
<th>{t('actions')}</th> |
|
|
</tr> |
|
|
</thead> |
|
|
<tbody> |
|
|
{#each sortedProjects as project} |
|
|
<tr> |
|
|
<td class="name"> |
|
|
<a href="" onclick={() => show(project.id)}>{project.name}</a> |
|
|
</td> |
|
|
<td class="company" onclick={() => show(project.id)} > |
|
|
{#if project.company_id && companies[project.company_id]} |
|
|
{companies[project.company_id].name} |
|
|
{/if} |
|
|
</td> |
|
|
<td class="state" onclick={() => show(project.id)} > |
|
|
|
|
|
{t("state_"+project.status.name.toLowerCase())} |
|
|
</td> |
|
|
<td class="members" onclick={() => show(project.id)} > |
|
|
{#each Object.entries(project.members) as [uid,member]} |
|
|
<div>{member.user.name}</div> |
|
|
{/each} |
|
|
</td> |
|
|
<td class="actions"> |
|
|
<button class="edit symbol" title={t('edit')}></button> |
|
|
{#if project.status.code < 60} |
|
|
<button class="complete symbol" title={t('complete')} onclick={() => setState(project.id,'COMPLETE')} ></button> |
|
|
<button class="abort symbol" title={t('abort')} onclick={() => setState(project.id,'CANCELLED')} ></button> |
|
|
{:else} |
|
|
<button class="open symbol" title={t('open')} onclick={() => setState(project.id,'OPEN')} ></button> |
|
|
{/if} |
|
|
</td> |
|
|
</tr> |
|
|
{/each} |
|
|
</tbody> |
|
|
</table> |
|
|
{/if} |
|
|
</fieldset>
|
|
|
|