diff --git a/CHANGELOG.md b/CHANGELOG.md index 595addb..3d7c51d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. +## [v0.2.1](https://code.thetadev.de/HSA/Visitenbuch/compare/v0.2.0..v0.2.1) - 2024-05-07 + +### 🐛 Bug Fixes + +- Carta editor mobile context menu transparent - ([6bd7e15](https://code.thetadev.de/HSA/Visitenbuch/commit/6bd7e157eea6589be82692499cb956455386f7e5)) +- Dont output null age - ([c2c21d1](https://code.thetadev.de/HSA/Visitenbuch/commit/c2c21d1296c399324cdfa661c490cee79f7e16e1)) + + ## [v0.2.0](https://code.thetadev.de/HSA/Visitenbuch/compare/v0.1.0..v0.2.0) - 2024-05-06 ### 🚀 Features diff --git a/package.json b/package.json index fbb40e9..9ba37e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "visitenbuch", - "version": "0.2.0", + "version": "0.2.1", "private": true, "license": "AGPL-3.0", "scripts": { @@ -21,7 +21,7 @@ "@floating-ui/core": "^1.6.1", "@mdi/js": "^7.4.47", "@prisma/client": "^5.13.0", - "carta-md": "4.0.2", + "@thetadev/carta-md": "^1.0.1", "diff": "^5.2.0", "isomorphic-dompurify": "^2.9.0", "prisma": "^5.13.0", @@ -71,10 +71,5 @@ "vite": "^5.2.11", "vitest": "^1.6.0" }, - "type": "module", - "pnpm": { - "patchedDependencies": { - "carta-md@4.0.2": "patches/carta-md@4.0.2.patch" - } - } + "type": "module" } diff --git a/patches/carta-md@4.0.2.patch b/patches/carta-md@4.0.2.patch deleted file mode 100644 index 2b8589e..0000000 --- a/patches/carta-md@4.0.2.patch +++ /dev/null @@ -1,27 +0,0 @@ -# Change "markdown-body" css class to "prose" for Tailwind compatibility -diff --git a/dist/Markdown.svelte b/dist/Markdown.svelte -index 92b29fade303a14539720b9bc389e7a41202b1cf..cbdeede16d7af17a481bcac519aea92b8959803d 100644 ---- a/dist/Markdown.svelte -+++ b/dist/Markdown.svelte -@@ -15,7 +15,7 @@ onMount(async () => { - }); - - --
-+
- - {@html rendered} - {#if mounted} -diff --git a/dist/internal/components/Renderer.svelte b/dist/internal/components/Renderer.svelte -index 1d2ff1a6937bc490ea1e6eb5c2ef9f3b33e4c326..6a95c154ea4ca7c4d19b20d02b3504fb2b65b7f7 100644 ---- a/dist/internal/components/Renderer.svelte -+++ b/dist/internal/components/Renderer.svelte -@@ -17,7 +17,7 @@ onMount(() => carta.$setRenderer(elem)); - onMount(() => mounted = true); - - --
-+
- - {@html renderedHtml} - {#if mounted} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e5a6cc0..23cae13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,11 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -patchedDependencies: - carta-md@4.0.2: - hash: i33ea43vfgrg3ziu25cfu7s2zq - path: patches/carta-md@4.0.2.patch - dependencies: '@auth/core': specifier: ^0.30.0 @@ -22,9 +17,9 @@ dependencies: '@prisma/client': specifier: ^5.13.0 version: 5.13.0(prisma@5.13.0) - carta-md: - specifier: 4.0.2 - version: 4.0.2(patch_hash=i33ea43vfgrg3ziu25cfu7s2zq)(svelte@4.2.15) + '@thetadev/carta-md': + specifier: ^1.0.1 + version: 1.0.1(svelte@4.2.15) diff: specifier: ^5.2.0 version: 5.2.0 @@ -889,10 +884,6 @@ packages: dev: true optional: true - /@shikijs/core@1.4.0: - resolution: {integrity: sha512-CxpKLntAi64h3j+TwWqVIQObPTED0FyXLHTTh3MKXtqiQNn2JGcMQQ362LftDbc9kYbDtrksNMNoVmVXzKFYUQ==} - dev: false - /@sideway/address@4.1.5: resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} requiresBuild: true @@ -1088,6 +1079,22 @@ packages: tailwindcss: 3.4.3 dev: true + /@thetadev/carta-md@1.0.1(svelte@4.2.15): + resolution: {integrity: sha512-B6TyB5gvrc1T5pEOM5nIZA+N6gG7EK7AJBrQCfokzrpNarq7CvgJEVaQoWh5oukB2MrRYSIQAYUNIoqEKzMSag==} + peerDependencies: + svelte: ^3.54.0 || ^4.0.0 + dependencies: + prismjs: 1.29.0 + rehype-stringify: 10.0.0 + remark-gfm: 4.0.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.0 + svelte: 4.2.15 + unified: 11.0.4 + transitivePeerDependencies: + - supports-color + dev: false + /@trpc/client@10.45.2(@trpc/server@10.45.2): resolution: {integrity: sha512-ykALM5kYWTLn1zYuUOZ2cPWlVfrXhc18HzBDyRhoPYN0jey4iQHEFSEowfnhg1RvYnrAVjNBgHNeSAXjrDbGwg==} peerDependencies: @@ -1757,23 +1764,6 @@ packages: resolution: {integrity: sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==} dev: true - /carta-md@4.0.2(patch_hash=i33ea43vfgrg3ziu25cfu7s2zq)(svelte@4.2.15): - resolution: {integrity: sha512-wMlw0r5RZiVwvF3dyxE/vHj9pXlXbzpijJ3m/o9zqZe7Cf6D96AjyBHBpa0A0OPj/uEJVF3k0R6ctopBJCpCQg==} - peerDependencies: - svelte: ^3.54.0 || ^4.0.0 - dependencies: - rehype-stringify: 10.0.0 - remark-gfm: 4.0.0 - remark-parse: 11.0.0 - remark-rehype: 11.1.0 - shiki: 1.4.0 - svelte: 4.2.15 - unified: 11.0.4 - transitivePeerDependencies: - - supports-color - dev: false - patched: true - /ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} dev: false @@ -4424,6 +4414,11 @@ packages: '@prisma/engines': 5.13.0 dev: false + /prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + dev: false + /property-expr@2.0.6: resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} requiresBuild: true @@ -4721,12 +4716,6 @@ packages: engines: {node: '>=8'} dev: true - /shiki@1.4.0: - resolution: {integrity: sha512-5WIn0OL8PWm7JhnTwRWXniy6eEDY234mRrERVlFa646V2ErQqwIFd2UML7e0Pq9eqSKLoMa3Ke+xbsF+DAuy+Q==} - dependencies: - '@shikijs/core': 1.4.0 - dev: false - /side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} diff --git a/prisma/migrations/20240509111910_execution_done/migration.sql b/prisma/migrations/20240509111910_execution_done/migration.sql new file mode 100644 index 0000000..fc40831 --- /dev/null +++ b/prisma/migrations/20240509111910_execution_done/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "entry_executions" ADD COLUMN "done" BOOLEAN NOT NULL DEFAULT true; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b626e89..633705d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -142,6 +142,7 @@ model EntryExecution { entry_id Int text String + done Boolean @default(true) author User @relation(fields: [author_id], references: [id]) author_id Int diff --git a/src/lib/components/entry/EntryBody.svelte b/src/lib/components/entry/EntryBody.svelte index 2dd3afc..48b5383 100644 --- a/src/lib/components/entry/EntryBody.svelte +++ b/src/lib/components/entry/EntryBody.svelte @@ -39,7 +39,7 @@

Erstellt {humanDate(entry.created_at, true)} · - {#if entry.execution} + {#if entry.execution?.done} Erledigt {humanDate(entry.execution.created_at)} {:else} Zu erledigen {humanDate(entry.current_version.date)} @@ -53,7 +53,7 @@ Beschreibung

- +
@@ -69,9 +69,12 @@ {#if withExecution && entry.execution}
-
+

- Erledigt am {formatDate(entry.execution.created_at, true)} von + {entry.execution.done ? "Erledigt am" : "Notiz von"} {formatDate(entry.execution.created_at, true)} von

@@ -88,3 +91,12 @@ {/if}
{/if} + + diff --git a/src/lib/components/table/EntryTable.svelte b/src/lib/components/table/EntryTable.svelte index 7143f49..6ea8b98 100644 --- a/src/lib/components/table/EntryTable.svelte +++ b/src/lib/components/table/EntryTable.svelte @@ -1,5 +1,4 @@ @@ -38,7 +37,7 @@ {#each entries.items as entry (entry.id)} {entry.current_version.text} - {#if entry.execution} + {#if entry.execution?.done} {formatDate(entry.execution.created_at, true)} void = () => {}; + export let sortData: string[] | undefined; + export let sortUpdate: (sort: string[] | undefined) => void = () => {}; export let baseUrl: string; diff --git a/src/lib/components/table/SortHeader.svelte b/src/lib/components/table/SortHeader.svelte index 40aab97..c4007fe 100644 --- a/src/lib/components/table/SortHeader.svelte +++ b/src/lib/components/table/SortHeader.svelte @@ -1,29 +1,33 @@ @@ -32,6 +36,9 @@ {#if sorting > 0} {/if} + {#if sortData && sortData.length > 1 && index !== -1} + ({index + 1}) + {/if} {title} diff --git a/src/lib/components/ui/EntryTodoButton.svelte b/src/lib/components/ui/EntryTodoButton.svelte new file mode 100644 index 0000000..6bedd3e --- /dev/null +++ b/src/lib/components/ui/EntryTodoButton.svelte @@ -0,0 +1,11 @@ +
+ {#each { length: 4 } as _, i} + + {/each} +
diff --git a/src/lib/components/ui/markdown/Markdown.svelte b/src/lib/components/ui/markdown/Markdown.svelte index c43ffe0..d67cd3e 100644 --- a/src/lib/components/ui/markdown/Markdown.svelte +++ b/src/lib/components/ui/markdown/Markdown.svelte @@ -1,10 +1,15 @@ - +
+ + {@html rendered} +
diff --git a/src/lib/components/ui/markdown/MarkdownInput.svelte b/src/lib/components/ui/markdown/MarkdownInput.svelte index df91d0a..276904e 100644 --- a/src/lib/components/ui/markdown/MarkdownInput.svelte +++ b/src/lib/components/ui/markdown/MarkdownInput.svelte @@ -1,11 +1,13 @@ -{#if !data.entry.execution} +{#if !data.entry.execution?.done}
-
+
+
+ {/if} diff --git a/src/routes/(app)/entry/[id]/+page.ts b/src/routes/(app)/entry/[id]/+page.ts index e99bd7b..3e48be4 100644 --- a/src/routes/(app)/entry/[id]/+page.ts +++ b/src/routes/(app)/entry/[id]/+page.ts @@ -1,14 +1,26 @@ import type { PageLoad } from "./$types"; +import { superValidate } from "sveltekit-superforms"; + import { ZUrlEntityId } from "$lib/shared/model/validation"; import { trpc } from "$lib/shared/trpc"; import { loadWrap } from "$lib/shared/util"; +import { SchemaNewExecution } from "./editExecution/schema"; + export const load: PageLoad = async (event) => { const entry = await loadWrap(async () => { const id = ZUrlEntityId.parse(event.params.id); return trpc(event).entry.get.query(id); }); - return { entry }; + const form = await superValidate( + { + old_execution_id: entry.execution?.id, + ...entry.execution, + }, + SchemaNewExecution, + ); + + return { entry, form }; }; diff --git a/src/routes/(app)/entry/[id]/editExecution/+page.server.ts b/src/routes/(app)/entry/[id]/editExecution/+page.server.ts index 8a0cb57..2fd0dbe 100644 --- a/src/routes/(app)/entry/[id]/editExecution/+page.server.ts +++ b/src/routes/(app)/entry/[id]/editExecution/+page.server.ts @@ -5,24 +5,33 @@ import { superValidate } from "sveltekit-superforms"; import { ZUrlEntityId } from "$lib/shared/model/validation"; import { trpc } from "$lib/shared/trpc"; -import { loadWrap } from "$lib/shared/util"; +import { loadWrap, moveEntryTodoDate } from "$lib/shared/util"; import { SchemaNewExecution } from "./schema"; export const actions: Actions = { default: async (event) => loadWrap(async () => { - const form = await superValidate(event.request, SchemaNewExecution); + const formData = await event.request.formData(); + const form = await superValidate(formData, SchemaNewExecution); if (!form.valid) { return fail(400, { form }); } const id = ZUrlEntityId.parse(event.params.id); + const todoDays = formData.get("todo"); + await trpc(event).entry.newExecution.mutate({ id, - execution: form.data, + execution: { + text: form.data.text, + done: todoDays === null, + }, old_execution_id: form.data.old_execution_id, }); + const nTodoDays = todoDays ? parseInt(todoDays.toString()) : 0; + await moveEntryTodoDate(id, nTodoDays, event); + redirect(302, `/entry/${id}`); }), }; diff --git a/src/routes/(app)/entry/[id]/editExecution/+page.svelte b/src/routes/(app)/entry/[id]/editExecution/+page.svelte index 4e94ca4..6815372 100644 --- a/src/routes/(app)/entry/[id]/editExecution/+page.svelte +++ b/src/routes/(app)/entry/[id]/editExecution/+page.svelte @@ -2,19 +2,19 @@ import type { PageData } from "./$types"; - import { defaults, superForm } from "sveltekit-superforms"; + import { superForm } from "sveltekit-superforms"; import { superformConfig } from "$lib/shared/util"; import EntryBody from "$lib/components/entry/EntryBody.svelte"; + import EntryTodoButton from "$lib/components/ui/EntryTodoButton.svelte"; import MarkdownInput from "$lib/components/ui/markdown/MarkdownInput.svelte"; import { SchemaNewExecution } from "./schema"; export let data: PageData; - const formData = defaults(SchemaNewExecution); - const { form, errors, enhance } = superForm(formData, { + const { form, errors, enhance } = superForm(data.form, { validators: SchemaNewExecution, ...superformConfig("Eintrag"), }); @@ -30,8 +30,10 @@ label="Ausführungstext bearbeiten" bind:value={$form.text} > -
+
+
+ diff --git a/src/routes/(app)/entry/[id]/executions/+page.svelte b/src/routes/(app)/entry/[id]/executions/+page.svelte index 694c7f2..9f39031 100644 --- a/src/routes/(app)/entry/[id]/executions/+page.svelte +++ b/src/routes/(app)/entry/[id]/executions/+page.svelte @@ -22,6 +22,9 @@
#{i + 1}  , {formatDate(version.created_at, true)} + {#if !version.done} +
Notiz
+ {/if}
{#if version.text.length > 0}
diff --git a/src/routes/(app)/visit/+page.ts b/src/routes/(app)/visit/+page.ts index c6b99cc..b3c727a 100644 --- a/src/routes/(app)/visit/+page.ts +++ b/src/routes/(app)/visit/+page.ts @@ -22,7 +22,7 @@ export const load: PageLoad = async (event) => { // Sort entries by date if (!query.sort) { - query.sort = { field: "date" }; + query.sort = ["priority:dsc", "date"]; } const entries = await trpc(event).entry.list.query(query); diff --git a/tests/integration/query/entry.ts b/tests/integration/query/entry.ts index 226075d..78c87d4 100644 --- a/tests/integration/query/entry.ts +++ b/tests/integration/query/entry.ts @@ -57,8 +57,8 @@ async function insertTestEntries() { }); // Execute entries - await newEntryExecution(1, eId2, { text: "Some execution txt" }); - await newEntryExecution(2, eId3, { text: "More execution txt" }); + await newEntryExecution(1, eId2, { text: "Some execution txt", done: true }); + await newEntryExecution(2, eId3, { text: "More execution txt", done: true }); return { eId1, eId2, eId3 }; } @@ -166,7 +166,7 @@ test("create entry execution", async () => { }); const text = "Blutabnahme erledigt."; - const xId = await newEntryExecution(1, eId, { text }, null); + const xId = await newEntryExecution(1, eId, { text, done: true }, null); const entry = await getEntry(eId); expect(entry.execution?.id).toBe(xId); @@ -183,8 +183,8 @@ test("create entry execution (update)", async () => { version: TEST_VERSION, }); - const x1 = await newEntryExecution(1, eId, { text: "x1" }, null); - const x2 = await newEntryExecution(2, eId, { text: "x2" }, x1); + const x1 = await newEntryExecution(1, eId, { text: "x1", done: true }, null); + const x2 = await newEntryExecution(2, eId, { text: "x2", done: true }, x1); const entry = await getEntry(eId); expect(entry.execution?.id).toBe(x2); @@ -200,9 +200,9 @@ test("create entry execution (wrong old xid)", async () => { patient_id: 1, version: TEST_VERSION, }); - const x1 = await newEntryExecution(1, eId, { text: "x1" }, null); + const x1 = await newEntryExecution(1, eId, { text: "x1", done: true }, null); - expect(async () => newEntryExecution(1, eId, { text: "x2" }, x1 + 1)).rejects.toThrowError(new ErrorConflict("old execution id does not match")); + expect(async () => newEntryExecution(1, eId, { text: "x2", done: true }, x1 + 1)).rejects.toThrowError(new ErrorConflict("old execution id does not match")); }); test("get entries", async () => { @@ -246,8 +246,8 @@ test("get entries", async () => { const entriesPatient = await getEntries({ patient: 1 }, {}); expect(entriesPatient.items).length(2); expect(entriesPatient.total).toBe(2); - expect(entriesPatient.items[0].id).toBe(eId3); - expect(entriesPatient.items[1].id).toBe(eId1); + expect(entriesPatient.items[0].id).toBe(eId1); + expect(entriesPatient.items[1].id).toBe(eId3); // Filter by room const entriesRoom = await getEntries({ room: 1 }, {}); @@ -257,8 +257,8 @@ test("get entries", async () => { const entriesDone = await getEntries({ done: true }, {}); expect(entriesDone.items).length(2); expect(entriesDone.total).toBe(2); - expect(entriesDone.items[0].id).toBe(eId3); - expect(entriesDone.items[1].id).toBe(eId2); + expect(entriesDone.items[0].id).toBe(eId2); + expect(entriesDone.items[1].id).toBe(eId3); // Filter not done const entriesNotDone = await getEntries({ done: false }, {}); @@ -281,8 +281,8 @@ test("get entries", async () => { const entriesDateRange = await getEntries({ date: "2024-01-05..2024-01-06" }); expect(entriesDateRange.items).length(2); expect(entriesDateRange.total).toBe(2); - expect(entriesDateRange.items[0].id).toBe(eId3); - expect(entriesDateRange.items[1].id).toBe(eId2); + expect(entriesDateRange.items[0].id).toBe(eId2); + expect(entriesDateRange.items[1].id).toBe(eId3); // Search const entriesSearch = await getEntries({ search: "Blu" }, {}); @@ -293,6 +293,30 @@ test("get entries", async () => { // NTodo const n = await getNTodo(new Date("2024-01-05")); expect(n).toBe(1); + + // Sort by ID + const entriesSortedId = await getEntries({}, {}, ["id"]); + const entriesSortedIdDsc = await getEntries({}, {}, ["id:dsc"]); + expect(entriesSortedId.total).toBe(3); + expect(entriesSortedIdDsc.total).toBe(3); + for (let i = 0; i < 3; i++) { + expect(entriesSortedId.items[i]).toStrictEqual( + entriesSortedIdDsc.items[entriesSortedIdDsc.items.length - i - 1], + ); + } + + // Sort by patient and ID + const entriesSortedPatientId = await getEntries({}, {}, ["patient", "id"]); + expect(entriesSortedPatientId.items.length).toBe(3); + expect(entriesSortedPatientId.items[0].id).toBe(eId1); + expect(entriesSortedPatientId.items[1].id).toBe(eId3); + expect(entriesSortedPatientId.items[2].id).toBe(eId2); + + const entriesSortedPatientIdDsc = await getEntries({}, {}, ["patient:dsc", "id"]); + expect(entriesSortedPatientIdDsc.items.length).toBe(3); + expect(entriesSortedPatientIdDsc.items[0].id).toBe(eId2); + expect(entriesSortedPatientIdDsc.items[1].id).toBe(eId1); + expect(entriesSortedPatientIdDsc.items[2].id).toBe(eId3); }); test("get patient n entries", async () => { @@ -304,3 +328,18 @@ test("get category n entries", async () => { await insertTestEntries(); expect(await getCategoryNEntries(3)).toBe(1); }); + +test("todo execution", async () => { + const eId = await newEntry(1, { + patient_id: 1, + version: TEST_VERSION, + }); + + const n1 = await newEntryExecution(1, eId, { text: "note1", done: false }, null); + + const entry = await getEntry(eId); + expect(entry.execution?.id).toBe(n1); + expect(entry.execution?.text).toBe("note1"); + expect(entry.execution?.author.id).toBe(1); + expect(entry.execution?.done).toBe(false); +}); diff --git a/tests/integration/query/patient.ts b/tests/integration/query/patient.ts index e2633de..f039870 100644 --- a/tests/integration/query/patient.ts +++ b/tests/integration/query/patient.ts @@ -11,7 +11,7 @@ import { } from "$lib/server/query"; import { S1, S2 } from "$tests/helpers/testdata"; -const SORT_ID = { field: "id" }; +const SORT_ID = ["id"]; test("create patient", async () => { const pId = await newPatient({