implemented selection functions

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
2025-08-28 11:45:01 +02:00
parent 49461199cd
commit 9fe94da5a6

View File

@@ -11,6 +11,50 @@
let times = $state(null); let times = $state(null);
let detail = $state(null); let detail = $state(null);
let sortedTimes = $derived.by(() => Object.values(times).sort((b, a) => a.start_time.localeCompare(b.start_time))); let sortedTimes = $derived.by(() => Object.values(times).sort((b, a) => a.start_time.localeCompare(b.start_time)));
let selected = $state({});
let ranges = {};
let timeMap = $derived.by(() => {
let result = {
months : {},
years : {}
}
let lastYear = null;
let lastMonth = null;
let yearCount = 0;
let monthCount = 0;
let yearIndex = 0;
let monthIndex = 0;
for (let idx in sortedTimes){
const time = sortedTimes[idx];
const start = time.start_time;
const year = start.substring(0,4);
const month = start.substring(5,7);
if (year != lastYear){
lastYear = year;
if (yearCount) result.years[yearIndex] = yearCount;
yearCount = 0;
yearIndex = idx;
}
yearCount +=1;
if (month != lastMonth){
lastMonth = month;
if (monthCount) result.months[monthIndex] = monthCount;
monthCount = 0;
monthIndex = idx;
}
monthCount +=1;
}
if (yearCount) result.years[yearIndex] = yearCount;
if (monthCount) result.months[monthIndex] = monthCount;
console.log(result);
return result;
});
function calcYearMap(){
let result = Object.values(times).length;
console.log({result:result});
return result;
}
async function loadTimes(){ async function loadTimes(){
const url = api('time'); const url = api('time');
@@ -26,6 +70,26 @@
router.navigate(`task/${tid}/view`); router.navigate(`task/${tid}/view`);
} }
function toggleRange(range){
let affected = sortedTimes.filter(time => time.start_time.startsWith(range));
if (ranges[range]){
delete ranges[range];
for (let time of affected){
if (selected[time.id]) delete selected[time.id]
}
} else {
for (let time of affected) selected[time.id] = true;
ranges[range] = true;
}
}
function toggleSelect(time_id){
detail = null;
if (selected[time_id]) {
delete selected[time_id]
} else selected[time_id] = true;
}
async function update(time){ async function update(time){
const url = api(`time/${time.id}`); const url = api(`time/${time.id}`);
time.start_time = time.start_time.split(':').slice(0,2).join(':'); time.start_time = time.start_time.split(':').slice(0,2).join(':');
@@ -53,6 +117,12 @@
<style> <style>
td { vertical-align: top; } td { vertical-align: top; }
.year, .month{
border: 1px solid;
}
.selected td:not(.year):not(.month){
background: navy;
}
</style> </style>
<h1>{t('timetracking')}</h1> <h1>{t('timetracking')}</h1>
@@ -63,6 +133,8 @@
<table class="timetracks"> <table class="timetracks">
<thead> <thead>
<tr> <tr>
<th>{t('year')}</th>
<th>{t('month')}</th>
<th>{t('start_end')}</th> <th>{t('start_end')}</th>
<th>{t('duration')}</th> <th>{t('duration')}</th>
<th>{t('subject')}</th> <th>{t('subject')}</th>
@@ -71,35 +143,44 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#each sortedTimes as time} {#each sortedTimes as time,line}
<tr class={selected[time.id]?'selected':''}>
{#if timeMap.years[line]}
<td class="year" rowspan={timeMap.years[line]} onclick={e => toggleRange(time.start_time.substring(0,4))}>
{time.start_time.substring(0,4)}
</td>
{/if}
{#if timeMap.months[line]}
<td class="month" rowspan={timeMap.months[line]} onclick={e => toggleRange(time.start_time.substring(0,7))}>
{time.start_time.substring(5,7)}
</td>
{/if}
{#if detail == time.id} {#if detail == time.id}
<tr>
<td colspan="5"> <td colspan="5">
<TimeEditor record={time} onSet={update} /> <TimeEditor record={time} onSet={update} />
</td> </td>
</tr>
{:else} {:else}
<tr onclick={e => {detail = time.id}}> <td class="start_end" onclick={e => toggleSelect(time.id)}>
<td class="start_end">
{time.start_time}{#if time.end_time}{time.end_time}{/if} {time.start_time}{#if time.end_time}{time.end_time}{/if}
</td> </td>
<td class="duration">{#if time.duration} <td class="duration" onclick={e => {detail = time.id}}>
{#if time.duration}
{time.duration.toFixed(3)}&nbsp;h {time.duration.toFixed(3)}&nbsp;h
{/if} {/if}
</td> </td>
<td class="subject"> <td class="subject" onclick={e => {detail = time.id}}>
{time.subject} {time.subject}
</td> </td>
<td class="tasks"> <td class="tasks" onclick={e => {detail = time.id}}>
{#each Object.entries(time.tasks) as [tid,task]} {#each Object.entries(time.tasks) as [tid,task]}
<a href="#" onclick={e => openTask(tid)}>{task}</a> <a href="#" onclick={e => openTask(tid)}>{task}</a>
{/each} {/each}
</td> </td>
<td class="state"> <td class="state" onclick={e => {detail = time.id}}>
{t("state_"+time.state.name.toLowerCase())} {t("state_"+time.state.name.toLowerCase())}
</td> </td>
</tr>
{/if} {/if}
</tr>
{/each} {/each}
</tbody> </tbody>
</table> </table>