Compare commits

...

9 commits

Author SHA1 Message Date
a69c474d1d
chore(release): release v0.3.4
All checks were successful
Visitenbuch CI / test (push) Successful in 2m40s
Visitenbuch CI / release (push) Successful in 5m46s
2024-05-16 20:31:46 +02:00
c6abf633f8
feat: select table entries on doubleclick
All checks were successful
Visitenbuch CI / test (push) Successful in 2m22s
Visitenbuch CI / release (push) Has been skipped
2024-05-16 20:19:55 +02:00
88a5040f9c
fix: autocomplete not closing on tab 2024-05-16 19:42:26 +02:00
cdb344609c
fix: remove process.on hooks (not necessary) 2024-05-16 18:47:16 +02:00
4a3155c33a
fix: close autocomplete on defocus 2024-05-16 18:45:22 +02:00
a4eebb944f
fix: avoid global state, use context for savedFilters
All checks were successful
Visitenbuch CI / test (push) Successful in 2m38s
Visitenbuch CI / release (push) Has been skipped
2024-05-16 17:29:22 +02:00
d5e9a9469f
fix: use btn-id class for all tables 2024-05-14 22:51:20 +02:00
f76e7fd97f
fix: escape HTML for licenses file
All checks were successful
Visitenbuch CI / test (push) Successful in 2m20s
Visitenbuch CI / release (push) Has been skipped
2024-05-14 16:28:23 +02:00
068c7961ae
ci: set fetch-depth to 1 to extract tag messages
All checks were successful
Visitenbuch CI / test (push) Successful in 2m21s
Visitenbuch CI / release (push) Has been skipped
2024-05-14 16:07:25 +02:00
15 changed files with 87 additions and 51 deletions

View file

@ -50,6 +50,7 @@ jobs:
id: e2etest
run: |
pnpm run build -l silent
npx playwright install chromium
pnpm run test:e2e
- name: 💢 Upload E2E report
if: ${{ failure() && steps.e2etest.conclusion == 'failure' }}
@ -67,7 +68,7 @@ jobs:
- name: 👁️ Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-depth: 1 # important to fetch tag logs
- name: 📦 pnpm install
run: pnpm install

View file

@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
## [v0.3.4](https://code.thetadev.de/HSA/Visitenbuch/compare/v0.3.3..v0.3.4) - 2024-05-16
### 🚀 Features
- Select table entries on doubleclick - ([c6abf63](https://code.thetadev.de/HSA/Visitenbuch/commit/c6abf633f8ae5e9b562dda36f9f7ab4d6adcb4e1))
### 🐛 Bug Fixes
- Escape HTML for licenses file - ([f76e7fd](https://code.thetadev.de/HSA/Visitenbuch/commit/f76e7fd97f62d9b41ecbabc3334c2c1876be253d))
- Use btn-id class for all tables - ([d5e9a94](https://code.thetadev.de/HSA/Visitenbuch/commit/d5e9a9469f0c57939367141985a97d8404fd6fbe))
- Avoid global state, use context for savedFilters - ([a4eebb9](https://code.thetadev.de/HSA/Visitenbuch/commit/a4eebb944f55da8e87cc899eebada0bd3fd37aa8))
- Close autocomplete on defocus - ([4a3155c](https://code.thetadev.de/HSA/Visitenbuch/commit/4a3155c33aa354973d4e0ca3ffeab2b7fd442040))
- Remove process.on hooks (not necessary) - ([cdb3446](https://code.thetadev.de/HSA/Visitenbuch/commit/cdb344609cde80084876faea9f80e7b26b01d0f2))
- Autocomplete not closing on tab - ([88a5040](https://code.thetadev.de/HSA/Visitenbuch/commit/88a5040f9c4e19ae3efb5ad0894c8dc5b905a92e))
## [v0.3.3](https://code.thetadev.de/HSA/Visitenbuch/compare/v0.3.2..v0.3.3) - 2024-05-14
### 🚀 Features

View file

@ -1,6 +1,6 @@
{
"name": "visitenbuch",
"version": "0.3.3",
"version": "0.3.4",
"private": true,
"license": "AGPL-3.0",
"scripts": {

View file

@ -1,5 +1,5 @@
<!doctype html>
<html lang="en">
<html lang="de">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />

View file

@ -31,7 +31,3 @@ export const handle = sequence(
authorization,
createTRPCHandle({ router, createContext }),
);
// Allow server application to exit
process.on("SIGINT", () => process.exit()); // Ctrl+C
process.on("SIGTERM", () => process.exit()); // docker stop

View file

@ -166,6 +166,14 @@
if (opened) {
onClose(kb);
}
// select remaining item if autoselect is enabled
if (!selection) {
if (!noAutoselect1 && filteredItems.length === 1) {
selectListItem(filteredItems[0], true);
} else {
setInputValue("");
}
}
opened = false;
}
@ -185,43 +193,38 @@
}
function onKeyDown(e: KeyboardEvent): void {
let { key } = e;
if (key === "Tab" && e.shiftKey) key = "ShiftTab";
const fnmap: Record<string, () => void> = {
Tab: () => close,
ShiftTab: () => close,
ArrowDown: () => {
switch (e.key) {
case "Tab":
close();
break;
case "ArrowDown":
open();
if (highlightIndex < filteredItems.length - 1) {
highlightIndex++;
highlight();
}
},
ArrowUp: () => {
break;
case "ArrowUp":
open();
if (highlightIndex > 0) {
highlightIndex--;
highlight();
}
},
Escape: () => {
break;
case "Escape":
e.stopPropagation();
if (opened) {
if (inputElm) inputElm.focus();
close();
}
},
Backspace: () => {
break;
case "Backspace":
if (inputValue().length === 0) {
onBackspace();
} else if (selection) {
clearSelection();
}
},
};
const fn = fnmap[key];
if (typeof fn === "function") {
fn();
break;
}
}
@ -234,16 +237,6 @@
}
}
function onBlur(): void {
if (!selection) {
if (!noAutoselect1 && filteredItems.length === 1) {
selectListItem(filteredItems[0], true);
} else {
setInputValue("");
}
}
}
function highlight(): void {
if (browser && opened) {
window.setTimeout(() => {
@ -303,12 +296,11 @@
on:focus={open}
on:keydown={onKeyDown}
on:keypress={onKeyPress}
on:blur={onBlur}
use:floatingRef
/>
{#if opened && filteredItems.length > 0}
<div bind:this={listElm} class="autocomplete-list" use:floatingContent>
<div bind:this={listElm} class="autocomplete-list" tabindex="-1" use:floatingContent>
{#each filteredItems as item, i}
<div
class="autocomplete-list-item"
@ -344,7 +336,12 @@
{#if clearBtn && selection}
<div class="absolute bottom-0 right-0 h-full flex items-center">
<IconButton cls="" path={mdiClose} title="Löschen" on:click={clearSelection} />
<IconButton
cls=""
path={mdiClose}
tabindex={-1}
title="Löschen"
on:click={clearSelection} />
</div>
{/if}
</div>

View file

@ -8,12 +8,14 @@
import { toastError, toastInfo } from "$lib/shared/util/toast";
import Icon from "$lib/components/ui/Icon.svelte";
import { savedFilters } from "$lib/stores";
import { getSavedFilters } from "$lib/stores";
import Chip from "./SavedFilterChip.svelte";
export let view: string;
const savedFilters = getSavedFilters();
$: filters = $savedFilters[view] ?? [];
function getQuery(): string {

View file

@ -1,4 +1,6 @@
<script lang="ts">
import { goto } from "$app/navigation";
import type { RouterOutput } from "$lib/shared/trpc";
import { formatDate } from "$lib/shared/util";
@ -39,6 +41,7 @@
class="transition-colors hover:bg-neutral-content/10"
class:done={entry.execution?.done}
class:priority={entry.current_version.priority}
on:dblclick={() => { void goto("/entry/" + entry.id); }}
>
<td
><a

View file

@ -1,4 +1,6 @@
<script lang="ts">
import { goto } from "$app/navigation";
import { mdiFilter } from "@mdi/js";
import { URL_ENTRIES } from "$lib/shared/constants";
@ -34,10 +36,11 @@
<tr
class="transition-colors hover:bg-neutral-content/10"
class:p-hidden={patient.hidden}
on:dblclick={() => { void goto("/patient/" + patient.id); }}
>
<td
><a
class="btn btn-xs btn-primary"
class="btn btn-xs btn-primary btn-id"
aria-label="Eintrag anzeigen"
href="/patient/{patient.id}">{patient.id}</a
></td

View file

@ -1,3 +1,4 @@
import { getContext } from "svelte";
import { derived, writable, type Writable } from "svelte/store";
import type { SavedFilter } from "$lib/shared/model";
@ -6,4 +7,6 @@ import type { SavedFilter } from "$lib/shared/model";
export const screenWidth = writable(0);
export const screenWidthSmall = derived(screenWidth, ($mainWidth) => $mainWidth < 500);
export const savedFilters: Writable<Record<string, SavedFilter[]>> = writable({});
// Context key: "savedFilters"
export type SavedFilters = Writable<Record<string, SavedFilter[]>>;
export const getSavedFilters: () => SavedFilters = () => getContext("savedFilters");

View file

@ -3,16 +3,22 @@
import type { LayoutData } from "./$types";
import { mdiAccount, mdiHome } from "@mdi/js";
import { setContext } from "svelte";
import { writable } from "svelte/store";
import { defaultFilterUrl, defaultVisitUrl } from "$lib/shared/util";
import Icon from "$lib/components/ui/Icon.svelte";
import NavLink from "$lib/components/ui/NavLink.svelte";
import { savedFilters } from "$lib/stores";
import type { SavedFilters } from "$lib/stores";
export let data: LayoutData;
const savedFilters: SavedFilters = writable();
$: savedFilters.set(data.savedFilters);
setContext("savedFilters", savedFilters);
</script>
<div class="navbar-outer">

View file

@ -4,9 +4,11 @@
import { defaultFilterUrl } from "$lib/shared/util";
import { savedFilters } from "$lib/stores";
import { getSavedFilters } from "$lib/stores";
export let data: PageData;
const savedFilters = getSavedFilters();
</script>
<svelte:head>

View file

@ -27,7 +27,7 @@
<tbody>
{#each data.rooms as room (room.id)}
<tr>
<td><a class="btn btn-sm" href="/room/{room.id}">{room.name}</a></td>
<td><a class="btn btn-sm btn-id" href="/room/{room.id}">{room.name}</a></td>
<td>{room.station.name}</td>
</tr>
{/each}

View file

@ -27,7 +27,7 @@
{#each data.stations as station (station.id)}
<tr>
<td>
<a class="btn btn-sm" href="/station/{station.id}">{station.name}</a>
<a class="btn btn-sm btn-id" href="/station/{station.id}">{station.name}</a>
</td>
</tr>
{/each}

View file

@ -23,7 +23,8 @@ export default defineConfig({
createViteLicensePlugin({
additionalFiles: {
"oss-licenses.html": (packages) => {
let res = `<html>
let res = `<!DOCTYPE html>
<html lang="en">
<head>
<title>Visitenbuch - Lizenzen</title>
</head>
@ -31,6 +32,12 @@ export default defineConfig({
<h1>Open-Source-Lizenzen</h1>
<a href="./oss-licenses.json">JSON-formatted license list</a>
`;
const escapeHTML = (s: string | null) => s ? s.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#x27;") : "";
for (const _p of packages) {
type LicenseMetaExt = LicenseMeta & {
repository: string | null,
@ -54,13 +61,13 @@ export default defineConfig({
}
res += `<div class="package">\n`;
res += `<h3><a href="https://www.npmjs.com/package/${p.name}" target="_blank" rel="noopener noreferrer">${p.name}</a></h3>\n`;
res += `<h3><a href="https://www.npmjs.com/package/${escapeHTML(p.name)}" target="_blank" rel="noopener noreferrer">${escapeHTML(p.name)}</a></h3>\n`;
res += `<table>\n`;
res += `<tr><td>Version:</td><td>${p.version}</td></tr>\n`;
if (aut) res += `<tr><td>Author:</td><td>${aut}</td></tr>\n`;
res += `<tr><td>License:</td><td>${p.license}</td></tr>\n`;
if (repoUrl) res += `<tr><td>Repository:</td><td><a href="${repoUrl}" target="_blank" rel="noopener noreferrer">${repoUrl}</a></td></tr>\n`;
else if (rp) res += `<tr><td>Repository:</td><td>${rp}</td></tr>\n`;
res += `<tr><td>Version:</td><td>${escapeHTML(p.version)}</td></tr>\n`;
if (aut) res += `<tr><td>Author:</td><td>${escapeHTML(aut)}</td></tr>\n`;
res += `<tr><td>License:</td><td>${escapeHTML(p.license)}</td></tr>\n`;
if (repoUrl) res += `<tr><td>Repository:</td><td><a href="${escapeHTML(repoUrl)}" target="_blank" rel="noopener noreferrer">${escapeHTML(repoUrl)}</a></td></tr>\n`;
else if (rp) res += `<tr><td>Repository:</td><td>${escapeHTML(rp)}</td></tr>\n`;
res += `</table>\n`;
res += "</div>";
}