Compare commits
4 commits
a69c474d1d
...
ad796dcb57
Author | SHA1 | Date | |
---|---|---|---|
ad796dcb57 | |||
9ed5f15b9e | |||
f4f03ab491 | |||
34e54fa4af |
10 changed files with 78 additions and 25 deletions
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
||||||
import { mdiClose } from "@mdi/js";
|
import { mdiClose } from "@mdi/js";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
import { Debouncer } from "$lib/shared/util";
|
import { Debouncer } from "$lib/shared/util";
|
||||||
|
|
||||||
|
@ -192,8 +193,23 @@
|
||||||
searchDebounce.now();
|
searchDebounce.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onWindowKeyup(e: KeyboardEvent): void {
|
||||||
|
// Dont catch keybinds when inputting text
|
||||||
|
if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
|
||||||
|
if (e.key === "f") {
|
||||||
|
e.preventDefault();
|
||||||
|
focusInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (hiddenIds.size === 0) activeFilters = activeFilters;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<svelte:window on:keyup={onWindowKeyup} />
|
||||||
|
|
||||||
<div class="filterbar-outer">
|
<div class="filterbar-outer">
|
||||||
<div class="filterbar-inner input input-sm input-bordered">
|
<div class="filterbar-inner input input-sm input-bordered">
|
||||||
{#each activeFilters as fdata, i}
|
{#each activeFilters as fdata, i}
|
||||||
|
|
|
@ -41,6 +41,15 @@
|
||||||
}
|
}
|
||||||
} : undefined;
|
} : undefined;
|
||||||
|
|
||||||
|
function acceptTextInput(e: Event): void {
|
||||||
|
// @ts-expect-error Event is from HTML input
|
||||||
|
if (e.target?.value) {
|
||||||
|
// @ts-expect-error Input value is checked
|
||||||
|
fdata.selection = { id: null, name: e.target.value };
|
||||||
|
}
|
||||||
|
stopEditing(true);
|
||||||
|
}
|
||||||
|
|
||||||
$: if (fdata.editing && autocomplete) {
|
$: if (fdata.editing && autocomplete) {
|
||||||
autocomplete.open();
|
autocomplete.open();
|
||||||
}
|
}
|
||||||
|
@ -118,14 +127,10 @@
|
||||||
}}
|
}}
|
||||||
on:keypress={(e) => {
|
on:keypress={(e) => {
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
// @ts-expect-error Input value is checked
|
acceptTextInput(e);
|
||||||
if (e.target?.value) {
|
|
||||||
// @ts-expect-error Input value is checked
|
|
||||||
fdata.selection = { id: null, name: e.target.value };
|
|
||||||
}
|
|
||||||
stopEditing(true);
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
on:blur={acceptTextInput}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
@ -1,6 +1,21 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { humanDate } from "$lib/shared/util";
|
||||||
|
|
||||||
|
function dateIn(n: number): string {
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() + n);
|
||||||
|
return humanDate(date);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<div class="join">
|
<div class="join">
|
||||||
{#each { length: 4 } as _, i}
|
{#each { length: 4 } as _, i}
|
||||||
<button name="todo" class="join-item btn btn-sm" type="submit" value={i}>
|
<button
|
||||||
|
name="todo"
|
||||||
|
class="join-item btn btn-sm"
|
||||||
|
title={i > 0 ? `Eintrag auf ${dateIn(i)} verschieben` : undefined}
|
||||||
|
type="submit"
|
||||||
|
value={i}>
|
||||||
{#if i === 0}
|
{#if i === 0}
|
||||||
Notiz
|
Notiz
|
||||||
{:else}
|
{:else}
|
||||||
|
|
|
@ -130,6 +130,10 @@ class SearchQueryComponents {
|
||||||
* Supported search syntax:
|
* Supported search syntax:
|
||||||
* - Negative query `-word`
|
* - Negative query `-word`
|
||||||
* - Exact query `"word"`
|
* - Exact query `"word"`
|
||||||
|
*
|
||||||
|
* The last word in a search query is prefix-matched (i.e.
|
||||||
|
* the search returns all results starting with the given characters).
|
||||||
|
* This allows for meaningful results in Search-as-you-type applications.
|
||||||
*/
|
*/
|
||||||
export function parseSearchQuery(q: string): SearchQueryComponents {
|
export function parseSearchQuery(q: string): SearchQueryComponents {
|
||||||
const regexpParts = /(-)?(?:"([^"]*)"|([^"\s]+))(?:\s|$)/g;
|
const regexpParts = /(-)?(?:"([^"]*)"|([^"\s]+))(?:\s|$)/g;
|
||||||
|
|
|
@ -77,6 +77,13 @@ function dateDiffInDays(a: Date, b: Date): number {
|
||||||
return Math.round((ts2 - ts1) / MS_PER_DAY);
|
return Math.round((ts2 - ts1) / MS_PER_DAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Format a date with a human-readable format
|
||||||
|
* - If the date is within +/- 3 days, output a textual format ("heute", "morgen", "vor 2 Tagen")
|
||||||
|
* - Otherwise, format it as DD.MM.YYYY (or DD.MM.YYYY, hh:mm if `time=true`)
|
||||||
|
*
|
||||||
|
* @param [time=false] Enable time display
|
||||||
|
* @param [cap=false] Enable capitalized format
|
||||||
|
*/
|
||||||
export function humanDate(date: Date | string, time = false, cap = false): string {
|
export function humanDate(date: Date | string, time = false, cap = false): string {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const dt = coerceDate(date);
|
const dt = coerceDate(date);
|
||||||
|
@ -96,7 +103,7 @@ export function humanDate(date: Date | string, time = false, cap = false): strin
|
||||||
if (diffDays !== 0) {
|
if (diffDays !== 0) {
|
||||||
if (diffDays === 1) return outstr("morgen");
|
if (diffDays === 1) return outstr("morgen");
|
||||||
if (diffDays === -1) return outstr("gestern");
|
if (diffDays === -1) return outstr("gestern");
|
||||||
return intl.format(diffDays, "day");
|
return outstr(intl.format(diffDays, "day"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time) {
|
if (time) {
|
||||||
|
|
|
@ -158,7 +158,11 @@ export function defaultVisitUrl(): string {
|
||||||
}, URL_VISIT);
|
}, URL_VISIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function moveEntryTodoDate(id: number, nTodoDays: number, init?: TRPCClientInit) {
|
export async function moveEntryTodoDate(
|
||||||
|
id:number,
|
||||||
|
nTodoDays: number,
|
||||||
|
init?: TRPCClientInit,
|
||||||
|
): Promise<Date | null> {
|
||||||
if (nTodoDays > 0) {
|
if (nTodoDays > 0) {
|
||||||
const entry = await trpc(init).entry.get.query(id);
|
const entry = await trpc(init).entry.get.query(id);
|
||||||
const newDate = new Date();
|
const newDate = new Date();
|
||||||
|
@ -171,5 +175,7 @@ export async function moveEntryTodoDate(id: number, nTodoDays: number, init?: TR
|
||||||
date: utcDateToYMD(newDate),
|
date: utcDateToYMD(newDate),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
return newDate;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { superValidate, message } from "sveltekit-superforms";
|
||||||
|
|
||||||
import { ZUrlEntityId } from "$lib/shared/model/validation";
|
import { ZUrlEntityId } from "$lib/shared/model/validation";
|
||||||
import { trpc } from "$lib/shared/trpc";
|
import { trpc } from "$lib/shared/trpc";
|
||||||
import { loadWrap, moveEntryTodoDate } from "$lib/shared/util";
|
import { humanDate, loadWrap, moveEntryTodoDate } from "$lib/shared/util";
|
||||||
|
|
||||||
import { SchemaNewExecution } from "./schema";
|
import { SchemaNewExecution } from "./schema";
|
||||||
|
|
||||||
|
@ -22,18 +22,21 @@ export const actions: Actions = {
|
||||||
const done = todoDays === null;
|
const done = todoDays === null;
|
||||||
const nTodoDays = todoDays ? parseInt(todoDays.toString()) : 0;
|
const nTodoDays = todoDays ? parseInt(todoDays.toString()) : 0;
|
||||||
|
|
||||||
await loadWrap(async () => {
|
if (form.data.text.length > 0) {
|
||||||
await trpc(event).entry.newExecution.mutate({
|
await trpc(event).entry.newExecution.mutate({
|
||||||
id,
|
id,
|
||||||
old_execution_id: form.data.old_execution_id,
|
old_execution_id: form.data.old_execution_id,
|
||||||
execution: { text: form.data.text, done: todoDays === null },
|
execution: { text: form.data.text, done: todoDays === null },
|
||||||
});
|
});
|
||||||
await moveEntryTodoDate(id, nTodoDays, event);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (nTodoDays > 0) {
|
|
||||||
return message(form, `Eintrag um ${nTodoDays} Tage in die Zukunft verschoben`);
|
|
||||||
}
|
}
|
||||||
return message(form, done ? "Eintrag erledigt" : "Eintrag mit Notiz versehen");
|
const newTodoDate = await moveEntryTodoDate(id, nTodoDays, event);
|
||||||
|
|
||||||
|
if (newTodoDate) {
|
||||||
|
return message(form, `Eintrag auf ${humanDate(newTodoDate)} verschoben`);
|
||||||
|
}
|
||||||
|
if (form.data.text.length > 0) {
|
||||||
|
return message(form, done ? "Eintrag erledigt" : "Eintrag mit Notiz versehen");
|
||||||
|
}
|
||||||
|
return { form };
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
import { superForm } from "sveltekit-superforms";
|
import { superForm } from "sveltekit-superforms";
|
||||||
|
@ -33,7 +32,7 @@
|
||||||
label="Eintrag erledigen"
|
label="Eintrag erledigen"
|
||||||
bind:value={$form.text}
|
bind:value={$form.text}
|
||||||
>
|
>
|
||||||
<div class="row c-vlight gap-2">
|
<div class="row c-vlight gap-2 flex-wrap">
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Erledigt</button>
|
<button class="btn btn-sm btn-primary" type="submit">Erledigt</button>
|
||||||
<EntryTodoButton />
|
<EntryTodoButton />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { superValidate } from "sveltekit-superforms";
|
||||||
|
|
||||||
import { ZUrlEntityId } from "$lib/shared/model/validation";
|
import { ZUrlEntityId } from "$lib/shared/model/validation";
|
||||||
import { trpc } from "$lib/shared/trpc";
|
import { trpc } from "$lib/shared/trpc";
|
||||||
import { loadWrap, moveEntryTodoDate } from "$lib/shared/util";
|
import { loadWrap } from "$lib/shared/util";
|
||||||
|
|
||||||
import { SchemaNewExecution } from "../schema";
|
import { SchemaNewExecution } from "../schema";
|
||||||
|
|
||||||
|
@ -29,9 +29,6 @@ export const actions: Actions = {
|
||||||
old_execution_id: form.data.old_execution_id,
|
old_execution_id: form.data.old_execution_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const nTodoDays = todoDays ? parseInt(todoDays.toString()) : 0;
|
|
||||||
await moveEntryTodoDate(id, nTodoDays, event);
|
|
||||||
|
|
||||||
redirect(302, `/entry/${id}`);
|
redirect(302, `/entry/${id}`);
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
import { superformConfig } from "$lib/shared/util";
|
import { superformConfig } from "$lib/shared/util";
|
||||||
|
|
||||||
import EntryBody from "$lib/components/entry/EntryBody.svelte";
|
import EntryBody from "$lib/components/entry/EntryBody.svelte";
|
||||||
import EntryTodoButton from "$lib/components/ui/EntryTodoButton.svelte";
|
|
||||||
import MarkdownInput from "$lib/components/ui/markdown/MarkdownInput.svelte";
|
import MarkdownInput from "$lib/components/ui/markdown/MarkdownInput.svelte";
|
||||||
|
|
||||||
import { SchemaNewExecution } from "../schema";
|
import { SchemaNewExecution } from "../schema";
|
||||||
|
@ -32,7 +31,9 @@
|
||||||
>
|
>
|
||||||
<div class="row c-vlight gap-2">
|
<div class="row c-vlight gap-2">
|
||||||
<button class="btn btn-sm btn-primary" type="submit">Speichern</button>
|
<button class="btn btn-sm btn-primary" type="submit">Speichern</button>
|
||||||
<EntryTodoButton />
|
<button name="todo" class="join-item btn btn-sm" type="submit" value="0">
|
||||||
|
Notiz
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</MarkdownInput>
|
</MarkdownInput>
|
||||||
<input name="old_execution_id" type="hidden" bind:value={$form.old_execution_id} />
|
<input name="old_execution_id" type="hidden" bind:value={$form.old_execution_id} />
|
||||||
|
|
Loading…
Reference in a new issue