Browse Source

fine-tuning kanban

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
kanban
Stephan Richter 3 months ago
parent
commit
be594b3d8f
  1. 48
      frontend/src/routes/project/Kanban.svelte
  2. 20
      frontend/src/routes/project/KanbanCard.svelte
  3. 46
      web/src/main/resources/web/css/default.css

48
frontend/src/routes/project/Kanban.svelte

@ -19,11 +19,13 @@
let columns = $derived(states?Object.keys(states).length+1:1); let columns = $derived(states?Object.keys(states).length+1:1);
let dragged = null; let dragged = null;
let users = {}; let users = {};
let ready = $state(false);
async function load(){ async function load(){
loadProject(); await loadProject();
loadStates(); await loadStates();
await loadTasks({project_id:+id,parent_task_id:0}); await loadTasks({project_id:+id,parent_task_id:0});
ready = true;
} }
async function loadProject(){ async function loadProject(){
@ -31,6 +33,11 @@
const resp = await fetch(url,{credentials:'include'}); const resp = await fetch(url,{credentials:'include'});
if (resp.ok){ if (resp.ok){
project = await resp.json(); project = await resp.json();
for (var uid of Object.keys(project.members)){
let member = project.members[uid];
users[uid] = member.user.name;
if (!tasks[uid]) tasks[uid] = {};
}
error = null; error = null;
} else { } else {
error = await resp.text(); error = await resp.text();
@ -68,7 +75,7 @@
var member = task.members[user_id]; var member = task.members[user_id];
if (member.permission.name == 'OWNER') owner = user_id; if (member.permission.name == 'OWNER') owner = user_id;
if (member.permission.name == 'ASSIGNEE') assignee = user_id; if (member.permission.name == 'ASSIGNEE') assignee = user_id;
users[user_id] = member.user.name;
} }
if (!assignee) assignee = owner; if (!assignee) assignee = owner;
task.assignee = assignee; task.assignee = assignee;
@ -116,31 +123,6 @@
onMount(load); onMount(load);
</script> </script>
<style>
.box,
.head,
.user{
border-radius: 5px;
margin: 2px;
min-height: 50px;
color: black;
padding: 2px;
}
.box{
background: orange;
}
.head,
.user{
background: black;
border: 1px solid orange;
color: orange;
text-align: center;
}
.highlight{
background: #4b3000;
}
</style>
{#if project} {#if project}
<h1 onclick={ev => router.navigate(`/project/${project.id}/view`)}>{project.name}</h1> <h1 onclick={ev => router.navigate(`/project/${project.id}/view`)}>{project.name}</h1>
{/if} {/if}
@ -148,6 +130,7 @@
<span class="error">{error}</span> <span class="error">{error}</span>
{/if} {/if}
{#if ready}
<div class="kanban" style="display: grid; grid-template-columns: {`repeat(${columns}, auto)`}"> <div class="kanban" style="display: grid; grid-template-columns: {`repeat(${columns}, auto)`}">
<div class="head">{t('user')}</div> <div class="head">{t('user')}</div>
{#if states} {#if states}
@ -155,10 +138,10 @@
<div class="head">{t('state_'+state.toLowerCase())}</div> <div class="head">{t('state_'+state.toLowerCase())}</div>
{/each} {/each}
{/if} {/if}
{#each Object.entries(tasks) as [user,stateList]} {#each Object.entries(tasks) as [uid,stateList]}
<div class="user">{users[user]}</div> <div class="user">{users[uid]}</div>
{#each Object.entries(states) as [state,name]} {#each Object.entries(states) as [state,name]}
<div class={[state, highlight.user == user && highlight.state == state ? 'highlight':'']} ondragover={ev => hover(ev,user,state)} ondrop={ev => drop(user,state)} > <div class={[state, highlight.user == uid && highlight.state == state ? 'highlight':'']} ondragover={ev => hover(ev,uid,state)} ondrop={ev => drop(uid,state)} >
{#if stateList[state]} {#if stateList[state]}
{#each Object.values(stateList[state]).sort((a,b) => a.name.localeCompare(b.name)) as task} {#each Object.values(stateList[state]).sort((a,b) => a.name.localeCompare(b.name)) as task}
<Card onclick={() => router.navigate(`/task/${task.id}/view`)} ondragstart={ev => dragged=task} {task} /> <Card onclick={() => router.navigate(`/task/${task.id}/view`)} ondragstart={ev => dragged=task} {task} />
@ -167,4 +150,5 @@
</div> </div>
{/each} {/each}
{/each} {/each}
</div> </div>
{/if}

20
frontend/src/routes/project/KanbanCard.svelte

@ -1,7 +1,21 @@
<script> <script>
let { ondragstart, task } = $props(); let { onclick, ondragstart, task } = $props();
</script> </script>
<div draggable="true" class="box" {ondragstart} > <div draggable="true" class="box" {onclick} {ondragstart} >
{task.name} <span class="title">{task.name}</span>
{#if task.estimated_time}
<span class="estimate">
({task.estimated_time}&nbsp;h)
</span>
{/if}
{#if task.due_date}
<span class="due_date">
{#if task.start_date}
{task.start_date}
{/if}
{task.due_date}
</span>
{/if}
</div> </div>

46
web/src/main/resources/web/css/default.css

@ -146,22 +146,38 @@ textarea{
.kanban .box, .kanban .box,
.kanban .head, .kanban .head,
.kanban .user{ .kanban .user{
border-radius: 5px; border-radius: 5px;
margin: 2px; margin: 2px;
min-height: 50px; min-height: 50px;
color: black; color: black;
padding: 2px; padding: 2px;
} }
.kanban .box{ .kanban .box{
background: orange; background: orange;
} position: relative;
}
.kanban .head, .kanban .head,
.kanban .user{ .kanban .user{
background: black; background: black;
border: 1px solid orange; border: 1px solid orange;
color: orange; color: orange;
text-align: center; text-align: center;
} }
.kanban .highlight{ .kanban .highlight{
background: #4b3000; background: #4b3000;
} }
.kanban .estimate{
border: 0 none;
position: absolute;
top: 0;
right: 0;
font-weight: bold;
font-size: 0.6em;
}
.kanban .due_date{
position: absolute;
font-weight: bold;
font-size: 0.6em;
bottom: 0;
right: 0;
}

Loading…
Cancel
Save