|
|
|
|
@ -11,14 +11,16 @@
@@ -11,14 +11,16 @@
|
|
|
|
|
let router = useTinyRouter(); |
|
|
|
|
let states = $state(null); |
|
|
|
|
let tasks = $state({}); |
|
|
|
|
let highlight = $state({}); |
|
|
|
|
|
|
|
|
|
let columns = $derived(states?Object.keys(states).length+1:1); |
|
|
|
|
let dragged = null; |
|
|
|
|
let users = {}; |
|
|
|
|
|
|
|
|
|
async function load(){ |
|
|
|
|
loadProject(); |
|
|
|
|
loadStates(); |
|
|
|
|
await loadTasks({project_id:+id,parent_task_id:0}); |
|
|
|
|
console.log(tasks); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async function loadProject(){ |
|
|
|
|
@ -53,28 +55,56 @@
@@ -53,28 +55,56 @@
|
|
|
|
|
}); |
|
|
|
|
if (resp.ok){ |
|
|
|
|
var json = await resp.json(); |
|
|
|
|
console.clear(); |
|
|
|
|
for (var task_id of Object.keys(json)) { |
|
|
|
|
let task = json[task_id]; |
|
|
|
|
let state = task.status.name; |
|
|
|
|
let state = task.status.code; |
|
|
|
|
let owner = null; |
|
|
|
|
let assignee = null; |
|
|
|
|
for (var uid of Object.keys(task.members)){ |
|
|
|
|
var member = task.members[uid]; |
|
|
|
|
if (member.permission.name == 'OWNER') owner = member.user.name; |
|
|
|
|
if (member.permission.name == 'ASSIGNEE') assignee = member.user.name; |
|
|
|
|
for (var user_id of Object.keys(task.members)){ |
|
|
|
|
var member = task.members[user_id]; |
|
|
|
|
if (member.permission.name == 'OWNER') owner = user_id; |
|
|
|
|
if (member.permission.name == 'ASSIGNEE') assignee = user_id; |
|
|
|
|
users[user_id] = member.user.name; |
|
|
|
|
} |
|
|
|
|
if (!assignee) assignee = owner; |
|
|
|
|
task.assignee = assignee; |
|
|
|
|
if (!tasks[assignee]) tasks[assignee] = {}; |
|
|
|
|
if (!tasks[assignee][state]) tasks[assignee][state] = {}; |
|
|
|
|
tasks[assignee][state][task_id] = task; |
|
|
|
|
} |
|
|
|
|
console.log(tasks); |
|
|
|
|
} else { |
|
|
|
|
error = await resp.text(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async function drop(user,state){ |
|
|
|
|
let task = dragged; |
|
|
|
|
dragged = null; |
|
|
|
|
highlight = {}; |
|
|
|
|
if (task.assignee == user && task.status.code == state) return; // no change |
|
|
|
|
let patch = {members:{},status:+state} |
|
|
|
|
patch.members[user] = 'ASSIGNEE'; |
|
|
|
|
|
|
|
|
|
const url = api(`task/${task.id}`); |
|
|
|
|
const resp = await fetch(url,{ |
|
|
|
|
credentials: 'include', |
|
|
|
|
method: 'PATCH', |
|
|
|
|
body: JSON.stringify(patch) |
|
|
|
|
}); |
|
|
|
|
if (resp.ok){ |
|
|
|
|
delete tasks[task.assignee][task.status.code][task.id] |
|
|
|
|
tasks[user][state][task.id] = task; |
|
|
|
|
error = null; |
|
|
|
|
} else { |
|
|
|
|
error = await resp.text(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function hover(ev,user,state){ |
|
|
|
|
ev.preventDefault(); |
|
|
|
|
highlight = {user:user,state:state}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
onMount(load); |
|
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
@ -95,13 +125,13 @@
@@ -95,13 +125,13 @@
|
|
|
|
|
.user{ |
|
|
|
|
background: lime; |
|
|
|
|
} |
|
|
|
|
.empty{ |
|
|
|
|
background: repeating-linear-gradient(45deg,transparent,transparent 10px,#ccc 10px,#ccc 20px),linear-gradient(to bottom,#eee,#999); |
|
|
|
|
.highlight{ |
|
|
|
|
background: red; |
|
|
|
|
} |
|
|
|
|
</style> |
|
|
|
|
|
|
|
|
|
{#if project} |
|
|
|
|
<h1>{project.name} : {t('kanban_view')}</h1> |
|
|
|
|
<h1 onclick={ev => router.navigate(`/project/${project.id}/view`)}>{project.name}</h1> |
|
|
|
|
{/if} |
|
|
|
|
{#if error} |
|
|
|
|
<span class="error">{error}</span> |
|
|
|
|
@ -111,17 +141,18 @@
@@ -111,17 +141,18 @@
|
|
|
|
|
<div class="head">{t('user')}</div> |
|
|
|
|
{#if states} |
|
|
|
|
{#each Object.entries(states) as [sid,state]} |
|
|
|
|
<div class="head">{t(state)}</div> |
|
|
|
|
<div class="head">{t('state_'+state.toLowerCase())}</div> |
|
|
|
|
{/each} |
|
|
|
|
{/if} |
|
|
|
|
{#each Object.entries(tasks) as [user,states]} |
|
|
|
|
<div class="user">{user}</div> |
|
|
|
|
{#each Object.entries(states) as [state,list]} |
|
|
|
|
<div> |
|
|
|
|
{#each Object.entries(list) as [tid,task]} |
|
|
|
|
<div class="box" onclick={() => router.navigate(`/task/${task.id}/view`)}>{task.name}</div> |
|
|
|
|
{#each Object.entries(tasks) as [user,stateList]} |
|
|
|
|
<div class="user">{users[user]}</div> |
|
|
|
|
{#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)} > |
|
|
|
|
{#if stateList[state]} |
|
|
|
|
{#each Object.entries(stateList[state]) as [tid,task]} |
|
|
|
|
<div draggable="true" class="box" onclick={() => router.navigate(`/task/${task.id}/view`)} ondragstart={ev => dragged=task} >{task.name}</div> |
|
|
|
|
{/each} |
|
|
|
|
<div class="empty box"></div> |
|
|
|
|
{/if} |
|
|
|
|
</div> |
|
|
|
|
{/each} |
|
|
|
|
{/each} |
|
|
|
|
|