kanban bugfixes & improvements
This commit is contained in:
@@ -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 @@
|
||||
});
|
||||
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 @@
|
||||
.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 @@
|
||||
<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}
|
||||
|
||||
@@ -111,6 +111,9 @@
|
||||
|
||||
<fieldset>
|
||||
<legend>{t('add_task')}</legend>
|
||||
{#if error}
|
||||
<span class="error">{error}</span>
|
||||
{/if}
|
||||
<table {onkeydown}>
|
||||
<tbody>
|
||||
<tr>
|
||||
|
||||
@@ -218,13 +218,21 @@ public class TaskModule extends BaseHandler implements TaskService {
|
||||
} catch (NumberFormatException e) {
|
||||
throw invalidFieldException(USER_ID,"long");
|
||||
}
|
||||
if (!(json.get(key) instanceof Number number)) throw invalidFieldException(PERMISSION,"int");
|
||||
var permission = Permission.of(number.intValue());
|
||||
var permission = switch (json.get(key)){
|
||||
case Number code -> Permission.of(code.intValue());
|
||||
case String name -> Permission.valueOf(name);
|
||||
default -> throw invalidFieldException(PERMISSION,"int / String");
|
||||
};
|
||||
if (permission == OWNER) { // if a new person is about to become the task owner
|
||||
for (var member : members.values()){ // alter the previous owners to editors
|
||||
if (member.permission() == OWNER) members.put(member.user().id(),new Member(member.user(),EDIT));
|
||||
}
|
||||
}
|
||||
if (permission == ASSIGNEE) { // if a new person is about to become the task owner
|
||||
for (var member : members.values()){ // alter the previous owners to editors
|
||||
if (member.permission() == ASSIGNEE) members.put(member.user().id(),new Member(member.user(),EDIT));
|
||||
}
|
||||
}
|
||||
members.put(userId,new Member(users.loadUser(userId),permission));
|
||||
task.dirty(MEMBERS);
|
||||
}
|
||||
@@ -253,8 +261,6 @@ public class TaskModule extends BaseHandler implements TaskService {
|
||||
projects.loadMembers(List.of(project));
|
||||
var member = project.members().get(user.id());
|
||||
if (member == null || member.permission() == READ_ONLY) throw forbidden("You are not allowed to create new tasks in this project");
|
||||
Task task = Task.of(json);
|
||||
task = taskDb.save(task);
|
||||
for (var key : memberData.keySet()){
|
||||
long userId = Long.parseLong(key);
|
||||
if (!(memberData.get(key) instanceof JSONObject nested)) throw invalidFieldException("members."+userId,"JSON");
|
||||
@@ -264,7 +270,14 @@ public class TaskModule extends BaseHandler implements TaskService {
|
||||
String username = nested.has(USER) && nested.get(USER) instanceof JSONObject userData && userData.get(NAME) instanceof String n ? n : key;
|
||||
throw new UmbrellaException(HTTP_BAD_REQUEST,"User {0} is no member of the leading project and cannot be assigned to this task",username);
|
||||
}
|
||||
taskDb.setMember(task.id(),userId,Permission.of(code.intValue()));
|
||||
}
|
||||
|
||||
Task task = Task.of(json);
|
||||
task = taskDb.save(task);
|
||||
for (var key : memberData.keySet()){
|
||||
long userId = Long.parseLong(key);
|
||||
var code = memberData.getJSONObject(key).getJSONObject(PERMISSION).getInt(CODE);
|
||||
taskDb.setMember(task.id(),userId,Permission.of(code));
|
||||
}
|
||||
return sendContent(ex,task);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user