Browse Source

implemented task creation right from the canban

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
module/notes
Stephan Richter 3 months ago
parent
commit
3b163a0d77
  1. 4
      core/src/main/java/de/srsoftware/umbrella/core/model/Task.java
  2. 1
      frontend/src/App.svelte
  3. 4
      frontend/src/Components/LineEditor.svelte
  4. 61
      frontend/src/routes/project/Kanban.svelte
  5. 12
      task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java
  6. 2
      web/src/main/resources/web/css/default.css

4
core/src/main/java/de/srsoftware/umbrella/core/model/Task.java

@ -129,6 +129,10 @@ public class Task implements Mappable { @@ -129,6 +129,10 @@ public class Task implements Mappable {
};
var status = Status.OPEN;
if (json.has(STATUS) && json.get(STATUS) instanceof JSONObject state){
if (state.get(CODE) instanceof Number code) status = Status.of(code.intValue());
if (state.get(CODE) instanceof String code) status = Status.valueOf(code);
}
Double estimatedTime = null;
if (json.has(ESTIMATED_TIME)) {
if (json.get(ESTIMATED_TIME) instanceof Number est) estimatedTime = est.doubleValue();

1
frontend/src/App.svelte

@ -25,6 +25,7 @@ @@ -25,6 +25,7 @@
import ViewTask from "./routes/task/View.svelte";
let translations_ready = $state(false);
onMount(async () => {
await loadTranslation('de','Login');
translations_ready = true;

4
frontend/src/Components/LineEditor.svelte

@ -4,7 +4,7 @@ @@ -4,7 +4,7 @@
let {
editable = false,
onclick = evt => {},
onclick = evt => { startEdit() },
onSet = newVal => {return true;},
type = 'div',
value = $bindable(null)
@ -84,5 +84,5 @@ @@ -84,5 +84,5 @@
{#if editable && editing}
<input bind:value={editValue} onkeyup={typed} autofocus />
{:else}
<svelte:element this={type} {onmousedown} {onmouseup} {ontouchstart} {ontouchend} class={{editable}} title={t('double_click_to_edit')} >{value}</svelte:element>
<svelte:element this={type} {onclick} {onmousedown} {onmouseup} {ontouchstart} {ontouchend} class={{editable}} title={t('double_click_to_edit')} >{value}</svelte:element>
{/if}

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

@ -4,8 +4,10 @@ @@ -4,8 +4,10 @@
import { api } from '../../urls.svelte.js';
import { t } from '../../translations.svelte.js';
import { user } from '../../user.svelte.js';
import Card from './KanbanCard.svelte';
import LineEditor from '../../Components/LineEditor.svelte';
let { id } = $props();
@ -21,6 +23,34 @@ @@ -21,6 +23,34 @@
let users = {};
let ready = $state(false);
async function create(name,user_id,state){
var url = api('task/add');
let task = {
description: '',
members : {},
name: name,
project_id: +id,
status : { code : +state}
}
task.members[user_id] = { permission: { name : 'ASSIGNEE' }};
task.members[user.id] = { permission: { name : 'OWNER' }};
const resp = await fetch(url,{
credentials:'include',
method:'POST',
body: JSON.stringify(task)
});
if (resp.ok) {
task = await resp.json();
task.assignee = user_id;
if (!tasks[user_id]) tasks[user_id] = {};
if (!tasks[user_id][state]) tasks[user_id][state] = {};
tasks[user_id][state][task.id] = task;
error = null;
} else {
error = await resp.text();
}
}
async function load(){
await loadProject();
await loadStates();
@ -88,13 +118,13 @@ @@ -88,13 +118,13 @@
}
}
async function drop(user,state){
async function drop(user_id,state){
let task = dragged;
dragged = null;
highlight = {};
if (task.assignee == user && task.status.code == state) return; // no change
if (task.assignee == user_id && task.status.code == state) return; // no change
let patch = {members:{},status:+state}
patch.members[user] = 'ASSIGNEE';
patch.members[user_id] = 'ASSIGNEE';
const url = api(`task/${task.id}`);
const resp = await fetch(url,{
@ -104,10 +134,10 @@ @@ -104,10 +134,10 @@
});
if (resp.ok){
delete tasks[task.assignee][task.status.code][task.id]
if (!tasks[user]) tasks[user] = {}
if (!tasks[user][state]) tasks[user][state] = {}
tasks[user][state][task.id] = task;
task.assignee = user;
if (!tasks[user_id]) tasks[user_id] = {}
if (!tasks[user_id][state]) tasks[user_id][state] = {}
tasks[user_id][state][task.id] = task;
task.assignee = user_id;
task.status = {code:state,name:states[state]};
error = null;
} else {
@ -115,9 +145,9 @@ @@ -115,9 +145,9 @@
}
}
function hover(ev,user,state){
function hover(ev,user_id,state){
ev.preventDefault();
highlight = {user:user,state:state};
highlight = {user:user_id,state:state};
}
onMount(load);
@ -142,11 +172,14 @@ @@ -142,11 +172,14 @@
<div class="user">{users[uid]}</div>
{#each Object.entries(states) as [state,name]}
<div class={[state, highlight.user == uid && highlight.state == state ? 'highlight':'']} ondragover={ev => hover(ev,uid,state)} ondrop={ev => drop(uid,state)} >
{#if stateList[state]}
{#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} />
{/each}
{/if}
{#if stateList[state]}
{#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} />
{/each}
{/if}
<div class="add_task">
<LineEditor value={t('add_task')} editable={true} onSet={(name) => create(name,uid,state)}/>
</div>
</div>
{/each}
{/each}

12
task/src/main/java/de/srsoftware/umbrella/task/TaskModule.java

@ -293,7 +293,10 @@ public class TaskModule extends BaseHandler implements TaskService { @@ -293,7 +293,10 @@ public class TaskModule extends BaseHandler implements TaskService {
long userId = Long.parseLong(key);
if (!(memberData.get(key) instanceof JSONObject nested)) throw invalidFieldException("members."+userId,"JSON");
if (!(nested.has(PERMISSION) && nested.get(PERMISSION) instanceof JSONObject permission)) throw invalidFieldException("members."+userId+".permission","JSON");
if (!(permission.has(CODE) && permission.get(CODE) instanceof Number code)) throw invalidFieldException("members."+userId+".permission.code","int");
Permission perm = null;
if (permission.has(CODE) && permission.get(CODE) instanceof Number code) perm = Permission.of(code.intValue());
if (permission.has(NAME) && permission.get(NAME) instanceof String name) perm = Permission.valueOf(name);
if (perm == null) throw invalidFieldException("members."+userId+".permission.code","int");
if (!project.members().containsKey(userId)) {
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);
@ -304,10 +307,11 @@ public class TaskModule extends BaseHandler implements TaskService { @@ -304,10 +307,11 @@ public class TaskModule extends BaseHandler implements TaskService {
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));
var nested = memberData.getJSONObject(key).getJSONObject(PERMISSION);
var permission = nested.has(CODE) ? Permission.of(nested.getInt(CODE)) : Permission.valueOf(nested.getString(NAME));
taskDb.setMember(task.id(),userId,permission);
}
return sendContent(ex,task);
return sendContent(ex,loadMembers(task));
}
private boolean postTaskList(UmbrellaUser user, HttpExchange ex) throws IOException {

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

@ -143,6 +143,7 @@ textarea{ @@ -143,6 +143,7 @@ textarea{
font-weight: black;
}
.kanban .add_task,
.kanban .box,
.kanban .head,
.kanban .user{
@ -156,6 +157,7 @@ textarea{ @@ -156,6 +157,7 @@ textarea{
background: orange;
position: relative;
}
.kanban .add_task,
.kanban .head,
.kanban .user{
background: black;

Loading…
Cancel
Save