From c7ea0aa8dbcef2852cc39293bacb31f792c186c7 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Wed, 31 Jan 2024 18:03:44 +0100 Subject: [PATCH 1/4] feat: use url path parameter for filters --- src/app.pcss | 4 ++++ src/hooks.server.ts | 1 + src/lib/components/filter/FilterBar.svelte | 4 ++-- src/lib/components/table/EntryTable.svelte | 11 ++++++++++- src/lib/components/table/PatientField.svelte | 4 +++- src/lib/components/table/UserField.svelte | 2 +- src/lib/shared/model/validation.ts | 1 + src/lib/shared/util/index.ts | 9 ++++----- src/routes/(app)/+page.svelte | 2 +- src/routes/(app)/entry/[id]/+page.svelte | 15 +++++++++++++++ src/routes/(app)/entry/[id]/+page.ts | 10 ++++++++++ .../(app)/plan/{ => [[query]]}/+page.svelte | 4 ++-- src/routes/(app)/plan/{ => [[query]]}/+page.ts | 5 ++--- 13 files changed, 56 insertions(+), 16 deletions(-) create mode 100644 src/routes/(app)/entry/[id]/+page.svelte create mode 100644 src/routes/(app)/entry/[id]/+page.ts rename src/routes/(app)/plan/{ => [[query]]}/+page.svelte (95%) rename src/routes/(app)/plan/{ => [[query]]}/+page.ts (78%) diff --git a/src/app.pcss b/src/app.pcss index 2d53edd..4abb4ca 100644 --- a/src/app.pcss +++ b/src/app.pcss @@ -7,6 +7,10 @@ button { text-align: initial; } +.heading { + @apply text-2xl font-bold; +} + .ellipsis { overflow: hidden; white-space: nowrap; diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 2b69631..065efc2 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -65,6 +65,7 @@ export const handle = sequence( return opt.session; }, }, + trustHost: true, }), authorization, createTRPCHandle({ router, createContext }) diff --git a/src/lib/components/filter/FilterBar.svelte b/src/lib/components/filter/FilterBar.svelte index eb8b41d..1adfb98 100644 --- a/src/lib/components/filter/FilterBar.svelte +++ b/src/lib/components/filter/FilterBar.svelte @@ -40,8 +40,8 @@ ); // Load query data if present - $: if (filterData) { - updateFromQueryData(filterData); + $: if (filterData || !filterData) { + updateFromQueryData(filterData ?? {}); } function updateFromQueryData(filterData: FilterQdata) { diff --git a/src/lib/components/table/EntryTable.svelte b/src/lib/components/table/EntryTable.svelte index 28436f3..c5eea32 100644 --- a/src/lib/components/table/EntryTable.svelte +++ b/src/lib/components/table/EntryTable.svelte @@ -8,6 +8,7 @@ import UserField from "./UserField.svelte"; import RoomField from "./RoomField.svelte"; import SortHeader from "./SortHeader.svelte"; + import { goto } from "$app/navigation"; export let entries: RouterOutput["entry"]["list"]; export let sortData: SortRequest | undefined = undefined; @@ -32,11 +33,19 @@ {#each entries.items as entry} + {@const entryUrl = `/entry/${entry.id}`} - {entry.id} + {entry.id} {#if entry.patient.room}{/if} - + diff --git a/src/lib/components/table/UserField.svelte b/src/lib/components/table/UserField.svelte index 6525cba..0d2eefb 100644 --- a/src/lib/components/table/UserField.svelte +++ b/src/lib/components/table/UserField.svelte @@ -14,4 +14,4 @@ } - + diff --git a/src/lib/shared/model/validation.ts b/src/lib/shared/model/validation.ts index f9dcae7..75d17af 100644 --- a/src/lib/shared/model/validation.ts +++ b/src/lib/shared/model/validation.ts @@ -15,6 +15,7 @@ import type { } from "."; export const ZEntityId = z.number().int().nonnegative(); +export const ZUrlEntityId = z.coerce.number().int().nonnegative(); const ZNameString = z.string().min(1).max(200).trim(); const ZTextString = z.string().trim(); diff --git a/src/lib/shared/util/index.ts b/src/lib/shared/util/index.ts index cd5453d..586547e 100644 --- a/src/lib/shared/util/index.ts +++ b/src/lib/shared/util/index.ts @@ -23,14 +23,13 @@ export function formatDate(date: Date | string, time = false): string { } } -export function getQueryUrl(q: EntityQuery, basePath: string = ""): string { - return basePath + "?q=" + JSON.stringify(q); +export function getQueryUrl(q: EntityQuery, basePath: string): string { + return basePath + "/" + JSON.stringify(q); } export function gotoEntityQuery(q: EntityQuery, basePath: string) { - if (window && basePath === window.location.pathname) { - const ws = new URLSearchParams(window.location.search); - const qstr = ws.get("q"); + if (window && window.location.pathname.startsWith(basePath + "/")) { + const qstr = decodeURI(window.location.pathname.substring(basePath.length + 1)); if (qstr) { const oldq: EntityQuery = JSON.parse(qstr); const nq: EntityQuery = { diff --git a/src/routes/(app)/+page.svelte b/src/routes/(app)/+page.svelte index e81153d..4c81131 100644 --- a/src/routes/(app)/+page.svelte +++ b/src/routes/(app)/+page.svelte @@ -7,7 +7,7 @@ {#if $page.data.session?.user} -

Hallo, {$page.data.session.user.name}

+

Hallo, {$page.data.session.user.name}

{:else}

Sie sind nicht angemeldet

{/if} diff --git a/src/routes/(app)/entry/[id]/+page.svelte b/src/routes/(app)/entry/[id]/+page.svelte new file mode 100644 index 0000000..bad6356 --- /dev/null +++ b/src/routes/(app)/entry/[id]/+page.svelte @@ -0,0 +1,15 @@ + + +

Entry #{data.entry.id}

+ +

{data.entry.current_version.text}

+ +

Execution:

+ +{#if data.entry.execution} +

{data.entry.execution?.text}

+{/if} diff --git a/src/routes/(app)/entry/[id]/+page.ts b/src/routes/(app)/entry/[id]/+page.ts new file mode 100644 index 0000000..6251fac --- /dev/null +++ b/src/routes/(app)/entry/[id]/+page.ts @@ -0,0 +1,10 @@ +import { ZUrlEntityId } from "$lib/shared/model/validation"; +import { trpc } from "$lib/shared/trpc"; +import type { PageLoad } from "./$types"; + +export const load: PageLoad = async (event) => { + const id = ZUrlEntityId.parse(event.params.id); + const entry = await trpc(event).entry.get.query(id); + + return { entry }; +}; diff --git a/src/routes/(app)/plan/+page.svelte b/src/routes/(app)/plan/[[query]]/+page.svelte similarity index 95% rename from src/routes/(app)/plan/+page.svelte rename to src/routes/(app)/plan/[[query]]/+page.svelte index d2bab98..d97e389 100644 --- a/src/routes/(app)/plan/+page.svelte +++ b/src/routes/(app)/plan/[[query]]/+page.svelte @@ -1,6 +1,6 @@ + +
+ + + + + + + + + + + + {#each patients.items as patient} + + + + + + + + + {/each} + +
+
{patient.id}{patient.first_name} {patient.last_name}{patient.age} + {#if patient.room} + + {/if} + {formatDate(patient.created_at, true)} + +
+
+ + diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte index 3d542a0..6755954 100644 --- a/src/routes/(app)/+layout.svelte +++ b/src/routes/(app)/+layout.svelte @@ -19,8 +19,9 @@ cls={$page.route.id === "/(app)" ? "text-primary" : ""} /> - Planung + Planung Visite + Patienten
{#if $page.data.session?.user} @@ -34,6 +35,9 @@ tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52" > +
  • Zimmer
  • +
  • Kategorien
  • +
  • Benutzer
  • + + Throw error diff --git a/src/routes/(app)/entry/[id]/+page.svelte b/src/routes/(app)/entry/[id]/+page.svelte index a525afc..38f223b 100644 --- a/src/routes/(app)/entry/[id]/+page.svelte +++ b/src/routes/(app)/entry/[id]/+page.svelte @@ -5,6 +5,7 @@ import UserField from "$lib/components/table/UserField.svelte"; import RoomField from "$lib/components/table/RoomField.svelte"; import CategoryField from "$lib/components/table/CategoryField.svelte"; + import { MARKDOWN_RENDERERS } from "$lib/shared/constants"; export let data: PageData; @@ -33,7 +34,10 @@

    - +

    @@ -45,7 +49,10 @@

    - +

    {/if} diff --git a/src/routes/(app)/entry/[id]/+page.ts b/src/routes/(app)/entry/[id]/+page.ts index 6251fac..204d7cf 100644 --- a/src/routes/(app)/entry/[id]/+page.ts +++ b/src/routes/(app)/entry/[id]/+page.ts @@ -1,10 +1,11 @@ import { ZUrlEntityId } from "$lib/shared/model/validation"; import { trpc } from "$lib/shared/trpc"; +import { loadWrap } from "$lib/shared/util"; import type { PageLoad } from "./$types"; export const load: PageLoad = async (event) => { const id = ZUrlEntityId.parse(event.params.id); - const entry = await trpc(event).entry.get.query(id); + const entry = await loadWrap(async () => trpc(event).entry.get.query(id)); return { entry }; }; diff --git a/src/routes/(app)/patients/[[query]]/+page.ts b/src/routes/(app)/patients/[[query]]/+page.ts index a912f74..af436ff 100644 --- a/src/routes/(app)/patients/[[query]]/+page.ts +++ b/src/routes/(app)/patients/[[query]]/+page.ts @@ -3,6 +3,7 @@ import { z } from "zod"; import { ZPatientsQuery } from "$lib/shared/model/validation"; import { trpc } from "$lib/shared/trpc"; import type { PageLoad } from "./$types"; +import { loadWrap } from "$lib/shared/util"; export const load: PageLoad = async (event) => { let query: z.infer = {}; @@ -11,7 +12,7 @@ export const load: PageLoad = async (event) => { query = ZPatientsQuery.parse(JSON.parse(event.params.query)); } - const patients = await trpc(event).patient.list.query(query); + const patients = await loadWrap(async () => trpc(event).patient.list.query(query)); return { query, patients }; }; diff --git a/src/routes/(app)/plan/[[query]]/+page.svelte b/src/routes/(app)/plan/[[query]]/+page.svelte index 405a76f..c9fa6bc 100644 --- a/src/routes/(app)/plan/[[query]]/+page.svelte +++ b/src/routes/(app)/plan/[[query]]/+page.svelte @@ -10,10 +10,13 @@ import { trpc } from "$lib/shared/trpc"; import { ENTRY_FILTERS } from "$lib/components/filter/filters"; import { getQueryUrl } from "$lib/shared/util"; + import LoadingBar from "$lib/components/ui/LoadingBar.svelte"; + import ErrorMessage from "$lib/components/ui/ErrorMessage.svelte"; export let data: PageData; - let loading = false; + let loadingBar: LoadingBar | undefined; + let error: Error | null = null; function paginationUpdate(pagination: PaginationRequest) { updateQuery({ @@ -42,13 +45,21 @@ const url = getQueryUrl(query, "/plan"); window.history.replaceState(null, "", url); - loading = true; + loadingBar?.start(); trpc() .entry.list.query(query) .then((entries) => { data.entries = entries; + error = null; + loadingBar?.reset(); + }) + .catch((err) => { + data.query = query; + error = err; + loadingBar?.error(); + }) + .finally(() => { data.query = query; - loading = false; }); } } @@ -64,14 +75,16 @@ onUpdate={filterUpdate} /> -{#if loading} -

    Loading...

    + + +{#if error} + +{:else} + + + {/if} - - - - diff --git a/src/routes/(app)/plan/[[query]]/+page.ts b/src/routes/(app)/plan/[[query]]/+page.ts index d336cb0..7476822 100644 --- a/src/routes/(app)/plan/[[query]]/+page.ts +++ b/src/routes/(app)/plan/[[query]]/+page.ts @@ -1,17 +1,15 @@ -import { z } from "zod"; - -import { ZEntriesQuery } from "$lib/shared/model/validation"; import { trpc } from "$lib/shared/trpc"; +import { loadWrap } from "$lib/shared/util"; import type { PageLoad } from "./$types"; export const load: PageLoad = async (event) => { - let query: z.infer = {}; + let query = {}; if (event.params.query) { - query = ZEntriesQuery.parse(JSON.parse(event.params.query)); + query = JSON.parse(event.params.query); } - const entries = await trpc(event).entry.list.query(query); + const entries = await loadWrap(() => trpc(event).entry.list.query(query)); return { query, entries }; }; diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte new file mode 100644 index 0000000..ac78805 --- /dev/null +++ b/src/routes/+error.svelte @@ -0,0 +1,15 @@ + + + + Visitenbuch: Fehler {$page.status} + + +{#if $page.error} + +{/if} diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index e680e4a..71aeada 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -1,7 +1,23 @@ + +
    From 622c8fdd39481c733a975e1b1b826e589112bd61 Mon Sep 17 00:00:00 2001 From: ThetaDev Date: Sun, 4 Feb 2024 22:20:48 +0100 Subject: [PATCH 4/4] fix: use DATE type for entry date field --- prisma/migrations/20240113185049_init/migration.sql | 2 +- prisma/schema.prisma | 2 +- tests/helpers/generate-mockdata.ts | 2 +- tests/integration/query/entry.ts | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/prisma/migrations/20240113185049_init/migration.sql b/prisma/migrations/20240113185049_init/migration.sql index ff8c396..e9f7a31 100644 --- a/prisma/migrations/20240113185049_init/migration.sql +++ b/prisma/migrations/20240113185049_init/migration.sql @@ -78,7 +78,7 @@ CREATE TABLE "entry_versions" ( "id" SERIAL NOT NULL, "entry_id" INTEGER NOT NULL, "text" TEXT NOT NULL, - "date" TIMESTAMP(3) NOT NULL, + "date" DATE NOT NULL, "category_id" INTEGER, "priority" BOOLEAN NOT NULL, "author_id" INTEGER NOT NULL, diff --git a/prisma/schema.prisma b/prisma/schema.prisma index aebefd9..915ca43 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -116,7 +116,7 @@ model EntryVersion { entry_id Int text String - date DateTime + date DateTime @db.Date category Category? @relation(fields: [category_id], references: [id], onDelete: SetNull) category_id Int? diff --git a/tests/helpers/generate-mockdata.ts b/tests/helpers/generate-mockdata.ts index 53b7356..2cc2b4c 100644 --- a/tests/helpers/generate-mockdata.ts +++ b/tests/helpers/generate-mockdata.ts @@ -15,7 +15,7 @@ const CATEGORY_IDS: { [id: string]: number } = { Entlassung: 5, Sonstiges: 6, }; -const refDate = new Date(2023, 11, 1); +const refDate = new Date(Date.UTC(2023, 11, 1)); const N_USERS = 10; const N_ROOMS = 20; diff --git a/tests/integration/query/entry.ts b/tests/integration/query/entry.ts index 03815e0..f7303fc 100644 --- a/tests/integration/query/entry.ts +++ b/tests/integration/query/entry.ts @@ -12,7 +12,7 @@ import { expect, test } from "vitest"; const TEST_VERSION = { category_id: 1, text: "10ml Blut abnehmen", - date: new Date(2024, 1, 1), + date: new Date(Date.UTC(2024, 1, 1)), priority: false, }; @@ -40,7 +40,7 @@ test("create entry version", async () => { version: TEST_VERSION, }); const text = "10ml Blut abnehmen\n\nPS: Nadel nicht vergessen"; - const date = new Date(2024, 1, 2); + const date = new Date(Date.UTC(2024, 1, 2)); await newEntryVersion(1, eId, { date, @@ -155,7 +155,7 @@ async function insertTestEntries() { patient_id: 2, version: { text: "Carrot cake jelly-o bonbon toffee chocolate.", - date: new Date(2024, 1, 5), + date: new Date(Date.UTC(2024, 1, 5)), priority: false, category_id: null, }, @@ -164,7 +164,7 @@ async function insertTestEntries() { patient_id: 1, version: { text: "Cheesecake danish donut oat cake caramels.", - date: new Date(2024, 1, 6), + date: new Date(Date.UTC(2024, 1, 6)), priority: false, category_id: null, }, @@ -174,7 +174,7 @@ async function insertTestEntries() { await newEntryVersion(2, eId1, { category_id: 3, text: TEST_VERSION.text + "\n\n> Hello World", - date: new Date(2024, 1, 1), + date: new Date(Date.UTC(2024, 1, 1)), priority: true, });