Merge branch 'feature/translation' into module/messagebus

This commit is contained in:
2026-01-16 21:45:49 +01:00
109 changed files with 2311 additions and 1275 deletions

View File

@@ -9,9 +9,9 @@
<div>
<div title={'task_'+task.id}>
{#if task.estimated_time}
{#if task.est_time}
<span class="estimate" onclick={() => onSelect(task)}>
{task.estimated_time}&nbsp;{t(task.estimated_time != 1 ? 'hours' : 'hour')}
{task.est_time}&nbsp;{t(task.est_time != 1 ? 'hours' : 'hour')}
</span>
{/if}
{task.name}

View File

@@ -23,7 +23,7 @@
item_code : t('estimated_time'),
title : estimate.name,
description : estimate.description.source,
amount : estimate.estimated_time,
amount : estimate.est_time,
unit : t('hours')
});
}

View File

@@ -2,13 +2,14 @@
import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router';
import { api } from '../../urls.svelte';
import { error, yikes } from '../../warn.svelte';
import { error, warn, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte';
import { user } from '../../user.svelte';
const image_extensions = ['jpg','jpeg','gif','png','svg','webp'];
const router = useTinyRouter();
let children = $state({});
let new_dir = $state(null);
let files = $state();
let parent = $state(false);
@@ -17,23 +18,6 @@
let delete_allowed = $state(false);
let available = $derived.by(isAvailable);
function isAvailable(){
if (!new_dir) return false;
if (children){
if (children.dirs) {
for (let key of Object.values(children.dirs)){
if (key == new_dir) return false;
}
}
if (children.files) {
for (let key of Object.values(children.files)){
if (key == new_dir) return false;
}
}
}
return true;
}
async function create_dir(ev){
ev.preventDefault();
ev.stopPropagation();
@@ -74,13 +58,30 @@
async function handleDirectory(res){
let json = await res.json();
children.dirs = json.dirs ? json.dirs : {};
children.files = json.files ? json.files : {};
children.dirs = json.dirs ? val_sort(json.dirs) : {};
children.files = json.files ? val_sort(json.files) : {};
children.title = json.title ? json.title : path;
delete_allowed = json.delete;
yikes();
}
function isAvailable(){
if (!new_dir) return false;
if (children){
if (children.dirs) {
for (let key of Object.values(children.dirs)){
if (key == new_dir) return false;
}
}
if (children.files) {
for (let key of Object.values(children.files)){
if (key == new_dir) return false;
}
}
}
return true;
}
function is_image(file){
let parts = file.toLowerCase().split('.');
let ext = parts.pop();
@@ -90,12 +91,15 @@
async function loadChildren(p){
p = p.substring(6);
if (p == '') p = '/';
children = { dirs : {}, files : {}, title : p};
children = { dirs : [], files : [], title : p};
path = p;
if (p == '/'){
children.dirs[`/user/${user.id}`] = t('my_files');
children.dirs['/project'] = t('projects')
children.dirs['/company'] = t('companies');
children.dirs = [
{ path : `/user/${user.id}`, name : t('my files') },
{ path : '/project', name : t('projects')},
{ path : '/company', name : t('companies')},
]
parent = false;
form = false;
} else {
@@ -114,8 +118,12 @@
function markdown(file){
let parts = file.split('/');
let md = `![${parts.pop()}](/api/files${file})`;
let path = `/api/files${file}`;
path = encodeURI(path);
let md = `![${parts.pop()}](${path})`;
navigator.clipboard.writeText(md);
warn(t('Markdown has been copied to clipboard!'));
setTimeout(yikes, 2500);
}
function onclick(ev){
@@ -153,6 +161,12 @@
return false;
}
function val_sort(map){
return Object.entries(map)
.map(item => ({name:item[1],path:item[0]}))
.sort((a,b) => a.name.localeCompare(b.name));
}
onMount(() => loadChildren(window.location.pathname));
</script>
@@ -166,12 +180,12 @@
</li>
{/if}
{#if children?.dirs}
{#each Object.entries(children.dirs) as [k,v]}
{#each children.dirs as dir}
<li class="dir">
<span class="symbol"></span>
<a href={'/files'+k} {onclick}>{v}</a>
<a href={'/files'+dir.path} {onclick}>{dir.name}</a>
{#if delete_allowed}
<button class="symbol" onclick={e => dropDir(`/api/files${k}`,v)}></button>
<button class="symbol" onclick={e => dropDir(`/api/files${dir.path}`,dir.name)}></button>
{/if}
</li>
{/each}
@@ -186,15 +200,15 @@
</li>
{/if}
{#if children.files}
{#each Object.entries(children.files) as [k,v]}
{#each children.files as file}
<li class="file">
<span class="symbol"></span>
<a href={`/api/files${k}`} target="_blank">{v}</a>
{#if is_image(k)}
<button class="symbol" title={'markdown_code'} onclick={e => markdown(k)}></button>
<a href={`/api/files${file.path}`} target="_blank">{file.name}</a>
{#if is_image(file.path)}
<button class="symbol" title={'markdown_code'} onclick={e => markdown(file.path)}></button>
{/if}
{#if delete_allowed}
<button class="symbol" title={t('delete_object',{'object':t('file')})} onclick={e => dropFile(`/api/files${k}`,v)}></button>
<button class="symbol" title={t('delete_object',{'object':t('file')})} onclick={e => dropFile(`/api/files${file.path}`,file.name)}></button>
{/if}
</li>
{/each}

View File

@@ -21,9 +21,9 @@
title={task.description.source}
>
<span class="title">{task.name}</span>
{#if task.estimated_time}
{#if task.est_time}
<span class="estimate">
({task.estimated_time}&nbsp;h)
({task.est_time}&nbsp;h)
</span>
{/if}
{#if task.tags}

View File

@@ -17,7 +17,7 @@
let eventSource = $state(null);
let { id } = $props();
let lastEvent = $state(null);
let estimated_time = $state({sum:0});
let est_time = $state({sum:0});
let project = $state(null);
let router = useTinyRouter();
let showSettings = $state(false);
@@ -135,7 +135,7 @@
});
if (resp.ok){
tasks = {};
estimated_time.sum = 0;
est_time.sum = 0;
tasks = await resp.json();
yikes();
} else {
@@ -283,9 +283,9 @@
{/each}
{/if} <!-- settings -->
{#if estimated_time.sum}
{#if est_time.sum}
<div>{t('estimated_time')}</div>
<div class="estimated_time">{estimated_time.sum} h</div>
<div class="estimated_time">{est_time.sum} h</div>
{/if}
<div>{t('description')}</div>
@@ -304,7 +304,7 @@
</div>
<div class="tasks">
{#if tasks}
<TaskList {tasks} {estimated_time} {lastEvent} {eventSource} states={project?.allowed_states} show_closed={show_closed || project.show_closed} />
<TaskList {tasks} {est_time} {lastEvent} {eventSource} states={project?.allowed_states} show_closed={show_closed || project.show_closed} />
{/if}
</div>
</div>

View File

@@ -19,7 +19,7 @@
name : '',
description : { source : '', rendered : '' },
due_date : null,
estimated_time : null,
est_time : null,
no_index : false,
show_closed : false,
start_date : null,
@@ -191,7 +191,7 @@
{t('estimated_time')}
</th>
<td>
<input type="number" bind:value={task.estimated_time} /> {t('hours')}
<input type="number" bind:value={task.est_time} /> {t('hours')}
</td>
</tr>
<tr>

View File

@@ -13,7 +13,7 @@
import LineEditor from '../../Components/LineEditor.svelte';
let {
estimated_time,
est_time,
lastEvent,
show_closed,
siblings,
@@ -137,8 +137,8 @@
return {state:0};
}
if (task.estimated_time){
estimated_time.sum += task.estimated_time;
if (task.est_time){
est_time.sum += task.est_time;
}
$effect(() => {
@@ -157,8 +157,8 @@
{#if !deleted}
<li draggable="true" {ondrop} ondragover={e => e.preventDefault()} {ondragstart} class="task {states[task.status]?.toLowerCase()}">
<LineEditor bind:value={task.name} onclick={openTask} editable={true} onSet={setName} type="a" href={`/task/${task.id}/view`} />
{#if task.estimated_time}
<span class="estimated_time">({+task.estimated_time}&nbsp;h)</span>
{#if task.est_time}
<span class="estimated_time">({+task.est_time}&nbsp;h)</span>
{/if}
<button class="symbol" title={t('drag_n_drop')}></button>
{#if states[task.status] != 'PENDING'}
@@ -180,7 +180,7 @@
<button class="symbol" title={t('add_object',{object:t('subtask')})} onclick={addSubtask}></button>
<button class="symbol" title={t('timetracking')} onclick={addTime}></button>
{#if children}
<TaskList {states} {lastEvent} tasks={children} {estimated_time} {show_closed} />
<TaskList {states} {lastEvent} tasks={children} {est_time} {show_closed} />
{/if}
</li>
{/if}

View File

@@ -2,13 +2,13 @@
import { t } from '../../translations.svelte.js';
import ListTask from './ListTask.svelte';
let { estimated_time, lastEvent, show_closed, states = {}, tasks } = $props();
let { est_time, lastEvent, show_closed, states = {}, tasks } = $props();
let sortedTasks = $derived.by(() => Object.values(tasks).sort((a, b) => a.name.localeCompare(b.name)));
</script>
<ul>
{#each sortedTasks as task}
<ListTask {states} {task} {lastEvent} siblings={tasks} {estimated_time} show_closed={show_closed || task.show_closed} />
<ListTask {states} {task} {lastEvent} siblings={tasks} {est_time} show_closed={show_closed || task.show_closed} />
{/each}
</ul>

View File

@@ -21,7 +21,7 @@
let { id } = $props();
let children = $state(null);
let dummy = $derived(updateOn(id));
let estimated_time = $state({sum:0});
let est_time = $state({sum:0});
let project = $state(null);
const router = useTinyRouter();
let showSettings = $state(router.fullPath.endsWith('/edit'));
@@ -251,9 +251,9 @@
<div>{t('due_date')}</div>
<div class="due date">{task.due_date}</div>
{/if}
{#if task.estimated_time}
{#if task.est_time}
<div>{t('estimated_time')}</div>
<div class="estimated time">{task.estimated_time} h</div>
<div class="estimated time">{task.est_time} h</div>
{/if}
{#if showSettings}
<div>{t('extended_settings')}</div>
@@ -284,7 +284,7 @@
<div>{t('estimated_time')}</div>
<div>
<input type="number" bind:value={task.estimated_time} onchange={() => update({estimated_time:task.estimated_time})} />&nbsp;h
<input type="number" bind:value={task.est_time} onchange={() => update({est_time:task.est_time})} />&nbsp;h
</div>
<div>{t('priority')}</div>
@@ -320,7 +320,7 @@
</div>
<div class="children">
{#if children}
<TaskList {eventSource} states={project?.allowed_states} tasks={children} {estimated_time} show_closed={task.show_closed} />
<TaskList {eventSource} states={project?.allowed_states} tasks={children} {est_time} show_closed={task.show_closed} />
{/if}
</div>

View File

@@ -15,5 +15,5 @@ export async function warn(msg){
export function yikes(){
messages.error = null;
messages.warn = null;
messages.warning = null;
}