Browse Source

implemented selection of timetrack entries and display of sum of durations of selected records

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
feature/join_times
Stephan Richter 2 months ago
parent
commit
829c1796b5
  1. 8
      frontend/src/Components/TimeRecordEditor.svelte
  2. 29
      frontend/src/routes/time/Index.svelte
  3. 7
      translations/src/main/resources/de.json
  4. 12
      web/src/main/resources/web/css/default.css

8
frontend/src/Components/TimeRecordEditor.svelte

@ -14,6 +14,10 @@
<form {onsubmit}> <form {onsubmit}>
<fieldset> <fieldset>
<legend>{t('edit_object',{object:t('record')})}</legend> <legend>{t('edit_object',{object:t('record')})}</legend>
<label>
{t('subject')}
<input type="text" bind:value={record.subject} />
</label>
<label> <label>
{t('start')} {t('start')}
<input type="datetime-local" bind:value={record.start_time} /> <input type="datetime-local" bind:value={record.start_time} />
@ -22,10 +26,6 @@
{t('end')} {t('end')}
<input type="datetime-local" bind:value={record.end_time} /> <input type="datetime-local" bind:value={record.end_time} />
</label> </label>
<label>
{t('subject')}
<input type="text" bind:value={record.subject} />
</label>
<label> <label>
{t('description')} {t('description')}
<MarkdownEditor simple={true} bind:value={record.description} /> <MarkdownEditor simple={true} bind:value={record.description} />

29
frontend/src/routes/time/Index.svelte

@ -13,7 +13,10 @@
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 selected = $state({});
let ranges = {}; let ranges = {};
let timeMap = $derived.by(() => { let timeMap = $derived.by(calcYearMap);
let selectionSum = $derived(sortedTimes.filter(time => selected[time.id]).map(time => time.duration).reduce((acc, a) => acc + a, 0));
function calcYearMap(){
let result = { let result = {
months : {}, months : {},
years : {} years : {}
@ -46,13 +49,6 @@
} }
if (yearCount) result.years[yearIndex] = yearCount; if (yearCount) result.years[yearIndex] = yearCount;
if (monthCount) result.months[monthIndex] = monthCount; 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; return result;
} }
@ -115,27 +111,22 @@
onMount(loadTimes); onMount(loadTimes);
</script> </script>
<style>
td { vertical-align: top; }
.year, .month{
border: 1px solid;
}
.selected td:not(.year):not(.month){
background: navy;
}
</style>
<h1>{t('timetracking')}</h1> <h1>{t('timetracking')}</h1>
{#if error} {#if error}
<span class="error">{error}</span> <span class="error">{error}</span>
{/if} {/if}
{#if times} {#if times}
{#if selectionSum}
<div class="timetracks sum">
{t('sum_of_records')}: <span>{selectionSum.toFixed(3)}&nbsp;{t('hours')}</span>
</div>
{/if}
<table class="timetracks"> <table class="timetracks">
<thead> <thead>
<tr> <tr>
<th>{t('year')}</th> <th>{t('year')}</th>
<th>{t('month')}</th> <th>{t('month')}</th>
<th>{t('start_end')}</th> <th>{t('start')}{t('end')}</th>
<th>{t('duration')}</th> <th>{t('duration')}</th>
<th>{t('subject')}</th> <th>{t('subject')}</th>
<th>{t('tasks')}</th> <th>{t('tasks')}</th>

7
translations/src/main/resources/de.json

@ -59,6 +59,7 @@
"do_open" : "öffnen", "do_open" : "öffnen",
"do_send" : "versenden", "do_send" : "versenden",
"due_date": "Fälligkeitsdatum", "due_date": "Fälligkeitsdatum",
"duration": "Dauer",
"edit": "Bearbeiten", "edit": "Bearbeiten",
"edit_object" : "{object} bearbeiten", "edit_object" : "{object} bearbeiten",
@ -67,6 +68,7 @@
"edit_service": "Login-Service \"{0}\" bearbeiten", "edit_service": "Login-Service \"{0}\" bearbeiten",
"email": "E-Mail", "email": "E-Mail",
"email_or_username": "Email oder Nutzername", "email_or_username": "Email oder Nutzername",
"end": "Ende",
"estimated_time": "geschätzte Zeit", "estimated_time": "geschätzte Zeit",
"estimated_times": "geschätzte Zeiten", "estimated_times": "geschätzte Zeiten",
"extended_settings": "erweiterte Einstellungen", "extended_settings": "erweiterte Einstellungen",
@ -137,6 +139,7 @@
"wiki": "Wiki" "wiki": "Wiki"
}, },
"mismatch": "ungleich", "mismatch": "ungleich",
"month": "Monat",
"must_not_be_empty": "darf nicht leer sein", "must_not_be_empty": "darf nicht leer sein",
"name": "Name", "name": "Name",
@ -197,6 +200,7 @@
"show": "anzeigen", "show": "anzeigen",
"show_closed": "geschlossene anzeigen", "show_closed": "geschlossene anzeigen",
"show_kanban": "Kanban-Ansicht", "show_kanban": "Kanban-Ansicht",
"start": "Start",
"start_date": "Startdatum", "start_date": "Startdatum",
"started": "angefangen", "started": "angefangen",
"state": "Status", "state": "Status",
@ -220,7 +224,7 @@
"subject": "Betreff", "subject": "Betreff",
"subtask": "Unteraufgabe", "subtask": "Unteraufgabe",
"subtasks": "Unteraufgaben", "subtasks": "Unteraufgaben",
"sum_of_records": "Summe der ausgewählten Einträge",
"tag_uses": "Verwendung des Tags „{tag}“", "tag_uses": "Verwendung des Tags „{tag}“",
"tags": "Tags", "tags": "Tags",
"task": "Aufgabe", "task": "Aufgabe",
@ -254,6 +258,7 @@
"welcome" : "Willkommen, {0}", "welcome" : "Willkommen, {0}",
"wiki": "Wiki", "wiki": "Wiki",
"year": "Jahr",
"your_password_reset_token" : "Ihr Token zum Erstellen eines neuen Passworts", "your_password_reset_token" : "Ihr Token zum Erstellen eines neuen Passworts",
"your_profile": "dein Profil" "your_profile": "dein Profil"
} }

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

@ -301,3 +301,15 @@ li > a > p:nth-child(1){
border-radius: 5px; border-radius: 5px;
padding: 12px 5px 3px 5px; padding: 12px 5px 3px 5px;
} }
.timetracks .year, .month{
border: 1px solid;
}
.timetracks .selected td:not(.year):not(.month){
background: navy;
}
.timetracks.sum span{
font-weight: bold;
}
Loading…
Cancel
Save