diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 595addb..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,41 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - - -## [v0.2.0](https://code.thetadev.de/HSA/Visitenbuch/compare/v0.1.0..v0.2.0) - 2024-05-06 - -### 🚀 Features - -- Add toast error messages - ([80c6243](https://code.thetadev.de/HSA/Visitenbuch/commit/80c6243e2b1fe1c8c92b57809c094219be666065)) -- Improve week selector, select all TODO items by default - ([21f145f](https://code.thetadev.de/HSA/Visitenbuch/commit/21f145f5f08e4f44beecd3d373dfc69e5fc94f23)) -- Use global store for saved filters, add default filters - ([f36ae71](https://code.thetadev.de/HSA/Visitenbuch/commit/f36ae71d32ebcfc5054dca9c6907ffc1554f39e3)) -- Hide rooms/stations/categories - ([2cb8dce](https://code.thetadev.de/HSA/Visitenbuch/commit/2cb8dce51a880d46af3a77ee9cbdfce680b88755)) -- Add about page, licenses - ([5230ee3](https://code.thetadev.de/HSA/Visitenbuch/commit/5230ee375c7b25e577dc2ae045309d8cd54cfdf4)) - -### 🐛 Bug Fixes - -- Date timezone issue, refactor utils - ([efb0e24](https://code.thetadev.de/HSA/Visitenbuch/commit/efb0e246127489d618ca6e3ea50f10dd1140a954)) -- Normalize line endings - ([799dbc4](https://code.thetadev.de/HSA/Visitenbuch/commit/799dbc4097b5cf72e813e67b6ca6ccfe6ab5f08b)) -- Light/dark theme - ([8630b6a](https://code.thetadev.de/HSA/Visitenbuch/commit/8630b6a32d850e93ff1b426a92f68400f7b2f666)) -- Not using UTC dates for parsing date ranges in backend - ([519ae01](https://code.thetadev.de/HSA/Visitenbuch/commit/519ae01eeec294f7522c5ad9bcf8f2b6b44539fb)) - -### 🚜 Refactor - -- Added return types - ([e6302f3](https://code.thetadev.de/HSA/Visitenbuch/commit/e6302f380b903e3ab3f4ef6b5c6e1483d2939027)) - -### ⚙️ Miscellaneous Tasks - -- Update dependencies - ([e3f7341](https://code.thetadev.de/HSA/Visitenbuch/commit/e3f7341a0e575a9b0cb922c9b697c21f6f6875c6)) -- Add git-cliff - ([1b96a46](https://code.thetadev.de/HSA/Visitenbuch/commit/1b96a46dcf2b34318bed5fc383a839a6d7cfd25e)) - -### Build - -- Remove docker multistage build, add entrypoint - ([be52e70](https://code.thetadev.de/HSA/Visitenbuch/commit/be52e70e8d5abed676c9594f2de0e749b7d2da08)) - - -## [v0.1.0](https://code.thetadev.de/HSA/Visitenbuch/commits/tag/v0.1.0) - 2024-05-06 - -Initial release - - diff --git a/cliff.toml b/cliff.toml deleted file mode 100644 index 8593819..0000000 --- a/cliff.toml +++ /dev/null @@ -1,100 +0,0 @@ -# git-cliff ~ default configuration file -# https://git-cliff.org/docs/configuration -# -# Lines starting with "#" are comments. -# Configuration options are organized into tables and keys. -# See documentation for more information on available options. - -[changelog] -# changelog header -header = """ -# Changelog\n -All notable changes to this project will be documented in this file.\n -""" -# template for the changelog body -# https://keats.github.io/tera/docs/#introduction -body = """ -{% set repo_url = "https://code.thetadev.de/HSA/Visitenbuch" %}\ -{% if version %}\ - {%set vname = version | split(pat="/") | last %} - {%if previous.version %}\ - ## [{{ vname }}]({{ repo_url }}/compare/{{ previous.version }}..{{ version }})\ - {% else %}\ - ## [{{ vname }}]({{ repo_url }}/commits/tag/{{ version }})\ - {% endif %} - {{ timestamp | date(format="%Y-%m-%d") }} -{% else %}\ - ## [unreleased] -{% endif %}\ -{% if previous.version %}\ -{% for group, commits in commits | group_by(attribute="group") %} - ### {{ group | striptags | trim | upper_first }} - {% for commit in commits %} - - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ - {% if commit.breaking %}[**breaking**] {% endif %}\ - {{ commit.message | upper_first }} - \ - ([{{ commit.id | truncate(length=7, end="") }}]({{ repo_url }}/commit/{{ commit.id }}))\ - {% endfor %} -{% endfor %}\ -{% else %} -Initial release -{% endif %}\n -""" -# template for the changelog footer -footer = """ - -""" -# remove the leading and trailing s -trim = true -# postprocessors -postprocessors = [ - # { pattern = '', replace = "https://github.com/orhun/git-cliff" }, # replace repository URL -] - -[git] -# parse the commits based on https://www.conventionalcommits.org -conventional_commits = true -# filter out the commits that are not conventional -filter_unconventional = true -# process each line of a commit as an individual commit -split_commits = false -# regex for preprocessing the commit messages -commit_preprocessors = [ - # Replace issue numbers - #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, - # Check spelling of the commit with https://github.com/crate-ci/typos - # If the spelling is incorrect, it will be automatically fixed. - #{ pattern = '.*', replace_command = 'typos --write-changes -' }, -] -# regex for parsing and grouping commits -commit_parsers = [ - { message = "^feat", group = "🚀 Features" }, - { message = "^fix", group = "🐛 Bug Fixes" }, - { message = "^doc", group = "📚 Documentation" }, - { message = "^perf", group = "⚡ Performance" }, - { message = "^refactor", group = "🚜 Refactor" }, - { message = "^style", group = "🎨 Styling" }, - { message = "^test", group = "🧪 Testing" }, - { message = "^chore\\(release\\)", skip = true }, - { message = "^chore\\(pr\\)", skip = true }, - { message = "^chore\\(pull\\)", skip = true }, - { message = "^chore", group = "⚙️ Miscellaneous Tasks" }, - { message = "^ci", skip = true }, - { body = ".*security", group = "🛡️ Security" }, - { message = "^revert", group = "◀️ Revert" }, -] -# protect breaking changes from being skipped due to matching a skipping commit_parser -protect_breaking_commits = false -# filter out the commits that are not matched by commit parsers -filter_commits = false -# regex for matching git tags -# tag_pattern = "v[0-9].*" -# regex for skipping tags -# skip_tags = "" -# regex for ignoring tags -# ignore_tags = "" -# sort the tags topologically -topo_order = false -# sort the commits inside sections by oldest/newest order -sort_commits = "oldest" -# limit the number of commits included in the changelog. -# limit_commits = 42 diff --git a/eslint.config.js b/eslint.config.js index 4d7656d..4f58599 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -493,7 +493,7 @@ export default [ // specify whether double or single quotes should be used // https://eslint.style/rules/default/quotes - "@stylistic/quotes": ["warn", "double", { avoidEscape: true, allowTemplateLiterals: true }], + "@stylistic/quotes": ["warn", "double", { avoidEscape: true }], // require or disallow use of semicolons instead of ASI // https://eslint.style/rules/default/semi diff --git a/package.json b/package.json index fbb40e9..9ad9517 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "visitenbuch", - "version": "0.2.0", + "version": "0.0.1", "private": true, "license": "AGPL-3.0", "scripts": { @@ -45,7 +45,6 @@ "@types/node": "^20.12.8", "@types/qs": "^6.9.15", "@types/set-cookie-parser": "^2.4.7", - "@zerodevx/svelte-toast": "^0.9.5", "autoprefixer": "^10.4.19", "daisyui": "^4.10.5", "dotenv": "^16.4.5", @@ -58,7 +57,6 @@ "globals": "^15.1.0", "postcss-import": "^16.1.0", "postcss-nesting": "^12.1.2", - "rollup-license-plugin": "^3.0.0", "svelte": "^4.2.15", "svelte-check": "^3.7.1", "sveltekit-superforms": "^2.13.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5a6cc0..a1c54b7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -90,9 +90,6 @@ devDependencies: '@types/set-cookie-parser': specifier: ^2.4.7 version: 2.4.7 - '@zerodevx/svelte-toast': - specifier: ^0.9.5 - version: 0.9.5(svelte@4.2.15) autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.38) @@ -129,9 +126,6 @@ devDependencies: postcss-nesting: specifier: ^12.1.2 version: 12.1.2(postcss@8.4.38) - rollup-license-plugin: - specifier: ^3.0.0 - version: 3.0.0 svelte: specifier: ^4.2.15 version: 4.2.15 @@ -1454,14 +1448,6 @@ packages: pretty-format: 29.7.0 dev: true - /@zerodevx/svelte-toast@0.9.5(svelte@4.2.15): - resolution: {integrity: sha512-JLeB/oRdJfT+dz9A5bgd3Z7TuQnBQbeUtXrGIrNWMGqWbabpepBF2KxtWVhL2qtxpRqhae2f6NAOzH7xs4jUSw==} - peerDependencies: - svelte: ^3.57.0 || ^4.0.0 - dependencies: - svelte: 4.2.15 - dev: true - /acorn-jsx@5.3.2(acorn@8.11.3): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1937,11 +1923,6 @@ packages: - postcss dev: true - /data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - dev: true - /data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} @@ -2603,14 +2584,6 @@ packages: reusify: 1.0.4 dev: true - /fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 - dev: true - /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -2669,13 +2642,6 @@ packages: mime-types: 2.1.35 dev: false - /formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - dependencies: - fetch-blob: 3.2.0 - dev: true - /fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} dev: true @@ -2731,11 +2697,6 @@ packages: has-symbols: 1.0.3 hasown: 2.0.2 - /get-npm-tarball-url@2.1.0: - resolution: {integrity: sha512-ro+DiMu5DXgRBabqXupW38h7WPZ9+Ad8UjwhvsmmN8w1sU7ab0nzAXvVZ4kqYg57OrqomRtJvepX5/xvFKNtjA==} - engines: {node: '>=12.17'} - dev: true - /get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} @@ -3977,20 +3938,6 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - dev: true - - /node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - dev: true - /node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} dev: true @@ -4581,15 +4528,6 @@ packages: glob: 7.2.3 dev: true - /rollup-license-plugin@3.0.0: - resolution: {integrity: sha512-dcTdmq+vgqNrSq8Q7uXWp+49u8Mq4gDGeUUI92/4+STMYDyJmO+H+WFLq8DGLWECZ8ckUYAzlfbOyfmbHBE68w==} - engines: {node: '>=18.0.0'} - dependencies: - get-npm-tarball-url: 2.1.0 - node-fetch: 3.3.2 - spdx-expression-validate: 2.0.0 - dev: true - /rollup@4.17.2: resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -4791,27 +4729,6 @@ packages: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} dev: false - /spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - dev: true - - /spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.17 - dev: true - - /spdx-expression-validate@2.0.0: - resolution: {integrity: sha512-b3wydZLM+Tc6CFvaRDBOF9d76oGIHNCLYFeHbftFXUWjnfZWganmDmvtM5sm1cRwJc/VDBMLyGGrsLFd1vOxbg==} - dependencies: - spdx-expression-parse: 3.0.1 - dev: true - - /spdx-license-ids@3.0.17: - resolution: {integrity: sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==} - dev: true - /stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true @@ -5654,11 +5571,6 @@ packages: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} dev: false - /web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - dev: true - /webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} diff --git a/prisma/migrations/20240506134647_hidden_room_station/migration.sql b/prisma/migrations/20240506134647_hidden_room_station/migration.sql deleted file mode 100644 index c7d42bc..0000000 --- a/prisma/migrations/20240506134647_hidden_room_station/migration.sql +++ /dev/null @@ -1,26 +0,0 @@ --- DropForeignKey -ALTER TABLE "entry_versions" DROP CONSTRAINT "entry_versions_category_id_fkey"; - --- DropForeignKey -ALTER TABLE "patients" DROP CONSTRAINT "patients_room_id_fkey"; - --- DropForeignKey -ALTER TABLE "rooms" DROP CONSTRAINT "rooms_station_id_fkey"; - --- AlterTable -ALTER TABLE "categories" ADD COLUMN "hidden" BOOLEAN NOT NULL DEFAULT false; - --- AlterTable -ALTER TABLE "rooms" ADD COLUMN "hidden" BOOLEAN NOT NULL DEFAULT false; - --- AlterTable -ALTER TABLE "stations" ADD COLUMN "hidden" BOOLEAN NOT NULL DEFAULT false; - --- AddForeignKey -ALTER TABLE "rooms" ADD CONSTRAINT "rooms_station_id_fkey" FOREIGN KEY ("station_id") REFERENCES "stations"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "patients" ADD CONSTRAINT "patients_room_id_fkey" FOREIGN KEY ("room_id") REFERENCES "rooms"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "entry_versions" ADD CONSTRAINT "entry_versions_category_id_fkey" FOREIGN KEY ("category_id") REFERENCES "categories"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b626e89..38f77ff 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -49,10 +49,9 @@ model User { // Hospital station model Station { - id Int @id @default(autoincrement()) - name String - Room Room[] - hidden Boolean @default(false) + id Int @id @default(autoincrement()) + name String + Room Room[] @@map("stations") } @@ -61,10 +60,9 @@ model Station { model Room { id Int @id @default(autoincrement()) name String - station Station @relation(fields: [station_id], references: [id], onDelete: Restrict) + station Station @relation(fields: [station_id], references: [id], onDelete: Cascade) station_id Int Patient Patient[] - hidden Boolean @default(false) @@map("rooms") } @@ -74,7 +72,7 @@ model Patient { first_name String last_name String age Int? - room Room? @relation(fields: [room_id], references: [id], onDelete: Restrict) + room Room? @relation(fields: [room_id], references: [id], onDelete: SetNull) room_id Int? Entry Entry[] hidden Boolean @default(false) @@ -94,7 +92,6 @@ model Category { color String? description String? EntryVersion EntryVersion[] - hidden Boolean @default(false) @@map("categories") } @@ -123,7 +120,7 @@ model EntryVersion { text String date DateTime @db.Date - category Category? @relation(fields: [category_id], references: [id], onDelete: Restrict) + category Category? @relation(fields: [category_id], references: [id], onDelete: SetNull) category_id Int? priority Boolean diff --git a/release.sh b/release.sh deleted file mode 100755 index 06abc7b..0000000 --- a/release.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -set -e - -CHANGELOG="CHANGELOG.md" - -VERSION=$(jq -r '.version' package.json) -TAG="v${VERSION}" -echo "Releasing $TAG:" - -if git rev-parse "$TAG" >/dev/null 2>&1; then echo "version tag $TAG already exists"; exit 1; fi - -CLIFF_ARGS="--tag '${TAG}' --unreleased" -echo "git-cliff $CLIFF_ARGS" -if [ -f "$CHANGELOG" ]; then - eval "git-cliff $CLIFF_ARGS --prepend '$CHANGELOG'" -else - eval "git-cliff $CLIFF_ARGS --output '$CHANGELOG'" -fi - -editor "$CHANGELOG" - -git add "$CHANGELOG" -git commit -m "chore(release): release v$VERSION" - -awk 'BEGIN{RS="(^|\n)## [^\n]+\n*"} NR==2 { print }' "$CHANGELOG" | git tag -as -F - --cleanup whitespace "$TAG" - -echo "🚀 Run 'git push origin $TAG' to publish" diff --git a/src/app.d.ts b/src/app.d.ts index 13a994c..0b9d41b 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -6,7 +6,7 @@ declare global { namespace App { // interface Error {} interface Locals { - session: Session | null; + session: Session; } // interface PageData {} // interface Platform {} @@ -18,9 +18,6 @@ declare global { "on:outclick"?: CompositionEventHandler; } } - - declare const __VERSION__: string; - declare const __LASTMOD__: string; } export {}; diff --git a/src/app.pcss b/src/app.pcss index 244e176..7ddf037 100644 --- a/src/app.pcss +++ b/src/app.pcss @@ -15,32 +15,6 @@ } } -:root { - --toastBackground: oklch(var(--b3)); - --toastColor: oklch(var(--bc)); - --toastLeftBorder: oklch(var(--p)); - --toastBarBackground: oklch(var(--bc) / 0.4); - --toastContainerTop: 4rem; - --toastContainerRight: 1rem; - --toastBarHeight: 3px; -} - -.toast-error { - --toastLeftBorder: oklch(var(--er)); -} - -._toastItem { - border-left: solid 6px var(--toastLeftBorder) !important; -} - -._toastMsg { - white-space: pre-wrap; -} - -.badge { - border: none; -} - button { text-align: initial; } diff --git a/src/lib/actions/outclick.ts b/src/lib/actions/outclick.ts index 1fc1dcd..8811649 100644 --- a/src/lib/actions/outclick.ts +++ b/src/lib/actions/outclick.ts @@ -1,7 +1,5 @@ -import type { ActionReturn } from "svelte/action"; - -export default function clickOutside(node: Element): ActionReturn { - const handleClick = (event: MouseEvent): void => { +export default function clickOutside(node: Element) { + const handleClick = (event: MouseEvent) => { const tnode = event.target as Element; if (!node.contains(tnode)) { diff --git a/src/lib/components/filter/Autocomplete.svelte b/src/lib/components/filter/Autocomplete.svelte index 05badf5..9a0de09 100644 --- a/src/lib/components/filter/Autocomplete.svelte +++ b/src/lib/components/filter/Autocomplete.svelte @@ -1,5 +1,4 @@ @@ -82,15 +61,19 @@ Gespeicherte Filter: - {#each filters as filter, i (filter.id)} - remove(i)} - onSave={() => update(i)} - > - {filter.name} - - {/each} + {#if filters} + {#each filters as filter, i (filter.id)} + remove(i)} + onSave={() => update(i)} + > + {filter.name} + + {/each} + {:else} + + {/if} -{:else if hasEntries} - -{:else} - -{/if} diff --git a/src/lib/components/form/PatientForm.svelte b/src/lib/components/form/PatientForm.svelte index 91a6ad5..dfdec17 100644 --- a/src/lib/components/form/PatientForm.svelte +++ b/src/lib/components/form/PatientForm.svelte @@ -7,7 +7,6 @@ import { ZPatientNew } from "$lib/shared/model/validation"; import { trpc } from "$lib/shared/trpc"; import type { RouterOutput } from "$lib/shared/trpc"; - import { superformConfig } from "$lib/shared/util"; import Autocomplete from "$lib/components/filter/Autocomplete.svelte"; import FormField from "$lib/components/ui/FormField.svelte"; @@ -24,7 +23,6 @@ } = superForm(formData, { validators: schema, resetForm: patient === null, - ...superformConfig("Patient"), }); diff --git a/src/lib/components/form/RoomForm.svelte b/src/lib/components/form/RoomForm.svelte index c906211..0fb07ba 100644 --- a/src/lib/components/form/RoomForm.svelte +++ b/src/lib/components/form/RoomForm.svelte @@ -7,7 +7,6 @@ import { ZRoomNew } from "$lib/shared/model/validation"; import { trpc, type RouterOutput } from "$lib/shared/trpc"; - import { superformConfig } from "$lib/shared/util"; import Autocomplete from "$lib/components/filter/Autocomplete.svelte"; import FormField from "$lib/components/ui/FormField.svelte"; @@ -23,7 +22,6 @@ } = superForm(formData, { validators: schema, resetForm: room === null, - ...superformConfig("Zimmer"), }); diff --git a/src/lib/components/form/StationForm.svelte b/src/lib/components/form/StationForm.svelte index d8ba548..93c1b93 100644 --- a/src/lib/components/form/StationForm.svelte +++ b/src/lib/components/form/StationForm.svelte @@ -7,7 +7,6 @@ import type { Station } from "$lib/shared/model"; import { ZStationNew } from "$lib/shared/model/validation"; - import { superformConfig } from "$lib/shared/util"; import FormField from "$lib/components/ui/FormField.svelte"; import Header from "$lib/components/ui/Header.svelte"; @@ -22,7 +21,6 @@ } = superForm(formData, { validators: schema, resetForm: station === null, - ...superformConfig("Station"), }); diff --git a/src/lib/components/table/CategoryField.svelte b/src/lib/components/table/CategoryField.svelte index 8c438d7..3bb0505 100644 --- a/src/lib/components/table/CategoryField.svelte +++ b/src/lib/components/table/CategoryField.svelte @@ -13,7 +13,7 @@ ? colorToHex(getTextColor(hexToColor(category.color))) : null; - function onClick(e: MouseEvent): void { + function onClick(e: MouseEvent) { gotoEntityQuery( { filter: { diff --git a/src/lib/components/table/FilteredEntryTable.svelte b/src/lib/components/table/FilteredEntryTable.svelte index fcb0e38..6e5e389 100644 --- a/src/lib/components/table/FilteredEntryTable.svelte +++ b/src/lib/components/table/FilteredEntryTable.svelte @@ -22,7 +22,7 @@ export let patientId: number | null = null; export let view: string | undefined = undefined; - function paginationUpdate(pagination: PaginationRequest): void { + function paginationUpdate(pagination: PaginationRequest) { updateQuery({ filter: query.filter, pagination, @@ -30,11 +30,11 @@ }); } - function filterUpdate(filter: FilterQdata | undefined): void { + function filterUpdate(filter: FilterQdata | undefined) { updateQuery({ filter, sort: query.sort }); } - function sortUpdate(sort: SortRequest | undefined): void { + function sortUpdate(sort: SortRequest | undefined) { updateQuery({ filter: query.filter, pagination: query.pagination, @@ -42,7 +42,7 @@ }); } - function updateQuery(q: typeof query): void { + function updateQuery(q: typeof query) { if (browser) { if (patientId !== null && q.filter?.patient) delete q.filter.patient; diff --git a/src/lib/components/table/FilteredPatientTable.svelte b/src/lib/components/table/FilteredPatientTable.svelte index f87ac24..935a276 100644 --- a/src/lib/components/table/FilteredPatientTable.svelte +++ b/src/lib/components/table/FilteredPatientTable.svelte @@ -20,7 +20,7 @@ export let patients: RouterOutput["patient"]["list"]; export let baseUrl: string; - function paginationUpdate(pagination: PaginationRequest): void { + function paginationUpdate(pagination: PaginationRequest) { updateQuery({ filter: query.filter, pagination, @@ -28,11 +28,11 @@ }); } - function filterUpdate(filter: FilterQdata | undefined): void { + function filterUpdate(filter: FilterQdata | undefined) { updateQuery({ filter, sort: query.sort }); } - function sortUpdate(sort: SortRequest | undefined): void { + function sortUpdate(sort: SortRequest | undefined) { updateQuery({ filter: query.filter, pagination: query.pagination, @@ -40,7 +40,7 @@ }); } - function updateQuery(q: typeof query): void { + function updateQuery(q: typeof query) { if (browser) { // Update page URL const url = getQueryUrl(q, baseUrl); diff --git a/src/lib/components/table/PatientField.svelte b/src/lib/components/table/PatientField.svelte index c3480b5..296c797 100644 --- a/src/lib/components/table/PatientField.svelte +++ b/src/lib/components/table/PatientField.svelte @@ -5,7 +5,7 @@ export let patient: RouterOutput["patient"]["list"]["items"][0]; export let baseUrl: string; - function onClick(e: MouseEvent): void { + function onClick(e: MouseEvent) { gotoEntityQuery( { filter: { diff --git a/src/lib/components/table/PatientTable.svelte b/src/lib/components/table/PatientTable.svelte index 8df3916..2fcd3f3 100644 --- a/src/lib/components/table/PatientTable.svelte +++ b/src/lib/components/table/PatientTable.svelte @@ -1,5 +1,5 @@ - - - - diff --git a/src/lib/components/ui/LoadingBar.svelte b/src/lib/components/ui/LoadingBar.svelte index 36737a7..53522de 100644 --- a/src/lib/components/ui/LoadingBar.svelte +++ b/src/lib/components/ui/LoadingBar.svelte @@ -7,7 +7,7 @@ let showProgress = false; let showError = false; - export function start(): void { + export function start() { navprogress = 5; showProgress = true; showError = false; @@ -17,7 +17,7 @@ }, 500); } - export function reset(): void { + export function reset() { clearInterval(navInterval); navprogress = 100; @@ -30,7 +30,7 @@ }, 500); } - export function error(): void { + export function error() { showError = true; reset(); } diff --git a/src/lib/components/ui/NavLink.svelte b/src/lib/components/ui/NavLink.svelte index 3dbc1dc..4a532d6 100644 --- a/src/lib/components/ui/NavLink.svelte +++ b/src/lib/components/ui/NavLink.svelte @@ -1,13 +1,13 @@ - +
+ +
diff --git a/src/lib/components/ui/PaginationButtons.svelte b/src/lib/components/ui/PaginationButtons.svelte index 3e49d40..3a070e0 100644 --- a/src/lib/components/ui/PaginationButtons.svelte +++ b/src/lib/components/ui/PaginationButtons.svelte @@ -6,7 +6,7 @@ import { PAGINATION_LIMIT } from "$lib/shared/constants"; import type { Pagination, PaginationRequest } from "$lib/shared/model"; - import { screenWidthSmall } from "$lib/stores"; + import { screenWidthSmall } from "$lib/stores/layout"; import Icon from "./Icon.svelte"; @@ -44,7 +44,7 @@ return pag; } - function pagClick(page: number): void { + function pagClick(page: number) { const pag = getPaginationRequest(page); if (pag) onUpdate(pag); } diff --git a/src/lib/components/ui/WeekSelector.svelte b/src/lib/components/ui/WeekSelector.svelte index ce4008d..3242800 100644 --- a/src/lib/components/ui/WeekSelector.svelte +++ b/src/lib/components/ui/WeekSelector.svelte @@ -12,38 +12,26 @@ let editing = false; let autocomplete: Autocomplete | undefined; - export let dateRange: DateRange = new DateRange(null, DateRange.thisWeek().end); + export let dateRange: DateRange = DateRange.thisWeek(); export let onSelect: (value: DateRange) => void = () => {}; - function addDays(n: number): void { - if (dateRange.start === null) { - dateRange.start = new Date(dateRange.end!); - dateRange.start.setDate(dateRange.start.getDate() - 6); - } else if (dateRange.end === null) { - dateRange.end = new Date(dateRange.start!); - dateRange.end.setDate(dateRange.end.getDate() + 6); - } else { - dateRange.addDays(n); - } - } - - function nextWeek(): void { - addDays(7); + function nextWeek() { + dateRange.addDays(7); dateRange = dateRange; // update reactive onSelect(dateRange); } - function previousWeek(): void { - addDays(-7); + function previousWeek() { + dateRange.addDays(-7); dateRange = dateRange; // update reactive onSelect(dateRange); } - function startEditing(): void { + function startEditing() { editing = true; } - function stopEditing(): void { + function stopEditing() { editing = false; } @@ -57,7 +45,7 @@ {#if editing} weekFilterItems()} + items={async () => weekFilterItems(false)} onClose={stopEditing} onSelect={(item) => { if (typeof item.id === "string") { diff --git a/src/lib/components/ui/markdown/carta.pcss b/src/lib/components/ui/markdown/carta.pcss index 17e80dd..f325440 100644 --- a/src/lib/components/ui/markdown/carta.pcss +++ b/src/lib/components/ui/markdown/carta.pcss @@ -121,9 +121,3 @@ .carta-theme__default .carta-toolbar-left button.carta-active { @apply font-semibold border-primary; } - -@media (prefers-color-scheme: dark) { - .shiki, .shiki span { - color: var(--shiki-dark) !important; - } -} diff --git a/src/lib/components/ui/markdown/carta.ts b/src/lib/components/ui/markdown/carta.ts index 0d6b549..693f304 100644 --- a/src/lib/components/ui/markdown/carta.ts +++ b/src/lib/components/ui/markdown/carta.ts @@ -3,6 +3,7 @@ import type { Options } from "carta-md"; import { sanitizeHtml } from "$lib/shared/util"; export const CARTA_CFG: Options = { + theme: "github-dark", sanitizer: sanitizeHtml, disableIcons: ["taskList"], }; diff --git a/src/lib/server/auth.ts b/src/lib/server/auth.ts index cee1579..da6120d 100644 --- a/src/lib/server/auth.ts +++ b/src/lib/server/auth.ts @@ -7,7 +7,6 @@ import { type AuthConfig, } from "@auth/core"; import Keycloak from "@auth/core/providers/keycloak"; -import type { Session } from "@auth/core/types"; import { redirect, type Handle, type RequestEvent } from "@sveltejs/kit"; import { parse } from "set-cookie-parser"; @@ -71,7 +70,7 @@ export async function makeAuthjsRequest( event: RequestEvent, authjsEndpoint: string, params: Record, -): Promise { +) { const headers = new Headers(event.request.headers); headers.set("Content-Type", "application/x-www-form-urlencoded"); @@ -88,7 +87,7 @@ export async function makeAuthjsRequest( return redirect(302, res.redirect ?? ""); } -export async function auth(event: RequestEvent): Promise { +export async function auth(event: RequestEvent) { const { request: req } = event; setEnvDefaults(env, AUTH_CFG); diff --git a/src/lib/server/query/category.ts b/src/lib/server/query/category.ts index 05226b6..0f5f9aa 100644 --- a/src/lib/server/query/category.ts +++ b/src/lib/server/query/category.ts @@ -1,13 +1,7 @@ -import type { Category, CategoryDetail, CategoryNew } from "$lib/shared/model"; +import type { Category, CategoryNew } from "$lib/shared/model"; import { prisma } from "$lib/server/prisma"; -import { handleDeleteConflict } from "./util"; - -const SELECT = { - id: true, name: true, description: true, color: true, -}; - export async function newCategory(category: CategoryNew): Promise { const created = await prisma.category.create({ data: category, @@ -16,26 +10,18 @@ export async function newCategory(category: CategoryNew): Promise { return created.id; } -export async function updateCategory(id: number, category: Partial): Promise { +export async function updateCategory(id: number, category: Partial) { await prisma.category.update({ where: { id }, data: category }); } -export async function deleteCategory(id: number): Promise { - await handleDeleteConflict(prisma.category.delete({ where: { id } }), "category with entries"); +export async function deleteCategory(id: number) { + await prisma.category.delete({ where: { id } }); } -export async function hideCategory(id: number, hidden: boolean): Promise { - await prisma.category.update({ where: { id }, data: { hidden } }); -} - -export async function getCategory(id: number): Promise { +export async function getCategory(id: number): Promise { return prisma.category.findUniqueOrThrow({ where: { id } }); } -export async function getCategories(hidden = false): Promise { - return prisma.category.findMany({ select: SELECT, where: { hidden }, orderBy: { id: "asc" } }); -} - -export async function getCategoryNEntries(id: number): Promise { - return prisma.entry.count({ where: { EntryVersion: { some: { category_id: id } } } }); +export async function getCategories(): Promise { + return prisma.category.findMany({ orderBy: { id: "asc" } }); } diff --git a/src/lib/server/query/entry.ts b/src/lib/server/query/entry.ts index 8fa402b..86ab620 100644 --- a/src/lib/server/query/entry.ts +++ b/src/lib/server/query/entry.ts @@ -276,7 +276,7 @@ left join stations s on s.id = r.station_id`, if (filter?.date) { filterListToArray(filter.date).forEach((itm) => { - const dateRange = DateRange.parse(itm, true); + const dateRange = DateRange.parse(itm); if (dateRange?.start) { qb.addFilterClause(`ev.date >= ${qb.pvar()}`, dateRange.start); } @@ -286,7 +286,7 @@ left join stations s on s.id = r.station_id`, }); } - const SORT_FIELDS: Record = { + const SORT_FIELDS: { [key: string]: string[] } = { id: ["e.id"], patient: ["p.last_name", "p.first_name"], room: ["s.name", "r.name"], @@ -405,19 +405,7 @@ export async function getNTodo(date: Date): Promise { order by ev2.created_at desc limit 1) - left join entry_executions ex on - ex.entry_id = e.id - and ex.id = ( - select - id - from - entry_executions ex2 - where - ex2.entry_id = ex.entry_id - order by - ex2.created_at desc - limit 1) - where ev.date <= ${date} and ex.id is null`; + where ev.date <= ${date}`; // @ts-expect-error type checked const count = Number(result[0].count); return count; diff --git a/src/lib/server/query/index.ts b/src/lib/server/query/index.ts index c35bfb2..220256b 100644 --- a/src/lib/server/query/index.ts +++ b/src/lib/server/query/index.ts @@ -3,4 +3,3 @@ export * from "./category"; export * from "./patient"; export * from "./user"; export * from "./room"; -export * from "./station"; diff --git a/src/lib/server/query/mapping.ts b/src/lib/server/query/mapping.ts index ad7e041..1be7996 100644 --- a/src/lib/server/query/mapping.ts +++ b/src/lib/server/query/mapping.ts @@ -14,6 +14,7 @@ import type { Patient, User, UserTag, + Room, EntryVersion, EntryExecution, UserTagNameNonnull, @@ -64,6 +65,10 @@ export function mapUserTagNameNonnull(user: Omit): UserTagNameN return { id: user.id, name: user.name || "" }; } +export function mapRoom(room: DbRoomLn): Room { + return { id: room.id, name: room.name, station: room.station }; +} + export function mapVersion(version: DbEntryVersionLn): EntryVersion { return { id: version.id, diff --git a/src/lib/server/query/patient.ts b/src/lib/server/query/patient.ts index 5180280..9f0f4dd 100644 --- a/src/lib/server/query/patient.ts +++ b/src/lib/server/query/patient.ts @@ -1,3 +1,5 @@ +import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; + import type { Patient, PatientNew, @@ -7,12 +9,12 @@ import type { PatientTag, SortRequest, } from "$lib/shared/model"; -import { ErrorInvalidInput } from "$lib/shared/util/error"; +import { ErrorConflict, ErrorInvalidInput } from "$lib/shared/util/error"; import { prisma } from "$lib/server/prisma"; import { mapPatient } from "./mapping"; -import { QueryBuilder, handleDeleteConflict } from "./util"; +import { QueryBuilder } from "./util"; export async function newPatient(patient: PatientNew): Promise { const created = await prisma.patient.create({ data: patient, select: { id: true } }); @@ -20,17 +22,27 @@ export async function newPatient(patient: PatientNew): Promise { } /** Update a patient */ -export async function updatePatient(id: number, patient: Partial): Promise { +export async function updatePatient(id: number, patient: Partial) { await prisma.patient.update({ where: { id }, data: patient }); } /** Delete a patient (Note: this only works if the patient is not associated with any entries) */ -export async function deletePatient(id: number): Promise { - await handleDeleteConflict(prisma.patient.delete({ where: { id } }), "patient with entries"); +export async function deletePatient(id: number) { + try { + await prisma.patient.delete({ where: { id } }); + } catch (error) { + if (error instanceof PrismaClientKnownRequestError) { + // Foreign key constraint failed + if (error.code === "P2003") { + throw new ErrorConflict("cannot delete patient with entries"); + } + } + throw error; + } } /** Hide/show a patient */ -export async function hidePatient(id: number, hidden: boolean): Promise { +export async function hidePatient(id: number, hidden: boolean) { await prisma.patient.update({ where: { id }, data: { hidden } }); } @@ -97,7 +109,7 @@ export async function getPatients( qb.addFilterList("r.id", filter.room); qb.addFilterList("s.id", filter.station); - const SORT_FIELDS: Record = { + const SORT_FIELDS: { [key: string]: string[] } = { id: ["p.id"], name: ["p.last_name", "p.first_name"], first_name: ["p.first_name"], diff --git a/src/lib/server/query/room.ts b/src/lib/server/query/room.ts index eecf463..1e6b9a5 100644 --- a/src/lib/server/query/room.ts +++ b/src/lib/server/query/room.ts @@ -1,43 +1,61 @@ import type { - RoomNew, Room, - RoomDetail, + RoomNew, Room, Station, StationNew, } from "$lib/shared/model"; import { prisma } from "$lib/server/prisma"; -import { handleDeleteConflict } from "./util"; +import { mapRoom } from "./mapping"; -const SELECT = { id: true, name: true, station: { select: { id: true, name: true } } }; +export async function newStation(station: StationNew): Promise { + const created = await prisma.station.create({ data: station, select: { id: true } }); + return created.id; +} + +export async function updateStation(id: number, station: Partial) { + await prisma.station.update({ where: { id }, data: station }); +} + +export async function deleteStation(id: number) { + await prisma.station.delete({ where: { id } }); +} + +export async function getStation(id: number): Promise { + return prisma.station.findUniqueOrThrow({ where: { id } }); +} + +export async function getStations(): Promise { + return prisma.station.findMany({ orderBy: { id: "asc" } }); +} export async function newRoom(room: RoomNew): Promise { const created = await prisma.room.create({ data: room, select: { id: true } }); return created.id; } -export async function updateRoom(id: number, room: Partial): Promise { +export async function updateRoom(id: number, room: Partial) { await prisma.room.update({ where: { id }, data: room }); } -export async function deleteRoom(id: number): Promise { - await handleDeleteConflict(prisma.room.delete({ where: { id } }), "room with patients"); +export async function deleteRoom(id: number) { + await prisma.room.delete({ where: { id } }); } -export async function hideRoom(id: number, hidden: boolean): Promise { - await prisma.room.update({ where: { id }, data: { hidden } }); +export async function getRoom(id: number): Promise { + const room = await prisma.room.findUniqueOrThrow({ + where: { id }, + include: { station: true }, + }); + return { + id: room.id, + name: room.name, + station: room.station, + }; } -export async function getRoom(id: number): Promise { - return prisma.room.findUniqueOrThrow({ select: { ...SELECT, hidden: true }, where: { id } }); -} - -export async function getRooms(hidden = false): Promise { - return prisma.room.findMany({ - select: SELECT, - where: { hidden }, +export async function getRooms(): Promise { + const rooms = await prisma.room.findMany({ + include: { station: true }, orderBy: [{ station: { name: "asc" } }, { name: "asc" }], }); -} - -export async function getRoomNPatients(id: number): Promise { - return prisma.patient.count({ where: { room_id: id } }); + return rooms.map(mapRoom); } diff --git a/src/lib/server/query/savedFilter.ts b/src/lib/server/query/savedFilter.ts index 2dd0d3e..761a2c7 100644 --- a/src/lib/server/query/savedFilter.ts +++ b/src/lib/server/query/savedFilter.ts @@ -1,11 +1,7 @@ -import { DEFAULT_FILTER_NAME } from "$lib/shared/constants"; import type { SavedFilter, SavedFilterNew } from "$lib/shared/model"; -import { isDefaultFilter } from "$lib/shared/util"; import { prisma } from "$lib/server/prisma"; -const SELECT = { id: true, name: true, query: true }; - export async function newSavedFilter(filter: SavedFilterNew, user_id: number): Promise { const created = await prisma.savedFilter.create({ data: { @@ -16,49 +12,17 @@ export async function newSavedFilter(filter: SavedFilterNew, user_id: number): P return created.id; } -export async function updateSavedFilter(id: number, query: string, user_id: number): Promise { +export async function updateSavedFilter(id: number, query: string, user_id: number) { await prisma.savedFilter.update({ where: { id, user_id }, data: { query } }); } -export async function deleteSavedFilter(id: number, user_id: number): Promise { +export async function deleteSavedFilter(id: number, user_id: number) { await prisma.savedFilter.delete({ where: { id, user_id } }); } export async function getSavedFilters(user_id: number, view: string): Promise { - const filters = await prisma.savedFilter.findMany({ - select: SELECT, + return prisma.savedFilter.findMany({ + select: { id: true, name: true, query: true }, where: { user_id, view }, }); - - // Move default filter to the top - const dix = filters.findIndex((f) => f.name.toLowerCase() === DEFAULT_FILTER_NAME); - if (dix !== -1) filters.unshift(filters.splice(dix, 1)[0]); - - return filters; -} - -export async function getDefaultFilter(user_id: number, view: string): Promise { - return prisma.savedFilter.findFirst({ - select: SELECT, - where: { user_id, view, name: { mode: "insensitive", equals: DEFAULT_FILTER_NAME } }, - }); -} - -export async function getAllFilters(user_id: number): Promise> { - const filters = await prisma.savedFilter.findMany({ - select: { - id: true, name: true, query: true, view: true, - }, - where: { user_id }, - }); - - const grouped: Record = {}; - for (const filter of filters) { - if (!grouped[filter.view]) grouped[filter.view] = []; - const f = { id: filter.id, name: filter.name, query: filter.query }; - // Place default filter at the first position - if (isDefaultFilter(f.name)) grouped[filter.view].unshift(f); - else grouped[filter.view].push(f); - } - return grouped; } diff --git a/src/lib/server/query/station.ts b/src/lib/server/query/station.ts deleted file mode 100644 index 4ae5d92..0000000 --- a/src/lib/server/query/station.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { Station, StationDetail, StationNew } from "$lib/shared/model"; - -import { prisma } from "$lib/server/prisma"; - -import { handleDeleteConflict } from "./util"; - -const SELECT = { id: true, name: true }; - -export async function newStation(station: StationNew): Promise { - const created = await prisma.station.create({ data: station, select: { id: true } }); - return created.id; -} - -export async function updateStation(id: number, station: Partial): Promise { - await prisma.station.update({ where: { id }, data: station }); -} - -export async function deleteStation(id: number): Promise { - await handleDeleteConflict(prisma.station.delete({ where: { id } }), "station with rooms"); -} - -export async function hideStation(id: number, hidden: boolean): Promise { - await prisma.station.update({ where: { id }, data: { hidden } }); -} - -export async function getStation(id: number): Promise { - return prisma.station.findUniqueOrThrow({ where: { id } }); -} - -export async function getStations(hidden = false): Promise { - return prisma.station.findMany({ - select: SELECT, - where: { hidden }, - orderBy: { id: "asc" }, - }); -} - -export async function getStationNRooms(id: number): Promise { - return prisma.room.count({ where: { station_id: id } }); -} diff --git a/src/lib/server/query/util.ts b/src/lib/server/query/util.ts index c1c7c3f..9fd629f 100644 --- a/src/lib/server/query/util.ts +++ b/src/lib/server/query/util.ts @@ -1,8 +1,5 @@ -import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library"; - import { PAGINATION_LIMIT } from "$lib/shared/constants"; import type { FilterList, PaginationRequest } from "$lib/shared/model"; -import { ErrorConflict } from "$lib/shared/util/error"; enum QueryComponentType { Normal = 1, @@ -23,20 +20,6 @@ export function filterListToArray(fl: FilterList): T[] { return [fl]; } -export async function handleDeleteConflict(act: Promise, msg: string): Promise { - try { - await act; - } catch (error) { - if (error instanceof PrismaClientKnownRequestError) { - // Foreign key constraint failed - if (error.code === "P2003") { - throw new ErrorConflict(`cannot delete ${msg}; hide instead`); - } - } - throw error; - } -} - class SearchQueryComponent { word: string; @@ -148,16 +131,16 @@ export class QueryBuilder { this.fromClause = fromClause; } - setPagination(pag: PaginationRequest): void { + setPagination(pag: PaginationRequest) { if (pag.limit) this.limit = pag.limit; if (pag.offset) this.offset = pag.offset; } - addOrderClause(orderClause: string): void { + addOrderClause(orderClause: string) { this.orderClauses.push(orderClause); } - orderByFields(fields: string[], asc: boolean | undefined = undefined): void { + orderByFields(fields: string[], asc: boolean | undefined = undefined) { const sortDir = asc === false ? " desc" : " asc"; const orderClause = fields.join(`${sortDir}, `) + sortDir; this.addOrderClause(orderClause); @@ -170,7 +153,7 @@ export class QueryBuilder { } /** Add a simple filter checking for equality */ - addFilter(fname: string, val: unknown | undefined): void { + addFilter(fname: string, val: unknown | undefined) { if (val === undefined) return; this.params.push(val); @@ -178,13 +161,13 @@ export class QueryBuilder { } /** Add a SQL filter clause */ - addFilterClause(clause: string, ...params: unknown[]): void { + addFilterClause(clause: string, ...params: unknown[]) { this.filterClauses.push(clause); this.params.push(...params); } /** Add a list filter (value matches any item from the filter list) */ - addFilterList(fname: string, fl: FilterList | undefined): void { + addFilterList(fname: string, fl: FilterList | undefined) { if (fl === undefined) return; this.filterClauses.push(`${fname} = any (${this.pvar()})`); diff --git a/src/lib/server/trpc/context.ts b/src/lib/server/trpc/context.ts index e474256..d1f8a90 100644 --- a/src/lib/server/trpc/context.ts +++ b/src/lib/server/trpc/context.ts @@ -1,13 +1,12 @@ import type { RequestEvent } from "@sveltejs/kit"; import { type inferAsyncReturnType, TRPCError } from "@trpc/server"; -import type { User } from "$lib/shared/model"; import { ZUser } from "$lib/shared/model/validation"; // we're not using the event parameter is this example, // hence the eslint-disable rule // eslint-disable-next-line @typescript-eslint/no-unused-vars -export async function createContext(event: RequestEvent): Promise<{ user: User }> { +export async function createContext(event: RequestEvent) { if (!event.locals.session?.user) { throw new TRPCError({ code: "UNAUTHORIZED", message: "not logged in" }); } diff --git a/src/lib/server/trpc/routes/category.ts b/src/lib/server/trpc/routes/category.ts index b5f3aa3..2333808 100644 --- a/src/lib/server/trpc/routes/category.ts +++ b/src/lib/server/trpc/routes/category.ts @@ -1,15 +1,11 @@ import { z } from "zod"; -import { - ZEntityId, ZCategoryNew, ZHide, ZListHidden, -} from "$lib/shared/model/validation"; +import { ZEntityId, ZCategoryNew } from "$lib/shared/model/validation"; import { deleteCategory, getCategories, getCategory, - getCategoryNEntries, - hideCategory, newCategory, updateCategory, } from "$lib/server/query"; @@ -17,18 +13,10 @@ import { import { t, trpcWrap } from ".."; export const categoryRouter = t.router({ - list: t.procedure.input(ZListHidden).query(async (opts) => trpcWrap( - async () => getCategories(opts.input?.hidden), - )), + list: t.procedure.query(async () => trpcWrap(getCategories)), get: t.procedure .input(ZEntityId) - .query(async (opts) => trpcWrap(async () => { - const [category, n_entries] = await Promise.all([ - getCategory(opts.input), - getCategoryNEntries(opts.input), - ]); - return { ...category, n_entries }; - })), + .query(async (opts) => trpcWrap(async () => getCategory(opts.input))), create: t.procedure.input(ZCategoryNew).mutation(async (opts) => trpcWrap(async () => { const id = await newCategory(opts.input); return id; @@ -41,7 +29,4 @@ export const categoryRouter = t.router({ delete: t.procedure.input(ZEntityId).mutation(async (opts) => trpcWrap(async () => { await deleteCategory(opts.input); })), - hide: t.procedure.input(ZHide).mutation(async (opts) => trpcWrap(async () => { - await hideCategory(opts.input.id, opts.input.hidden); - })), }); diff --git a/src/lib/server/trpc/routes/patient.ts b/src/lib/server/trpc/routes/patient.ts index 2f90645..dfe77a3 100644 --- a/src/lib/server/trpc/routes/patient.ts +++ b/src/lib/server/trpc/routes/patient.ts @@ -1,8 +1,6 @@ import { z } from "zod"; -import { - ZEntityId, ZHide, ZPatientNew, ZPatientsQuery, -} from "$lib/shared/model/validation"; +import { ZEntityId, ZPatientNew, ZPatientsQuery } from "$lib/shared/model/validation"; import { deletePatient, @@ -44,7 +42,14 @@ export const patientRouter = t.router({ delete: t.procedure.input(ZEntityId).mutation(async (opts) => trpcWrap(async () => { await deletePatient(opts.input); })), - hide: t.procedure.input(ZHide).mutation(async (opts) => trpcWrap(async () => { - await hidePatient(opts.input.id, opts.input.hidden); - })), + hide: t.procedure + .input( + z.object({ + id: ZEntityId, + hidden: z.boolean().default(true), + }), + ) + .mutation(async (opts) => trpcWrap(async () => { + await hidePatient(opts.input.id, opts.input.hidden); + })), }); diff --git a/src/lib/server/trpc/routes/room.ts b/src/lib/server/trpc/routes/room.ts index c0e2bc9..9872f2d 100644 --- a/src/lib/server/trpc/routes/room.ts +++ b/src/lib/server/trpc/routes/room.ts @@ -1,28 +1,18 @@ import { z } from "zod"; -import { - ZEntityId, ZHide, ZListHidden, ZRoomNew, -} from "$lib/shared/model/validation"; +import { ZEntityId, ZRoomNew } from "$lib/shared/model/validation"; import { - deleteRoom, getRoom, getRoomNPatients, getRooms, hideRoom, newRoom, updateRoom, + deleteRoom, getRoom, getRooms, newRoom, updateRoom, } from "$lib/server/query"; import { t, trpcWrap } from ".."; export const roomRouter = t.router({ - list: t.procedure.input(ZListHidden).query(async (opts) => trpcWrap( - async () => getRooms(opts.input?.hidden), - )), + list: t.procedure.query(async (opts) => trpcWrap(getRooms)), get: t.procedure .input(ZEntityId) - .query(async (opts) => trpcWrap(async () => { - const [room, n_patients] = await Promise.all([ - getRoom(opts.input), - getRoomNPatients(opts.input), - ]); - return { ...room, n_patients }; - })), + .query(async (opts) => trpcWrap(async () => getRoom(opts.input))), create: t.procedure.input(ZRoomNew).mutation(async (opts) => trpcWrap(async () => { const id = await newRoom(opts.input); return id; @@ -35,7 +25,4 @@ export const roomRouter = t.router({ delete: t.procedure.input(ZEntityId).mutation(async (opts) => trpcWrap(async () => { await deleteRoom(opts.input); })), - hide: t.procedure.input(ZHide).mutation(async (opts) => trpcWrap(async () => { - await hideRoom(opts.input.id, opts.input.hidden); - })), }); diff --git a/src/lib/server/trpc/routes/savedFilter.ts b/src/lib/server/trpc/routes/savedFilter.ts index 3c333ff..78d9388 100644 --- a/src/lib/server/trpc/routes/savedFilter.ts +++ b/src/lib/server/trpc/routes/savedFilter.ts @@ -3,12 +3,7 @@ import { } from "$lib/shared/model/validation"; import { - deleteSavedFilter, - getAllFilters, - getDefaultFilter, - getSavedFilters, - newSavedFilter, - updateSavedFilter, + deleteSavedFilter, getSavedFilters, newSavedFilter, updateSavedFilter, } from "$lib/server/query/savedFilter"; import { t, trpcWrap } from ".."; @@ -17,12 +12,6 @@ export const savedFilterRouter = t.router({ get: t.procedure.input(fields.NameString()).query(async (opts) => trpcWrap( async () => getSavedFilters(opts.ctx.user.id, opts.input), )), - getDefault: t.procedure.input(fields.NameString()).query(async (opts) => trpcWrap( - async () => getDefaultFilter(opts.ctx.user.id, opts.input), - )), - getAll: t.procedure.query(async (opts) => trpcWrap( - async () => getAllFilters(opts.ctx.user.id), - )), create: t.procedure.input(ZSavedFilterNew).mutation(async (opts) => trpcWrap( async () => newSavedFilter(opts.input, opts.ctx.user.id), )), diff --git a/src/lib/server/trpc/routes/station.ts b/src/lib/server/trpc/routes/station.ts index d9d68aa..d611f86 100644 --- a/src/lib/server/trpc/routes/station.ts +++ b/src/lib/server/trpc/routes/station.ts @@ -1,15 +1,11 @@ import { z } from "zod"; -import { - ZEntityId, ZHide, ZListHidden, ZStationNew, -} from "$lib/shared/model/validation"; +import { ZEntityId, ZStationNew } from "$lib/shared/model/validation"; import { deleteStation, getStation, - getStationNRooms, getStations, - hideStation, newStation, updateStation, } from "$lib/server/query"; @@ -17,16 +13,10 @@ import { import { t, trpcWrap } from ".."; export const stationRouter = t.router({ - list: t.procedure.input(ZListHidden).query(async (opts) => getStations(opts.input?.hidden)), + list: t.procedure.query(getStations), get: t.procedure .input(ZEntityId) - .query(async (opts) => trpcWrap(async () => { - const [station, n_rooms] = await Promise.all([ - getStation(opts.input), - getStationNRooms(opts.input), - ]); - return { ...station, n_rooms }; - })), + .query(async (opts) => trpcWrap(async () => getStation(opts.input))), create: t.procedure.input(ZStationNew).mutation(async (opts) => trpcWrap(async () => { const id = await newStation(opts.input); return id; @@ -39,7 +29,4 @@ export const stationRouter = t.router({ delete: t.procedure.input(ZEntityId).mutation(async (opts) => trpcWrap(async () => { await deleteStation(opts.input); })), - hide: t.procedure.input(ZHide).mutation(async (opts) => trpcWrap(async () => { - await hideStation(opts.input.id, opts.input.hidden); - })), }); diff --git a/src/lib/shared/constants.ts b/src/lib/shared/constants.ts index 12a15f7..374b071 100644 --- a/src/lib/shared/constants.ts +++ b/src/lib/shared/constants.ts @@ -4,5 +4,3 @@ export const WEEK_LIMIT = 8; export const URL_ENTRIES = "/plan"; export const URL_VISIT = "/visit"; export const URL_PATIENTS = "/patients"; - -export const DEFAULT_FILTER_NAME = "default"; diff --git a/src/lib/shared/model/model.ts b/src/lib/shared/model/model.ts index bbecac8..e3c75de 100644 --- a/src/lib/shared/model/model.ts +++ b/src/lib/shared/model/model.ts @@ -35,40 +35,31 @@ export type UserTagNameNonnull = { name: string; }; -export type StationDetail = { +export type Station = { id: number; name: string; - hidden: boolean; }; -export type Station = Omit; - export type StationNew = Omit; -export type RoomDetail = { +export type Room = { id: number; name: string; station: Station; - hidden: boolean; }; -export type Room = Omit; - export type RoomNew = { name: string; station_id: number; }; -export type CategoryDetail = { +export type Category = { id: number; name: string; color: Option; description: Option; - hidden: boolean; }; -export type Category = Omit; - export type CategoryNew = Omit; export type Patient = { diff --git a/src/lib/shared/model/validation.ts b/src/lib/shared/model/validation.ts index d353444..8c78b06 100644 --- a/src/lib/shared/model/validation.ts +++ b/src/lib/shared/model/validation.ts @@ -45,7 +45,7 @@ const coercedBool = z.string().toLowerCase().transform((v) => v === "true").or(z function returnDataInSameOrderAsPassed>( schema: Schema, -): z.ZodEffects | undefined> { +) { return z.any().transform((value, ctx) => { const parsed = schema.safeParse(value); if (parsed.success) { @@ -181,10 +181,3 @@ export const ZSavedFilterUpdate = z.object({ id: ZEntityId, query: z.string(), }); - -export const ZHide = z.object({ - id: ZEntityId, - hidden: z.boolean().default(true), -}); - -export const ZListHidden = z.object({ hidden: z.boolean().optional() }).optional(); diff --git a/src/lib/shared/util/date.ts b/src/lib/shared/util/date.ts index 1e21442..43770bb 100644 --- a/src/lib/shared/util/date.ts +++ b/src/lib/shared/util/date.ts @@ -109,6 +109,9 @@ export class DateRange { /** Create a date range of the current calendar week */ static thisWeek(): DateRange { const dayStart = new Date(); + // Correct for timezone + dayStart.setMinutes(dayStart.getMinutes() - dayStart.getTimezoneOffset()); + const todayWd = dayStart.getDay(); // Day starts at Sunday (0) const daysMinus = todayWd === 0 ? 6 : todayWd - 1; @@ -123,11 +126,11 @@ export class DateRange { * - Range with 2 ends: `2024-04-13..2024-04-20` * - Range with 1 end: `2024-04-13..`; `..2024-04-20` */ - static parse(s: string, utc = false): DateRange | null { + static parse(s: string): DateRange | null { const parts = s.split("..", 2); const parsed = parts.map((p) => { if (p.length === 0) return null; - return utc ? new Date(p) : dateFromYMD(p); + return dateFromYMD(p); }); if (parsed.length === 0 @@ -144,7 +147,7 @@ export class DateRange { } /** Shift the range by the given number of days. This modifies the range in-place */ - addDays(n: number): void { + addDays(n: number) { this.start?.setDate(this.start.getDate() + n); this.end?.setDate(this.end.getDate() + n); } @@ -160,8 +163,10 @@ export class DateRange { /** Return a string representation for display purposes */ format(): string { - if (this.start === null) return "bis " + formatDate(this.end!); - if (this.end === null) return "ab " + formatDate(this.start); - return formatDate(this.start) + " \u2013 " + formatDate(this.end); + let res = ""; + if (this.start) res += formatDate(this.start); + res += " \u2013 "; + if (this.end) res += formatDate(this.end); + return res; } } diff --git a/src/lib/shared/util/toast.ts b/src/lib/shared/util/toast.ts deleted file mode 100644 index 8eefab0..0000000 --- a/src/lib/shared/util/toast.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { toast } from "@zerodevx/svelte-toast"; - -export function toastInfo(msg: string): void { - toast.push({ msg }); -} -export function toastError(msg: string): void { - toast.push({ msg, classes: ["toast-error"] }); -} diff --git a/src/lib/shared/util/util.ts b/src/lib/shared/util/util.ts index ee63e61..d074d52 100644 --- a/src/lib/shared/util/util.ts +++ b/src/lib/shared/util/util.ts @@ -4,15 +4,10 @@ import { isRedirect, error } from "@sveltejs/kit"; import { TRPCClientError } from "@trpc/client"; import DOMPurify from "isomorphic-dompurify"; import qs from "qs"; -import type { FormOptions } from "sveltekit-superforms"; import { ZodError } from "zod"; -import { DEFAULT_FILTER_NAME, URL_VISIT } from "$lib/shared/constants"; -import type { EntityQuery, SavedFilter } from "$lib/shared/model"; -import { type RouterOutput } from "$lib/shared/trpc"; - -import { DateRange } from "./date"; -import { toastError, toastInfo } from "./toast"; +import type { EntityQuery } from "$lib/shared/model"; +import type { RouterOutput } from "$lib/shared/trpc"; export function formatBool(val: boolean): string { return val ? "Ja" : "Nein"; @@ -29,7 +24,7 @@ export function parseQueryUrl(search: string): any { return qs.parse(search, { ignoreQueryPrefix: true, allowDots: true }); } -export function gotoEntityQuery(query: EntityQuery, basePath: string): void { +export function gotoEntityQuery(query: EntityQuery, basePath: string) { if (window && window.location.pathname.startsWith(`${basePath}/`)) { if (window.location.search) { const oldQuery: EntityQuery = parseQueryUrl(window.location.search); @@ -48,7 +43,7 @@ export function gotoEntityQuery(query: EntityQuery, basePath: string): void { * * Converts TRPC errors to SvelteKit ones */ -export async function loadWrap(f: () => Promise): Promise { +export async function loadWrap(f: () => Promise) { try { return await f(); } catch (e) { @@ -82,16 +77,16 @@ export class Debouncer { this.handler = handler; } - clear(): void { + clear() { if (this.timeout) window.clearTimeout(this.timeout); } - trigger(): void { + trigger() { this.clear(); this.timeout = window.setTimeout(this.handler, this.delay); } - now(): void { + now() { this.clear(); this.handler(); } @@ -112,45 +107,3 @@ export function divFloor(a: number, b: number): number { export function normalizeLineEndings(s: string): string { return s.replaceAll("\r\n", "\n"); } - -export function superformConfig(entity?: string): Pick { - return { - onError: ({ result }) => { - toastError(result.error.message); - }, - onResult: ({ result }) => { - if (result.type === "success") { - if (result.data?.form && typeof result.data.form.message === "string") { - toastInfo(result.data.form.message); - } else if (entity) { - toastInfo(entity + " aktualisiert"); - } else { - toastInfo("OK"); - } - } - }, - }; -} - -export function isDefaultFilter(name: string): boolean { - return name.toLowerCase() === DEFAULT_FILTER_NAME; -} - -export function defaultFilterUrl( - savedFilters: Record, - view: string, -): string { - let df = undefined; - const filters = savedFilters[view]; - if (filters) df = filters.find((f) => isDefaultFilter(f.name)); - return "/" + view + (df ? "?" + df.query : ""); -} - -export function defaultVisitUrl(): string { - return getQueryUrl({ - filter: { - done: false, - date: [{ id: new DateRange(null, DateRange.thisWeek().end).toString() }], - }, - }, URL_VISIT); -} diff --git a/src/lib/stores/index.ts b/src/lib/stores/index.ts deleted file mode 100644 index 556cc5d..0000000 --- a/src/lib/stores/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { derived, writable, type Writable } from "svelte/store"; - -import type { SavedFilter } from "$lib/shared/model"; - -// Width of the main section of the layout -export const screenWidth = writable(0); -export const screenWidthSmall = derived(screenWidth, ($mainWidth) => $mainWidth < 500); - -export const savedFilters: Writable> = writable({}); diff --git a/src/lib/stores/layout.ts b/src/lib/stores/layout.ts new file mode 100644 index 0000000..d69b86a --- /dev/null +++ b/src/lib/stores/layout.ts @@ -0,0 +1,5 @@ +import { derived, writable } from "svelte/store"; + +// Width of the main section of the layout +export const screenWidth = writable(0); +export const screenWidthSmall = derived(screenWidth, ($mainWidth) => $mainWidth < 500); diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 61cacf4..9385be4 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -1,23 +1,15 @@
diff --git a/src/routes/(app)/+layout.ts b/src/routes/(app)/+layout.ts deleted file mode 100644 index 75f44f6..0000000 --- a/src/routes/(app)/+layout.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { LayoutLoad } from "./$types"; - -import { trpc } from "$lib/shared/trpc"; - -export const load: LayoutLoad = async (event) => { - const savedFilters = await trpc(event).savedFilter.getAll.query(); - - return { savedFilters }; -}; diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index fadfee1..3eaa8cd 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -2,10 +2,6 @@ import { page } from "$app/stores"; import type { PageData } from "./$types"; - import { defaultFilterUrl } from "$lib/shared/util"; - - import { savedFilters } from "$lib/stores"; - export let data: PageData; @@ -26,7 +22,7 @@

Planung

Hier können sie neue Visitenbucheinträge erstellen.

diff --git a/src/routes/(app)/about/+page.svelte b/src/routes/(app)/about/+page.svelte deleted file mode 100644 index 0ef0423..0000000 --- a/src/routes/(app)/about/+page.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - -
-

Visitenbuch

-

Version: {version}

-

Letzte Änderung: {lastmod}

-

Open-Source-Lizenzen

-
diff --git a/src/routes/(app)/categories/+page.svelte b/src/routes/(app)/categories/+page.svelte index 9ae3771..bf183e0 100644 --- a/src/routes/(app)/categories/+page.svelte +++ b/src/routes/(app)/categories/+page.svelte @@ -3,7 +3,6 @@ import CategoryField from "$lib/components/table/CategoryField.svelte"; import Header from "$lib/components/ui/Header.svelte"; - import HiddenToggle from "$lib/components/ui/HiddenToggle.svelte"; export let data: PageData; @@ -12,11 +11,8 @@ Kategorien -
- +
+ Neue Kategorie
diff --git a/src/routes/(app)/categories/+page.ts b/src/routes/(app)/categories/+page.ts index c3b130f..6845c60 100644 --- a/src/routes/(app)/categories/+page.ts +++ b/src/routes/(app)/categories/+page.ts @@ -5,8 +5,7 @@ import { loadWrap } from "$lib/shared/util"; export const load: PageLoad = async (event) => { return loadWrap(async () => { - const hidden = event.url.searchParams.get("hidden") !== null; - const categories = await trpc(event).category.list.query({ hidden }); - return { categories, hidden }; + const categories = await trpc(event).category.list.query(); + return { categories }; }); }; diff --git a/src/routes/(app)/category/[id]/+page.server.ts b/src/routes/(app)/category/[id]/+page.server.ts index 9851d12..07968b3 100644 --- a/src/routes/(app)/category/[id]/+page.server.ts +++ b/src/routes/(app)/category/[id]/+page.server.ts @@ -1,7 +1,7 @@ import type { Actions } from "./$types"; import { fail, redirect } from "@sveltejs/kit"; -import { message, superValidate } from "sveltekit-superforms"; +import { superValidate } from "sveltekit-superforms"; import { zod } from "sveltekit-superforms/adapters"; import { ZCategoryNew, ZUrlEntityId } from "$lib/shared/model/validation"; @@ -12,18 +12,14 @@ export const actions: Actions = { default: async (event) => loadWrap(async () => { const id = ZUrlEntityId.parse(event.params.id); const formData = await event.request.formData(); - const form = await superValidate(formData, zod(ZCategoryNew)); - const hide = formData.get("hide"); const del = formData.get("delete"); - if (hide) { - const hidden = Boolean(parseInt(hide.toString())); - await trpc(event).category.hide.mutate({ id, hidden }); - return message(form, "Kategorie " + (hidden ? "ausgeblendet" : "eingeblendet")); - } else if (del) { + if (del) { await trpc(event).category.delete.mutate(id); redirect(302, "/categories"); } else { + const form = await superValidate(formData, zod(ZCategoryNew)); + if (!form.valid) { return fail(400, { form }); } @@ -32,7 +28,8 @@ export const actions: Actions = { id, category: form.data, }); + + return { form }; } - return { form }; }), }; diff --git a/src/routes/(app)/category/[id]/+page.svelte b/src/routes/(app)/category/[id]/+page.svelte index f27f3b7..a311900 100644 --- a/src/routes/(app)/category/[id]/+page.svelte +++ b/src/routes/(app)/category/[id]/+page.svelte @@ -2,7 +2,6 @@ import type { PageData } from "./$types"; import CategoryForm from "$lib/components/form/CategoryForm.svelte"; - import HideDelete from "$lib/components/form/HideDelete.svelte"; export let data: PageData; @@ -12,5 +11,5 @@ - 0} hidden={data.category.hidden} /> + diff --git a/src/routes/(app)/entry/[id]/+page.server.ts b/src/routes/(app)/entry/[id]/+page.server.ts index 693fb9c..92565c6 100644 --- a/src/routes/(app)/entry/[id]/+page.server.ts +++ b/src/routes/(app)/entry/[id]/+page.server.ts @@ -1,7 +1,7 @@ import type { Actions } from "./$types"; import { fail } from "@sveltejs/kit"; -import { superValidate, message } from "sveltekit-superforms"; +import { superValidate } from "sveltekit-superforms"; import { ZUrlEntityId } from "$lib/shared/model/validation"; import { trpc } from "$lib/shared/trpc"; @@ -22,6 +22,5 @@ export const actions: Actions = { old_execution_id: null, execution: { text: form.data.text }, }); - return message(form, "Eintrag erledigt"); }), }; diff --git a/src/routes/(app)/entry/[id]/+page.svelte b/src/routes/(app)/entry/[id]/+page.svelte index 7d0464d..46a4e0f 100644 --- a/src/routes/(app)/entry/[id]/+page.svelte +++ b/src/routes/(app)/entry/[id]/+page.svelte @@ -4,8 +4,6 @@ import { defaults, superForm } from "sveltekit-superforms"; - import { superformConfig } from "$lib/shared/util"; - import EntryBody from "$lib/components/entry/EntryBody.svelte"; import MarkdownInput from "$lib/components/ui/markdown/MarkdownInput.svelte"; @@ -14,11 +12,8 @@ export let data: PageData; const formData = defaults(SchemaEntryExecution); - const { - form, errors, enhance, - } = superForm(formData, { + const { form, errors, enhance } = superForm(formData, { validators: SchemaEntryExecution, - ...superformConfig("Eintrag"), }); diff --git a/src/routes/(app)/entry/[id]/edit/+page.svelte b/src/routes/(app)/entry/[id]/edit/+page.svelte index 8717329..f29d052 100644 --- a/src/routes/(app)/entry/[id]/edit/+page.svelte +++ b/src/routes/(app)/entry/[id]/edit/+page.svelte @@ -6,7 +6,6 @@ import { trpc } from "$lib/shared/trpc"; import { formatDate, humanDate } from "$lib/shared/util"; - import { superformConfig } from "$lib/shared/util"; import PatientCard from "$lib/components/entry/PatientCard.svelte"; import Autocomplete from "$lib/components/filter/Autocomplete.svelte"; @@ -25,7 +24,6 @@ form, errors, constraints, enhance, tainted, } = superForm(data.form, { validators: SchemaNewEntryVersion, - ...superformConfig("Eintrag"), }); diff --git a/src/routes/(app)/entry/[id]/editExecution/+page.svelte b/src/routes/(app)/entry/[id]/editExecution/+page.svelte index 4e94ca4..b82c776 100644 --- a/src/routes/(app)/entry/[id]/editExecution/+page.svelte +++ b/src/routes/(app)/entry/[id]/editExecution/+page.svelte @@ -4,8 +4,6 @@ import { defaults, superForm } from "sveltekit-superforms"; - import { superformConfig } from "$lib/shared/util"; - import EntryBody from "$lib/components/entry/EntryBody.svelte"; import MarkdownInput from "$lib/components/ui/markdown/MarkdownInput.svelte"; @@ -16,7 +14,6 @@ const formData = defaults(SchemaNewExecution); const { form, errors, enhance } = superForm(formData, { validators: SchemaNewExecution, - ...superformConfig("Eintrag"), }); diff --git a/src/routes/(app)/entry/new/+page.svelte b/src/routes/(app)/entry/new/+page.svelte index 8030b63..9ff091a 100644 --- a/src/routes/(app)/entry/new/+page.svelte +++ b/src/routes/(app)/entry/new/+page.svelte @@ -2,8 +2,6 @@ import { defaults, superForm } from "sveltekit-superforms"; import { trpc } from "$lib/shared/trpc"; - import { superformConfig } from "$lib/shared/util"; - import { toastError } from "$lib/shared/util/toast"; import Autocomplete from "$lib/components/filter/Autocomplete.svelte"; import FormField from "$lib/components/ui/FormField.svelte"; @@ -16,7 +14,6 @@ form, errors, constraints, enhance, } = superForm(formData, { validators: SchemaNewEntryWithPatient, - ...superformConfig("Eintrag"), }); @@ -59,7 +56,7 @@ $form.patient_first_name = p.first_name; $form.patient_last_name = p.last_name; $form.patient_age = p.age; - }).catch((e) => toastError("Konnte Patient nicht laden:\n" + e)); + }); return { newValue: item.name ?? "", close: true }; }} onUnselect={() => { diff --git a/src/routes/(app)/patient/[id]/+page.server.ts b/src/routes/(app)/patient/[id]/+page.server.ts index e32dc20..55e08a2 100644 --- a/src/routes/(app)/patient/[id]/+page.server.ts +++ b/src/routes/(app)/patient/[id]/+page.server.ts @@ -1,7 +1,7 @@ import type { Actions } from "./$types"; import { fail, redirect } from "@sveltejs/kit"; -import { message, superValidate } from "sveltekit-superforms"; +import { superValidate } from "sveltekit-superforms"; import { zod } from "sveltekit-superforms/adapters"; import { ZPatientNew, ZUrlEntityId } from "$lib/shared/model/validation"; @@ -12,18 +12,20 @@ export const actions: Actions = { default: async (event) => loadWrap(async () => { const id = ZUrlEntityId.parse(event.params.id); const formData = await event.request.formData(); - const form = await superValidate(formData, zod(ZPatientNew)); const hide = formData.get("hide"); const del = formData.get("delete"); if (hide) { - const hidden = Boolean(parseInt(hide.toString())); - await trpc(event).patient.hide.mutate({ id, hidden }); - return message(form, "Patient " + (hidden ? "ausgeblendet" : "eingeblendet")); + await trpc(event).patient.hide.mutate({ + id, + hidden: Boolean(parseInt(hide.toString())), + }); } else if (del) { await trpc(event).patient.delete.mutate(id); redirect(302, "/patients"); } else { + const form = await superValidate(formData, zod(ZPatientNew)); + if (!form.valid) { return fail(400, { form }); } @@ -32,7 +34,8 @@ export const actions: Actions = { id, patient: form.data, }); + + return { form }; } - return { form }; }), }; diff --git a/src/routes/(app)/patient/[id]/+page.svelte b/src/routes/(app)/patient/[id]/+page.svelte index 6fd3d82..ad88658 100644 --- a/src/routes/(app)/patient/[id]/+page.svelte +++ b/src/routes/(app)/patient/[id]/+page.svelte @@ -1,7 +1,6 @@ @@ -11,11 +10,8 @@ Zimmer -
-
-
+
+ Neues Zimmer
diff --git a/src/routes/(app)/rooms/+page.ts b/src/routes/(app)/rooms/+page.ts index 87a0e36..774e5e6 100644 --- a/src/routes/(app)/rooms/+page.ts +++ b/src/routes/(app)/rooms/+page.ts @@ -5,8 +5,7 @@ import { loadWrap } from "$lib/shared/util"; export const load: PageLoad = async (event) => { return loadWrap(async () => { - const hidden = event.url.searchParams.get("hidden") !== null; - const rooms = await trpc(event).room.list.query({ hidden }); - return { rooms, hidden }; + const rooms = await trpc(event).room.list.query(); + return { rooms }; }); }; diff --git a/src/routes/(app)/station/[id]/+page.server.ts b/src/routes/(app)/station/[id]/+page.server.ts index ee30f40..d03c112 100644 --- a/src/routes/(app)/station/[id]/+page.server.ts +++ b/src/routes/(app)/station/[id]/+page.server.ts @@ -1,7 +1,7 @@ import type { Actions } from "./$types"; import { fail, redirect } from "@sveltejs/kit"; -import { message, superValidate } from "sveltekit-superforms"; +import { superValidate } from "sveltekit-superforms"; import { zod } from "sveltekit-superforms/adapters"; import { ZStationNew, ZUrlEntityId } from "$lib/shared/model/validation"; @@ -12,18 +12,14 @@ export const actions: Actions = { default: async (event) => loadWrap(async () => { const id = ZUrlEntityId.parse(event.params.id); const formData = await event.request.formData(); - const form = await superValidate(formData, zod(ZStationNew)); - const hide = formData.get("hide"); const del = formData.get("delete"); - if (hide) { - const hidden = Boolean(parseInt(hide.toString())); - await trpc(event).station.hide.mutate({ id, hidden }); - return message(form, "Station " + (hidden ? "ausgeblendet" : "eingeblendet")); - } else if (del) { + if (del) { await trpc(event).station.delete.mutate(id); redirect(302, "/stations"); } else { + const form = await superValidate(formData, zod(ZStationNew)); + if (!form.valid) { return fail(400, { form }); } @@ -32,7 +28,8 @@ export const actions: Actions = { id, station: form.data, }); + + return { form }; } - return { form }; }), }; diff --git a/src/routes/(app)/station/[id]/+page.svelte b/src/routes/(app)/station/[id]/+page.svelte index 2f99db3..34531e1 100644 --- a/src/routes/(app)/station/[id]/+page.svelte +++ b/src/routes/(app)/station/[id]/+page.svelte @@ -1,7 +1,6 @@ @@ -11,11 +10,8 @@ Stationen -
-
-
+
+ Neue Station
diff --git a/src/routes/(app)/stations/+page.ts b/src/routes/(app)/stations/+page.ts index 747cc98..679ca82 100644 --- a/src/routes/(app)/stations/+page.ts +++ b/src/routes/(app)/stations/+page.ts @@ -5,8 +5,7 @@ import { loadWrap } from "$lib/shared/util"; export const load: PageLoad = async (event) => { return loadWrap(async () => { - const hidden = event.url.searchParams.get("hidden") !== null; - const stations = await trpc(event).station.list.query({ hidden }); - return { stations, hidden }; + const stations = await trpc(event).station.list.query(); + return { stations }; }); }; diff --git a/src/routes/(app)/test/+page.svelte b/src/routes/(app)/test/+page.svelte deleted file mode 100644 index a08fe61..0000000 --- a/src/routes/(app)/test/+page.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - -
- - -
diff --git a/src/routes/(app)/visit/+page.svelte b/src/routes/(app)/visit/+page.svelte index ef23819..1ff17d6 100644 --- a/src/routes/(app)/visit/+page.svelte +++ b/src/routes/(app)/visit/+page.svelte @@ -4,8 +4,9 @@ import type { PageData } from "./$types"; import { URL_VISIT } from "$lib/shared/constants"; - import type { PaginationRequest, Station } from "$lib/shared/model"; + import type { PaginationRequest } from "$lib/shared/model"; import { trpc } from "$lib/shared/trpc"; + import type { RouterOutput } from "$lib/shared/trpc"; import { DateRange, getQueryUrl } from "$lib/shared/util"; import Autocomplete from "$lib/components/filter/Autocomplete.svelte"; @@ -16,7 +17,7 @@ export let data: PageData; let dateRange: DateRange; - let selection: Station | null; + let selection: RouterOutput["station"]["get"] | null; $: if (data.query.filter?.date) { const date = data.query.filter?.date[0]; @@ -40,14 +41,14 @@ selection = null; } - function paginationUpdate(pagination: PaginationRequest): void { + function paginationUpdate(pagination: PaginationRequest) { updateQuery({ filter: data.query.filter, pagination, }); } - function filterUpdate(): void { + function filterUpdate() { updateQuery({ filter: { done: false, @@ -57,7 +58,7 @@ }); } - function updateQuery(q: typeof data.query): void { + function updateQuery(q: typeof data.query) { if (browser) { // Update page URL const url = getQueryUrl(q, URL_VISIT); diff --git a/src/routes/(app)/visit/+page.ts b/src/routes/(app)/visit/+page.ts index c6b99cc..bd1917b 100644 --- a/src/routes/(app)/visit/+page.ts +++ b/src/routes/(app)/visit/+page.ts @@ -3,9 +3,12 @@ import type { PageLoad } from "./$types"; import { redirect } from "@sveltejs/kit"; import { z } from "zod"; +import { URL_VISIT } from "$lib/shared/constants"; import { ZEntriesQuery } from "$lib/shared/model/validation"; import { trpc } from "$lib/shared/trpc"; -import { defaultVisitUrl, loadWrap, parseQueryUrl } from "$lib/shared/util"; +import { + DateRange, getQueryUrl, loadWrap, parseQueryUrl, +} from "$lib/shared/util"; export const load: PageLoad = async (event) => { return loadWrap(async () => { @@ -17,7 +20,13 @@ export const load: PageLoad = async (event) => { } if (!query.filter) { - redirect(302, defaultVisitUrl()); + const url = getQueryUrl({ + filter: { + done: false, + date: [{ id: DateRange.thisWeek().toString() }], + }, + }, URL_VISIT); + redirect(302, url); } // Sort entries by date diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index a18fc7e..0d87d59 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -3,10 +3,8 @@ import { navigating } from "$app/stores"; - import { SvelteToast } from "@zerodevx/svelte-toast"; - import LoadingBar from "$lib/components/ui/LoadingBar.svelte"; - import { screenWidth } from "$lib/stores"; + import { screenWidth } from "$lib/stores/layout"; let loadingBar: LoadingBar | undefined; @@ -15,12 +13,9 @@ } else { loadingBar?.reset(); } - - const options = { pausable: true }; -
diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts index b5212e1..e01e0df 100644 --- a/src/routes/login/+page.server.ts +++ b/src/routes/login/+page.server.ts @@ -10,7 +10,7 @@ import { makeAuthjsRequest } from "$lib/server/auth"; */ const COOKIE_NAME = "autoLoginTs"; -async function doLogin(event: RequestEvent): Promise { +async function doLogin(event: RequestEvent) { const callbackUrl = event.url.searchParams.get("returnURL") ?? baseUrl(event.url); return makeAuthjsRequest(event, "signin/keycloak", { callbackUrl }); diff --git a/tailwind.config.cjs b/tailwind.config.cjs index 99f32dd..db96d07 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -1,7 +1,4 @@ /** @type {import('tailwindcss').Config}*/ - -const themes = require("daisyui/src/theming/themes"); - const config = { content: [ "./src/**/*.{html,js,svelte,ts}", @@ -12,15 +9,6 @@ const config = { daisyui: { logs: false, - themes: [ - { - light: { - ...themes["light"], - "primary": "#799dff", - } - }, - "dark" - ], }, safelist: ["prose"], }; diff --git a/tests/helpers/reset-db.ts b/tests/helpers/reset-db.ts index a7d12f8..2cbe018 100644 --- a/tests/helpers/reset-db.ts +++ b/tests/helpers/reset-db.ts @@ -1,12 +1,9 @@ import { prisma } from "$lib/server/prisma"; -import { - CATEGORIES, ROOMS, STATIONS, USERS, -} from "./testdata"; +import { CATEGORIES, STATIONS, USERS } from "./testdata"; export default async () => { await prisma.$transaction([ - prisma.savedFilter.deleteMany(), prisma.entryExecution.deleteMany(), prisma.entryVersion.deleteMany(), prisma.entry.deleteMany(), @@ -20,9 +17,11 @@ export default async () => { prisma.category.createMany({ data: CATEGORIES }), prisma.station.createMany({ data: STATIONS }), prisma.room.createMany({ - data: ROOMS.map((v) => { - return { id: v.id, name: v.name, station_id: v.station.id }; - }), + data: [ + { id: 1, name: "R1.1", station_id: 1 }, + { id: 2, name: "R1.2", station_id: 1 }, + { id: 3, name: "R2.1", station_id: 2 }, + ], }), prisma.patient.createMany({ data: [ @@ -56,7 +55,7 @@ export default async () => { prisma.$executeRawUnsafe( `alter sequence stations_id_seq restart with ${STATIONS.length + 1}`, ), - prisma.$executeRawUnsafe(`alter sequence rooms_id_seq restart with ${ROOMS.length + 1}`), + prisma.$executeRawUnsafe("alter sequence rooms_id_seq restart with 4"), prisma.$executeRawUnsafe("alter sequence patients_id_seq restart with 4"), prisma.$executeRawUnsafe("alter sequence entry_executions_id_seq restart with 1"), prisma.$executeRawUnsafe("alter sequence entry_versions_id_seq restart with 1"), diff --git a/tests/helpers/testdata.ts b/tests/helpers/testdata.ts index 1f7b0d2..e802b0e 100644 --- a/tests/helpers/testdata.ts +++ b/tests/helpers/testdata.ts @@ -1,33 +1,9 @@ -import type { Category, Station, Room } from "$lib/shared/model"; +import type { Category, Station } from "$lib/shared/model"; export const S1: Station = { id: 1, name: "S1" }; export const S2: Station = { id: 2, name: "S2" }; -export const S3: Station = { id: 3, name: "S3_tmp" }; -export const STATIONS: Station[] = [S1, S2, S3]; - -export const ROOMS: Room[] = [ - { - id: 1, - name: "R1.1", - station: S1, - }, - { - id: 2, - name: "R1.2", - station: S1, - }, - { - id: 3, - name: "R2.1", - station: S2, - }, - { - id: 4, - name: "Rtmp", - station: S2, - }, -]; +export const STATIONS: Station[] = [S1, S2]; export const CATEGORIES: Category[] = [ { diff --git a/tests/integration/query/category.ts b/tests/integration/query/category.ts index 2e17793..2e4e0fc 100644 --- a/tests/integration/query/category.ts +++ b/tests/integration/query/category.ts @@ -1,8 +1,6 @@ import { expect, test } from "vitest"; -import { - deleteCategory, getCategories, getCategory, hideCategory, newCategory, -} from "$lib/server/query"; +import { getCategories, getCategory, newCategory } from "$lib/server/query"; import { CATEGORIES } from "$tests/helpers/testdata"; test("create category", async () => { @@ -23,19 +21,3 @@ test("get categories", async () => { const categories = await getCategories(); expect(categories).toStrictEqual(CATEGORIES); }); - -test("delete categories", async () => { - await deleteCategory(6); - expect(getCategory(6)).rejects.toThrowError("No Category found"); -}); - -test("hide category", async () => { - await hideCategory(1, true); - const cs1 = await getCategories(); - const exp = [...CATEGORIES]; - exp.splice(0, 1); - expect(cs1).toStrictEqual(exp); - await hideCategory(1, false); - const cs2 = await getCategories(); - expect(cs2).toStrictEqual(CATEGORIES); -}); diff --git a/tests/integration/query/entry.ts b/tests/integration/query/entry.ts index 226075d..3d8709e 100644 --- a/tests/integration/query/entry.ts +++ b/tests/integration/query/entry.ts @@ -3,14 +3,12 @@ import { expect, test } from "vitest"; import { ErrorConflict } from "$lib/shared/util/error"; import { - getCategoryNEntries, getEntries, getEntry, getEntryNExecutions, getEntryNVersions, getEntryVersions, getNTodo, - getPatientNEntries, newEntry, newEntryExecution, newEntryVersion, @@ -23,46 +21,6 @@ const TEST_VERSION = { priority: false, }; -async function insertTestEntries() { - // Create some entries - const eId1 = await newEntry(1, { - patient_id: 1, - version: TEST_VERSION, - }); - const eId2 = await newEntry(1, { - patient_id: 2, - version: { - text: "Carrot cake jelly-o bonbon toffee chocolate.", - date: "2024-01-05", - priority: false, - category_id: null, - }, - }); - const eId3 = await newEntry(1, { - patient_id: 1, - version: { - text: "Cheesecake danish donut oat cake caramels.", - date: "2024-01-06", - priority: false, - category_id: null, - }, - }); - - // Update an entry - await newEntryVersion(2, eId1, { - category_id: 3, - text: `${TEST_VERSION.text}\n\n> Hello World`, - date: "2024-01-01", - priority: true, - }); - - // Execute entries - await newEntryExecution(1, eId2, { text: "Some execution txt" }); - await newEntryExecution(2, eId3, { text: "More execution txt" }); - - return { eId1, eId2, eId3 }; -} - test("create entry", async () => { const eId = await newEntry(1, { patient_id: 1, @@ -205,6 +163,46 @@ test("create entry execution (wrong old xid)", async () => { expect(async () => newEntryExecution(1, eId, { text: "x2" }, x1 + 1)).rejects.toThrowError(new ErrorConflict("old execution id does not match")); }); +async function insertTestEntries() { + // Create some entries + const eId1 = await newEntry(1, { + patient_id: 1, + version: TEST_VERSION, + }); + const eId2 = await newEntry(1, { + patient_id: 2, + version: { + text: "Carrot cake jelly-o bonbon toffee chocolate.", + date: "2024-01-05", + priority: false, + category_id: null, + }, + }); + const eId3 = await newEntry(1, { + patient_id: 1, + version: { + text: "Cheesecake danish donut oat cake caramels.", + date: "2024-01-06", + priority: false, + category_id: null, + }, + }); + + // Update an entry + await newEntryVersion(2, eId1, { + category_id: 3, + text: `${TEST_VERSION.text}\n\n> Hello World`, + date: "2024-01-01", + priority: true, + }); + + // Execute entries + await newEntryExecution(1, eId2, { text: "Some execution txt" }); + await newEntryExecution(2, eId3, { text: "More execution txt" }); + + return { eId1, eId2, eId3 }; +} + test("get entries", async () => { const { eId1, eId2, eId3 } = await insertTestEntries(); const entries = await getEntries({}, {}); @@ -292,15 +290,5 @@ test("get entries", async () => { // NTodo const n = await getNTodo(new Date("2024-01-05")); - expect(n).toBe(1); -}); - -test("get patient n entries", async () => { - await insertTestEntries(); - expect(await getPatientNEntries(1)).toBe(2); -}); - -test("get category n entries", async () => { - await insertTestEntries(); - expect(await getCategoryNEntries(3)).toBe(1); + expect(n).toBe(2); }); diff --git a/tests/integration/query/patient.ts b/tests/integration/query/patient.ts index e2633de..4b0ee39 100644 --- a/tests/integration/query/patient.ts +++ b/tests/integration/query/patient.ts @@ -64,7 +64,9 @@ test("delete patient (restricted)", async () => { }, }); - expect(async () => deletePatient(pId)).rejects.toThrowError(); + expect(async () => deletePatient(pId)).rejects.toThrowError( + "cannot delete patient with entries", + ); }); test("hide patient", async () => { diff --git a/tests/integration/query/room.ts b/tests/integration/query/room.ts index b730a3f..d5ca5b0 100644 --- a/tests/integration/query/room.ts +++ b/tests/integration/query/room.ts @@ -1,20 +1,42 @@ import { expect, test } from "vitest"; -import type { RoomDetail } from "$lib/shared/model"; +import type { Room, Station } from "$lib/shared/model"; import { - deleteRoom, + deleteStation, getRoom, - getRoomNPatients, getRooms, getStation, - hideRoom, + getStations, newRoom, + newStation, updateStation, } from "$lib/server/query"; -import { - ROOMS, S1, -} from "$tests/helpers/testdata"; +import { S1, S2 } from "$tests/helpers/testdata"; + +test("create station", async () => { + const sId = await newStation({ name: "S3" }); + const station = await getStation(sId); + expect(station).toStrictEqual({ id: sId, name: "S3" } satisfies Station); +}); + +test("update station", async () => { + const name = "HelloStation"; + await updateStation(S1.id, { name }); + const station = await getStation(S1.id); + expect(station.id).toBe(S1.id); + expect(station.name).toBe(name); +}); + +test("delete station", async () => { + await deleteStation(S1.id); + expect(async () => getStation(S1.id)).rejects.toThrowError("No Station found"); +}); + +test("get stations", async () => { + const stations = await getStations(); + expect(stations).toStrictEqual([S1, S2]); +}); test("create room", async () => { const rId = await newRoom({ name: "A1", station_id: 1 }); @@ -23,8 +45,7 @@ test("create room", async () => { id: rId, name: "A1", station: S1, - hidden: false, - } satisfies RoomDetail); + } satisfies Room); }); test("update room", async () => { @@ -36,26 +57,27 @@ test("update room", async () => { }); test("delete room", async () => { - await deleteRoom(ROOMS[3].id); - expect(async () => getRoom(ROOMS[3].id)).rejects.toThrowError("No Room found"); -}); - -test("hide room", async () => { - await hideRoom(ROOMS[0].id, true); - const rs1 = await getRooms(); - const exp = [...ROOMS]; - exp.splice(0, 1); - expect(rs1).toStrictEqual(exp); - await hideRoom(ROOMS[0].id, false); - const rs2 = await getRooms(); - expect(rs2).toStrictEqual(ROOMS); + await deleteStation(S1.id); + expect(async () => getStation(S1.id)).rejects.toThrowError("No Station found"); }); test("get rooms", async () => { const rooms = await getRooms(); - expect(rooms).toStrictEqual(ROOMS); -}); - -test("get room n patients", async () => { - expect(await getRoomNPatients(ROOMS[0].id)).toBe(1); + expect(rooms).toStrictEqual([ + { + id: 1, + name: "R1.1", + station: S1, + }, + { + id: 2, + name: "R1.2", + station: S1, + }, + { + id: 3, + name: "R2.1", + station: S2, + }, + ] satisfies Room[]); }); diff --git a/tests/integration/query/savedFilter.ts b/tests/integration/query/savedFilter.ts deleted file mode 100644 index c7c02cc..0000000 --- a/tests/integration/query/savedFilter.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { expect, test } from "vitest"; - -import { - deleteSavedFilter, - getAllFilters, - getDefaultFilter, getSavedFilters, newSavedFilter, updateSavedFilter, -} from "$lib/server/query/savedFilter"; - -const VIEW = "plan"; - -async function createFilters() { - const fA1 = await newSavedFilter({ - name: "Todo", - query: "filter[done]=false", - view: VIEW, - }, 1); - const fA2 = await newSavedFilter({ - name: "Done", - query: "filter[done]=true", - view: VIEW, - }, 1); - const fADefault = await newSavedFilter({ - name: "Default", - query: "filter[done]=true&filter[station][0][id]=1&filter[station][0][name]=S1", - view: VIEW, - }, 1); - const fADefaultP = await newSavedFilter({ - name: "Default", - query: "filter[station][0][id]=1&filter[station][0][name]=S1", - view: "patients", - }, 1); - const fAOther = await newSavedFilter({ - name: "Done", - query: "filter[done]=true", - view: "other", - }, 1); - const fB1 = await newSavedFilter({ - name: "Prio", - query: "filter[priority]=true", - view: VIEW, - }, 2); - return { - fA1, fA2, fADefault, fB1, fADefaultP, fAOther, - }; -} - -const U1_FILTERS = (ids: Awaited>) => [ - // Default filter must be ordered on top - { - id: ids.fADefault, - name: "Default", - query: "filter[done]=true&filter[station][0][id]=1&filter[station][0][name]=S1", - }, - { - id: ids.fA1, - name: "Todo", - query: "filter[done]=false", - }, - { - id: ids.fA2, - name: "Done", - query: "filter[done]=true", - }, -]; - -test("get filters", async () => { - const ids = await createFilters(); - - const u1Filters = await getSavedFilters(1, VIEW); - expect(u1Filters).toStrictEqual(U1_FILTERS(ids)); - - const u2Filters = await getSavedFilters(2, VIEW); - expect(u2Filters).toStrictEqual([ - { - id: ids.fB1, - name: "Prio", - query: "filter[priority]=true", - }, - ]); -}); - -test("get default filter", async () => { - const ids = await createFilters(); - - const filter = await getDefaultFilter(1, VIEW); - expect(filter).toStrictEqual(U1_FILTERS(ids)[0]); -}); - -test("update filter", async () => { - const q = "filter[done]=true"; - const ids = await createFilters(); - await updateSavedFilter(ids.fADefault, q, 1); - - const filter = await getDefaultFilter(1, VIEW); - expect(filter?.query).toBe(q); -}); - -test("update filter not found", async () => { - expect(updateSavedFilter(1, "Hello World", 1)).rejects.toThrowError(); -}); - -test("delete filter", async () => { - const ids = await createFilters(); - await deleteSavedFilter(ids.fADefault, 1); - const filter = await getDefaultFilter(1, VIEW); - expect(filter).toBe(null); -}); - -test("get all filters", async () => { - const ids = await createFilters(); - const filters = await getAllFilters(1); - expect(filters).toStrictEqual({ - other: [ - { - id: ids.fAOther, - name: "Done", - query: "filter[done]=true", - }, - ], - patients: [ - { - id: ids.fADefaultP, - name: "Default", - query: "filter[station][0][id]=1&filter[station][0][name]=S1", - }, - ], - plan: U1_FILTERS(ids), - }); -}); diff --git a/tests/integration/query/station.ts b/tests/integration/query/station.ts deleted file mode 100644 index 65a8db9..0000000 --- a/tests/integration/query/station.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { expect, test } from "vitest"; - -import type { StationDetail } from "$lib/shared/model"; - -import { - deleteStation, getStation, getStationNRooms, getStations, hideStation, newStation, updateStation, -} from "$lib/server/query"; -import { - STATIONS, S1, S2, S3, -} from "$tests/helpers/testdata"; - -test("create station", async () => { - const sId = await newStation({ name: "S3" }); - const station = await getStation(sId); - expect(station).toStrictEqual({ id: sId, name: "S3", hidden: false } satisfies StationDetail); -}); - -test("update station", async () => { - const name = "HelloStation"; - await updateStation(S1.id, { name }); - const station = await getStation(S1.id); - expect(station.id).toBe(S1.id); - expect(station.name).toBe(name); -}); - -test("delete station", async () => { - await deleteStation(S3.id); - expect(async () => getStation(S3.id)).rejects.toThrowError("No Station found"); -}); - -test("hide station", async () => { - await hideStation(S1.id, true); - const cs1 = await getStations(); - expect(cs1).toStrictEqual([S2, S3]); - await hideStation(S1.id, false); - const cs2 = await getStations(); - expect(cs2).toStrictEqual(STATIONS); -}); - -test("get stations", async () => { - const stations = await getStations(); - expect(stations).toStrictEqual(STATIONS); -}); - -test("get station n rooms", async () => { - expect(await getStationNRooms(1)).toBe(2); -}); diff --git a/vite.config.js b/vite.config.js index 6539b16..49c9e7c 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,77 +1,9 @@ -import { exec } from "child_process"; -import { promisify } from "util"; - import { sveltekit } from "@sveltejs/kit/vite"; -import { createViteLicensePlugin } from "rollup-license-plugin"; import { defineConfig } from "vitest/config"; -// Get current tag/commit and last commit date from git -const pexec = promisify(exec); -let [version, lastmod] = ( - await Promise.allSettled([ - pexec("git describe --tags || git rev-parse --short HEAD"), - pexec('git log -1 --format=%cd --date=format:"%Y-%m-%d %H:%M"'), - ]) -).map((v) => { - if (v.status !== "fulfilled") throw Error("could not get version info from git"); - return JSON.stringify(v.value.stdout.trim()); -}); - export default defineConfig({ - plugins: [ - sveltekit(), - createViteLicensePlugin({ - additionalFiles: { - "oss-licenses.html": (packages) => { - let res = ` - -Visitenbuch - Lizenzen - - -

Open-Source-Lizenzen

-JSON-formatted license list -`; - for (const p of packages) { - // @ts-expect-error repo not present in type definition - let rp = p.repository; - // @ts-expect-error author not present in type definition - let aut = p.author; - if (typeof aut === "object") { - aut = aut.name; - } - - let repoUrl = null; - if (typeof rp === "string") { - if (rp.startsWith("http")) repoUrl = rp; - else if (rp.startsWith("git+http")) repoUrl = rp.substring(4); - else if (rp.startsWith("git://")) repoUrl = "https://" + rp.substring(6); - else if (rp.startsWith("github:")) repoUrl = "https://github.com/" + rp.substring(7); - else if (rp.match(/^[\w-]+\/[\w-]+$/)) repoUrl = "https://github.com/" + rp; - } - - res += `
\n`; - res += `

${p.name}

\n`; - res += `\n`; - res += `\n`; - if (aut) res += `\n`; - res += `\n`; - if (repoUrl) res += `\n`; - else if (rp) res += `\n`; - res += `
Version:${p.version}
Author:${aut}
License:${p.license}
Repository:${repoUrl}
Repository:${rp}
\n`; - res += "
"; - } - - res += "\n\n"; - return res; - }, - }, - }), - ], + plugins: [sveltekit()], test: { include: ["src/**/*.{test,spec}.{js,ts}"], }, - define: { - __VERSION__: version, - __LASTMOD__: lastmod, - }, });