diff --git a/core/src/main/java/de/srsoftware/umbrella/core/model/Task.java b/core/src/main/java/de/srsoftware/umbrella/core/model/Task.java index 4503c1c..f5a00ca 100644 --- a/core/src/main/java/de/srsoftware/umbrella/core/model/Task.java +++ b/core/src/main/java/de/srsoftware/umbrella/core/model/Task.java @@ -171,7 +171,7 @@ public class Task implements Mappable { case MEMBERS: continue; case NAME: name = json.getString(key); break; case NO_INDEX: noIndex = json.getBoolean(NO_INDEX); break; - case PARENT_TASK_ID: parentTaskId = json.getLong(PARENT_TASK_ID); break; + case PARENT_TASK_ID: parentTaskId = json.isNull(PARENT_TASK_ID) ? null : json.getLong(PARENT_TASK_ID); break; case PRIORITY: priority = json.getInt(PRIORITY); break; case REQUIRED_TASKS_IDS: requiredTasksIds.clear(); diff --git a/frontend/src/routes/task/Index.svelte b/frontend/src/routes/task/Index.svelte index c536f0f..0cb7461 100644 --- a/frontend/src/routes/task/Index.svelte +++ b/frontend/src/routes/task/Index.svelte @@ -14,8 +14,9 @@ let map = $state({}); let hidden = $state({}); - async function changeState(tid,state){ - const task = tasks[tid]; + async function changeState(idx,state){ + const task = tasks[idx]; + const tid = task.id; const prj = projects[task.project_id]; const stat = Object.keys(prj.allowed_states).find(k => prj.allowed_states[k] === state); const url = api(`task/${tid}`); @@ -25,18 +26,18 @@ body : JSON.stringify({status:stat}) }); if (resp.ok){ - tasks[tid] = await resp.json(); + tasks[idx] = await resp.json(); } else { error(resp); } } - function abort(tid){ - changeState(tid,'CANCELLED'); + function abort(idx){ + changeState(idx,'CANCELLED'); } - function complete(tid){ - changeState(tid,'COMPLETE'); + function complete(idx){ + changeState(idx,'COMPLETE'); } function edit(tid){ @@ -81,16 +82,16 @@ } } - function open(tid){ - changeState(tid,'OPEN'); + function open(idx){ + changeState(idx,'OPEN'); } - function postpone(tid){ - changeState(tid,'PENDING'); + function postpone(idx){ + changeState(idx,'PENDING'); } - function start(tid){ - changeState(tid,'STARTED'); + function start(idx){ + changeState(idx,'STARTED'); } onMount(load); @@ -115,7 +116,7 @@ - {#each tasks as task (task.id)} + {#each tasks as task,idx} {#if task.status > 10 && task.status < 60 && !task.no_index && projects[task.project_id]?.status < 60 && !hidden[task.id]} go('task',task.id)}>{task.name} @@ -136,11 +137,11 @@ - - - - - + + + + + {/if} diff --git a/frontend/src/routes/task/View.svelte b/frontend/src/routes/task/View.svelte index 9434461..193eeb9 100644 --- a/frontend/src/routes/task/View.svelte +++ b/frontend/src/routes/task/View.svelte @@ -140,6 +140,10 @@ showSettings = !showSettings; } + function unlink_parent(){ + update({parent_task_id:null}); + } + async function update(data){ const url = api(`task/${id}`); const resp = await fetch(url,{ @@ -151,7 +155,11 @@ yikes(); let old_task = task; task = await resp.json(); - if (task.parent_id == old_task.parent_id) task.parent = old_task.parent; + if (!task.parent_id){ + task.parent = null; + } else { + if (task.parent_id == old_task.parent_id) task.parent = old_task.parent; + } return true; } else { error(resp); @@ -196,6 +204,7 @@
{t('parent_task')}
{task.parent.name} +
{/if}
{t('task')}
diff --git a/frontend/src/routes/time/Index.svelte b/frontend/src/routes/time/Index.svelte index 33653f1..20c845c 100644 --- a/frontend/src/routes/time/Index.svelte +++ b/frontend/src/routes/time/Index.svelte @@ -10,12 +10,15 @@ import TimeEditor from '../../Components/TimeRecordEditor.svelte'; + let detail = $state(null); let docLinks = $state(null); let router = useTinyRouter(); + let times = $state(null); let tasks = {}; let projects = {}; - let detail = $state(null); + let project_filter = $state(null); + if (router.hasQueryParam('project')) project_filter = router.getQueryParam('project'); let sortedTimes = $derived.by(() => Object.values(times).map(time => ({ ...time, start: display(time.start_time), @@ -114,6 +117,14 @@ } } + function match_prj_filter(time){ + if (!project_filter) return true; + for (var tid of time.task_ids){ + if (project_filter == tasks[tid].project_id) return true; + } + return false; + } + function onAbort(){ detail = null; } @@ -217,6 +228,7 @@ {#each sortedTimes as time,line} + {#if match_prj_filter(time)} {#if timeMap.years[line]} toggleRange(time.start.substring(0,4))}> @@ -279,6 +291,7 @@ {/if} + {/if} {/each} diff --git a/web/src/main/resources/web/css/bloodshed-color.css b/web/src/main/resources/web/css/bloodshed-color.css index 78bcbd4..d6f08d0 100644 --- a/web/src/main/resources/web/css/bloodshed-color.css +++ b/web/src/main/resources/web/css/bloodshed-color.css @@ -46,6 +46,11 @@ textarea{ background-color: black; } +tr:hover{ + background: darkred; + color: black; +} + .archive{ background: red; color: black; @@ -155,15 +160,21 @@ textarea{ color: yellow; } -.timetracks .year, .month{ - border-color: 1px solid; -} - .timetracks .selected td:not(.year):not(.month){ background-color: darkred; color: orange; } +.timetracks.sum { + background: black; +} + +.timetracks .year, .month{ + border-color: 1px solid; +} + + + .warn { background-color: yellow; color: black; diff --git a/web/src/main/resources/web/css/bloodshed.css b/web/src/main/resources/web/css/bloodshed.css index 0585de2..31ad291 100644 --- a/web/src/main/resources/web/css/bloodshed.css +++ b/web/src/main/resources/web/css/bloodshed.css @@ -290,11 +290,31 @@ span.timetracking { display: inline-block; } +.markdown.editing{ + display: grid; + grid-template-columns: 1fr 1fr; + column-gap: 10px; +} +.markdown .buttons, +.markdown .hint{ + grid-column-end: span 2; +} + +.editable:hover{ + border: 1px dotted; +} .timetracks .year, .timetracks .month{ border: 1px solid; } +.timetracks.sum { + position: sticky; + top: 60px; + z-index: 20; + padding: 5px; +} + .timetracks.sum span{ font-weight: bold; } @@ -307,23 +327,6 @@ span.timetracking { float: right; } -.markdown.editing{ - position: relative; -} - -.markdown.editing > *{ - width: 49%; -} - -.markdown.editing > *:nth-child(2){ - position: absolute; - right: 0; - top: 0; -} - -.markdown img{ - max-width: 75%; -} table{ min-width: 30vw; @@ -370,6 +373,7 @@ a.wikilink{ .grid2{ display: grid; grid-template-columns: 230px auto; + margin: 0 5px; } .grid2 > :nth-child(2n-1){ @@ -399,6 +403,17 @@ a.wikilink{ text-align: initial; padding-top: 8px; } + + .markdown.editing{ + display: block; + grid-template-columns: 1fr 1fr; + column-gap: 10px; + } + + .markdown textarea{ + width: calc(100% - 10px); + min-height: 50px; + } } fieldset.vcard{ diff --git a/web/src/main/resources/web/css/default-color.css b/web/src/main/resources/web/css/default-color.css index 1360748..9b42b96 100644 --- a/web/src/main/resources/web/css/default-color.css +++ b/web/src/main/resources/web/css/default-color.css @@ -45,6 +45,15 @@ textarea{ background-color: #333; } +tr:hover{ + background: orange; + color: black; +} + +tr:hover a{ + background: black; +} + .archive{ background: black; } @@ -150,6 +159,11 @@ textarea{ background-color: navy; } +.timetracks.sum { + background: black; +} + + .version a.selected{ border-color: orange; } diff --git a/web/src/main/resources/web/css/default.css b/web/src/main/resources/web/css/default.css index 24d7053..31ad291 100644 --- a/web/src/main/resources/web/css/default.css +++ b/web/src/main/resources/web/css/default.css @@ -308,6 +308,13 @@ span.timetracking { border: 1px solid; } +.timetracks.sum { + position: sticky; + top: 60px; + z-index: 20; + padding: 5px; +} + .timetracks.sum span{ font-weight: bold; } diff --git a/web/src/main/resources/web/css/winter-color.css b/web/src/main/resources/web/css/winter-color.css index 49f2730..31996d8 100644 --- a/web/src/main/resources/web/css/winter-color.css +++ b/web/src/main/resources/web/css/winter-color.css @@ -41,6 +41,10 @@ textarea{ background-color: lightcyan;; } +tr:hover{ + background: #afffff; +} + .archive{ background: white; } @@ -140,6 +144,11 @@ textarea{ background-color: aquamarine; } +.timetracks.sum { + background: white; +} + + .version a.selected{ border-color: orange; } diff --git a/web/src/main/resources/web/css/winter.css b/web/src/main/resources/web/css/winter.css index 0585de2..31ad291 100644 --- a/web/src/main/resources/web/css/winter.css +++ b/web/src/main/resources/web/css/winter.css @@ -290,11 +290,31 @@ span.timetracking { display: inline-block; } +.markdown.editing{ + display: grid; + grid-template-columns: 1fr 1fr; + column-gap: 10px; +} +.markdown .buttons, +.markdown .hint{ + grid-column-end: span 2; +} + +.editable:hover{ + border: 1px dotted; +} .timetracks .year, .timetracks .month{ border: 1px solid; } +.timetracks.sum { + position: sticky; + top: 60px; + z-index: 20; + padding: 5px; +} + .timetracks.sum span{ font-weight: bold; } @@ -307,23 +327,6 @@ span.timetracking { float: right; } -.markdown.editing{ - position: relative; -} - -.markdown.editing > *{ - width: 49%; -} - -.markdown.editing > *:nth-child(2){ - position: absolute; - right: 0; - top: 0; -} - -.markdown img{ - max-width: 75%; -} table{ min-width: 30vw; @@ -370,6 +373,7 @@ a.wikilink{ .grid2{ display: grid; grid-template-columns: 230px auto; + margin: 0 5px; } .grid2 > :nth-child(2n-1){ @@ -399,6 +403,17 @@ a.wikilink{ text-align: initial; padding-top: 8px; } + + .markdown.editing{ + display: block; + grid-template-columns: 1fr 1fr; + column-gap: 10px; + } + + .markdown textarea{ + width: calc(100% - 10px); + min-height: 50px; + } } fieldset.vcard{