Compare commits
No commits in common. "e68681129f87b521021de1b379680c847cc3c342" and "12dbf7227b564f1353de68ec2f9c0caa57003a89" have entirely different histories.
e68681129f
...
12dbf7227b
24 changed files with 570 additions and 1015 deletions
49
package.json
49
package.json
|
@ -17,50 +17,49 @@
|
||||||
"test:e2e": "playwright test"
|
"test:e2e": "playwright test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@auth/core": "^0.27.0",
|
"@auth/core": "^0.18.6",
|
||||||
"@auth/sveltekit": "^0.13.0",
|
"@auth/sveltekit": "^0.5.3",
|
||||||
"@floating-ui/core": "^1.6.0",
|
"@floating-ui/core": "^1.6.0",
|
||||||
"@mdi/js": "^7.4.47",
|
"@mdi/js": "^7.4.47",
|
||||||
"@prisma/client": "^5.9.1",
|
"@prisma/client": "^5.8.1",
|
||||||
"isomorphic-dompurify": "^2.3.0",
|
|
||||||
"marked": "^12.0.0",
|
|
||||||
"svelte-floating-ui": "^1.5.8",
|
"svelte-floating-ui": "^1.5.8",
|
||||||
|
"svelte-markdown": "^0.4.1",
|
||||||
"zod": "^3.22.4",
|
"zod": "^3.22.4",
|
||||||
"zod-form-data": "^2.0.2"
|
"zod-form-data": "^2.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@faker-js/faker": "^8.4.1",
|
"@faker-js/faker": "^8.4.0",
|
||||||
"@playwright/test": "^1.41.2",
|
"@playwright/test": "^1.41.1",
|
||||||
"@sveltejs/adapter-node": "^4.0.1",
|
"@sveltejs/adapter-node": "^2.1.2",
|
||||||
"@sveltejs/kit": "^2.5.0",
|
"@sveltejs/kit": "^2.5.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.0.2",
|
"@sveltejs/vite-plugin-svelte": "^3.0.1",
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"@trpc/client": "^10.45.1",
|
"@trpc/client": "^10.45.0",
|
||||||
"@trpc/server": "^10.45.1",
|
"@trpc/server": "^10.45.0",
|
||||||
"@types/node": "^20.11.19",
|
"@types/node": "^20.11.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
||||||
"@typescript-eslint/parser": "^7.0.1",
|
"@typescript-eslint/parser": "^6.19.1",
|
||||||
"autoprefixer": "^10.4.17",
|
"autoprefixer": "^10.4.17",
|
||||||
"daisyui": "^4.7.2",
|
"daisyui": "^4.6.0",
|
||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-no-relative-import-paths": "^1.5.3",
|
"eslint-plugin-no-relative-import-paths": "^1.5.3",
|
||||||
"eslint-plugin-svelte": "^2.35.1",
|
"eslint-plugin-svelte": "^2.35.1",
|
||||||
"postcss-import": "^16.0.1",
|
"postcss-import": "^15.1.0",
|
||||||
"postcss-nesting": "^12.0.2",
|
"postcss-nesting": "^12.0.2",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.4",
|
||||||
"prettier-plugin-svelte": "^3.2.1",
|
"prettier-plugin-svelte": "^3.1.2",
|
||||||
"prisma": "^5.9.1",
|
"prisma": "^5.8.1",
|
||||||
"svelte": "^4.2.11",
|
"svelte": "^4.2.9",
|
||||||
"svelte-check": "^3.6.4",
|
"svelte-check": "^3.6.3",
|
||||||
"sveltekit-superforms": "^1.13.4",
|
"sveltekit-superforms": "^1.13.4",
|
||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"trpc-sveltekit": "^3.5.26",
|
"trpc-sveltekit": "^3.5.22",
|
||||||
"tslib": "^2.6.2",
|
"tslib": "^2.6.2",
|
||||||
"tsx": "^4.7.1",
|
"tsx": "^4.7.0",
|
||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"vite": "^5.1.3",
|
"vite": "^5.0.12",
|
||||||
"vitest": "^1.3.0"
|
"vitest": "^1.2.2"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
|
1155
pnpm-lock.yaml
1155
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,18 @@
|
||||||
|
import { SvelteKitAuth } from "@auth/sveltekit";
|
||||||
|
import Keycloak from "@auth/core/providers/keycloak";
|
||||||
|
import {
|
||||||
|
KEYCLOAK_CLIENT_ID,
|
||||||
|
KEYCLOAK_CLIENT_SECRET,
|
||||||
|
KEYCLOAK_ISSUER,
|
||||||
|
} from "$env/static/private";
|
||||||
import { redirect, type Handle } from "@sveltejs/kit";
|
import { redirect, type Handle } from "@sveltejs/kit";
|
||||||
import { sequence } from "@sveltejs/kit/hooks";
|
import { sequence } from "@sveltejs/kit/hooks";
|
||||||
import { createTRPCHandle } from "trpc-sveltekit";
|
import { createTRPCHandle } from "trpc-sveltekit";
|
||||||
|
|
||||||
|
import { prisma } from "$lib/server/prisma";
|
||||||
|
import { PrismaAdapter } from "$lib/server/authAdapter";
|
||||||
import { createContext } from "$lib/server/trpc/context";
|
import { createContext } from "$lib/server/trpc/context";
|
||||||
import { router } from "$lib/server/trpc/router";
|
import { router } from "$lib/server/trpc/router";
|
||||||
import { skAuthHandle } from "$lib/server/auth";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Protect the application against unauthorized access.
|
* Protect the application against unauthorized access.
|
||||||
|
@ -26,7 +34,40 @@ const authorization: Handle = async ({ event, resolve }) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const handle = sequence(
|
export const handle = sequence(
|
||||||
skAuthHandle,
|
SvelteKitAuth({
|
||||||
|
adapter: PrismaAdapter(prisma),
|
||||||
|
providers: [
|
||||||
|
Keycloak({
|
||||||
|
clientId: KEYCLOAK_CLIENT_ID,
|
||||||
|
clientSecret: KEYCLOAK_CLIENT_SECRET,
|
||||||
|
issuer: KEYCLOAK_ISSUER,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
session: {
|
||||||
|
strategy: "jwt",
|
||||||
|
},
|
||||||
|
callbacks: {
|
||||||
|
// Add user ID to session token
|
||||||
|
jwt({ token, account, user }) {
|
||||||
|
if (account) {
|
||||||
|
token.accessToken = account.access_token;
|
||||||
|
token.id = user?.id;
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
},
|
||||||
|
session(opt) {
|
||||||
|
// @ts-expect-error because of union type
|
||||||
|
if (opt.session.user && opt.token.id) {
|
||||||
|
// @ts-expect-error because of union type
|
||||||
|
opt.session.user.id = opt.token.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return opt.session;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
trustHost: true,
|
||||||
|
}),
|
||||||
authorization,
|
authorization,
|
||||||
createTRPCHandle({ router, createContext })
|
createTRPCHandle({ router, createContext })
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
export let inputCls = "w-full bg-transparent outline-none";
|
export let inputCls = "w-full bg-transparent outline-none";
|
||||||
export let asTextInput = false;
|
export let asTextInput = false;
|
||||||
export let idInputName: string | undefined = undefined;
|
export let idInputName: string | undefined = undefined;
|
||||||
export let filterFn: (item: BaseItem) => boolean = () => true;
|
|
||||||
|
|
||||||
/** Selection callback. Returns the new input value after selection */
|
/** Selection callback. Returns the new input value after selection */
|
||||||
export let onSelect: (item: SelectionOrText, kb: boolean) => OnSelectResult = (
|
export let onSelect: (item: SelectionOrText, kb: boolean) => OnSelectResult = (
|
||||||
|
@ -123,11 +122,9 @@
|
||||||
!selection && searchWord.length > 0
|
!selection && searchWord.length > 0
|
||||||
? srcItems.filter(
|
? srcItems.filter(
|
||||||
(it) =>
|
(it) =>
|
||||||
!hiddenIds.has(it.id) &&
|
!hiddenIds.has(it.id) && it.name.toLowerCase().includes(searchWord)
|
||||||
filterFn(it) &&
|
|
||||||
it.name.toLowerCase().includes(searchWord)
|
|
||||||
)
|
)
|
||||||
: srcItems.filter((it) => !hiddenIds.has(it.id) && filterFn(it));
|
: srcItems.filter((it) => !hiddenIds.has(it.id));
|
||||||
markSelection();
|
markSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,12 +256,10 @@
|
||||||
bind:this={inputElm}
|
bind:this={inputElm}
|
||||||
on:input={onInput}
|
on:input={onInput}
|
||||||
on:click={open}
|
on:click={open}
|
||||||
on:focus={open}
|
|
||||||
on:keydown={onKeyDown}
|
on:keydown={onKeyDown}
|
||||||
on:keypress={onKeyPress}
|
on:keypress={onKeyPress}
|
||||||
on:blur={onBlur}
|
on:blur={onBlur}
|
||||||
use:floatingRef
|
use:floatingRef
|
||||||
value=""
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if opened && filteredItems.length > 0}
|
{#if opened && filteredItems.length > 0}
|
||||||
|
|
|
@ -81,14 +81,15 @@
|
||||||
/>
|
/>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|
||||||
<FormField label="Station">
|
<label class="form-control w-full max-w-xs">
|
||||||
|
<div class="label">Station</div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
class="input input-bordered w-full max-w-xs"
|
class="input input-bordered w-full max-w-xs"
|
||||||
disabled
|
disabled
|
||||||
value={station?.name ?? ""}
|
value={station?.name ?? ""}
|
||||||
/>
|
/>
|
||||||
</FormField>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
|
|
|
@ -84,9 +84,9 @@
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.done {
|
.done {
|
||||||
@apply bg-success/20 !important;
|
@apply bg-green-500/20 !important;
|
||||||
}
|
}
|
||||||
.priority {
|
.priority {
|
||||||
@apply bg-warning/20;
|
@apply bg-red-500/20;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -73,6 +73,6 @@
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.p-hidden {
|
.p-hidden {
|
||||||
@apply bg-error/20;
|
@apply bg-red-500/20;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let label = "";
|
export let label = "";
|
||||||
export let errors: string[] | undefined = undefined;
|
export let errors: string[] | undefined = undefined;
|
||||||
export let fullWidth = false;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<label class="v-form-field form-control w-full" class:max-w-xs={!fullWidth}>
|
<label class="v-form-field form-control w-full max-w-xs">
|
||||||
<div class="label">
|
<div class="label">{label}</div>
|
||||||
<span class="label-text">{label}</span>
|
|
||||||
<slot name="topLabel" />
|
|
||||||
</div>
|
|
||||||
<slot />
|
<slot />
|
||||||
{#if errors}
|
{#if errors}
|
||||||
<div class="label flex-col items-start">
|
<div class="label flex-col items-start">
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import DOMPurify from "isomorphic-dompurify";
|
|
||||||
import { marked } from "marked";
|
|
||||||
|
|
||||||
export let src: string;
|
|
||||||
|
|
||||||
$: doc = DOMPurify.sanitize(marked.parse(src) as string, { FORBID_TAGS: ["img"] });
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<p class="prose">
|
|
||||||
<!--eslint-disable-next-line svelte/no-at-html-tags-->
|
|
||||||
{@html doc}
|
|
||||||
</p>
|
|
|
@ -1,28 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import FormField from "./FormField.svelte";
|
|
||||||
import Markdown from "./Markdown.svelte";
|
|
||||||
import type { InputConstraint } from "sveltekit-superforms";
|
|
||||||
|
|
||||||
export let label = "";
|
|
||||||
export let errors: string[] | undefined = undefined;
|
|
||||||
export let constraints: InputConstraint | undefined = undefined;
|
|
||||||
|
|
||||||
let editMode = true;
|
|
||||||
let value = "";
|
|
||||||
|
|
||||||
function toggle() {
|
|
||||||
editMode = !editMode;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<FormField {label} {errors} fullWidth>
|
|
||||||
<button slot="topLabel" class="label-text" on:click={toggle} tabindex="-1">
|
|
||||||
{editMode ? "Vorschau" : "Bearbeiten"}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{#if editMode}
|
|
||||||
<textarea class="textarea textarea-bordered w-full" bind:value {...constraints} />
|
|
||||||
{:else}
|
|
||||||
<Markdown src={value} />
|
|
||||||
{/if}
|
|
||||||
</FormField>
|
|
|
@ -1,43 +0,0 @@
|
||||||
import { SvelteKitAuth } from "@auth/sveltekit";
|
|
||||||
import Keycloak from "@auth/core/providers/keycloak";
|
|
||||||
import { prisma } from "$lib/server/prisma";
|
|
||||||
import { PrismaAdapter } from "$lib/server/authAdapter";
|
|
||||||
import { env } from "$env/dynamic/private";
|
|
||||||
|
|
||||||
export const {
|
|
||||||
handle: skAuthHandle,
|
|
||||||
signIn,
|
|
||||||
signOut,
|
|
||||||
} = SvelteKitAuth({
|
|
||||||
adapter: PrismaAdapter(prisma),
|
|
||||||
providers: [
|
|
||||||
Keycloak({
|
|
||||||
clientId: env.KEYCLOAK_CLIENT_ID,
|
|
||||||
clientSecret: env.KEYCLOAK_CLIENT_SECRET,
|
|
||||||
issuer: env.KEYCLOAK_ISSUER,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
session: {
|
|
||||||
strategy: "jwt",
|
|
||||||
},
|
|
||||||
callbacks: {
|
|
||||||
// Add user ID to session token
|
|
||||||
jwt({ token, account, user }) {
|
|
||||||
if (account) {
|
|
||||||
token.accessToken = account.access_token;
|
|
||||||
token.id = user?.id;
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
},
|
|
||||||
session(opt) {
|
|
||||||
if (opt.session.user && opt.token.id) {
|
|
||||||
// @ts-expect-error because of union type
|
|
||||||
opt.session.user.id = opt.token.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return opt.session;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
trustHost: true,
|
|
||||||
});
|
|
|
@ -55,21 +55,12 @@ export async function getPatient(id: number): Promise<Patient> {
|
||||||
|
|
||||||
export async function getPatientNames(): Promise<PatientTag[]> {
|
export async function getPatientNames(): Promise<PatientTag[]> {
|
||||||
const patients = await prisma.patient.findMany({
|
const patients = await prisma.patient.findMany({
|
||||||
select: {
|
select: { id: true, first_name: true, last_name: true },
|
||||||
id: true,
|
|
||||||
first_name: true,
|
|
||||||
last_name: true,
|
|
||||||
room_id: true,
|
|
||||||
},
|
|
||||||
where: { hidden: false },
|
where: { hidden: false },
|
||||||
orderBy: { last_name: "asc" },
|
orderBy: { last_name: "asc" },
|
||||||
});
|
});
|
||||||
return patients.map((p) => {
|
return patients.map((p) => {
|
||||||
return {
|
return { id: p.id, name: p.first_name + " " + p.last_name };
|
||||||
id: p.id,
|
|
||||||
name: p.first_name + " " + p.last_name,
|
|
||||||
room_id: p.room_id,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
import type { Renderers } from "svelte-markdown";
|
||||||
|
|
||||||
export const PAGINATION_LIMIT = 20;
|
export const PAGINATION_LIMIT = 20;
|
||||||
|
export const MARKDOWN_RENDERERS: Partial<Renderers> = { image: undefined };
|
||||||
|
|
||||||
export const URL_ENTRIES = "/plan";
|
export const URL_ENTRIES = "/plan";
|
||||||
export const URL_PATIENTS = "/patients";
|
export const URL_PATIENTS = "/patients";
|
||||||
|
|
|
@ -75,7 +75,6 @@ export type Patient = {
|
||||||
export type PatientTag = {
|
export type PatientTag = {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
room_id: number | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PatientNew = {
|
export type PatientNew = {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import SvelteMarkdown from "svelte-markdown";
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
import { formatDate } from "$lib/shared/util";
|
import { formatDate } from "$lib/shared/util";
|
||||||
import UserField from "$lib/components/table/UserField.svelte";
|
import UserField from "$lib/components/table/UserField.svelte";
|
||||||
import RoomField from "$lib/components/table/RoomField.svelte";
|
import RoomField from "$lib/components/table/RoomField.svelte";
|
||||||
import CategoryField from "$lib/components/table/CategoryField.svelte";
|
import CategoryField from "$lib/components/table/CategoryField.svelte";
|
||||||
import Markdown from "$lib/components/ui/Markdown.svelte";
|
import { MARKDOWN_RENDERERS } from "$lib/shared/constants";
|
||||||
|
|
||||||
export let data: PageData;
|
export let data: PageData;
|
||||||
</script>
|
</script>
|
||||||
|
@ -33,7 +34,10 @@
|
||||||
|
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<p class="prose">
|
<p class="prose">
|
||||||
<Markdown src={data.entry.current_version.text} />
|
<SvelteMarkdown
|
||||||
|
source={data.entry.current_version.text}
|
||||||
|
renderers={MARKDOWN_RENDERERS}
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -45,7 +49,10 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="prose">
|
<p class="prose">
|
||||||
<Markdown src={data.entry.execution?.text} />
|
<SvelteMarkdown
|
||||||
|
source={data.entry.execution?.text}
|
||||||
|
renderers={MARKDOWN_RENDERERS}
|
||||||
|
/>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -1,134 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import Autocomplete from "$lib/components/filter/Autocomplete.svelte";
|
|
||||||
import FormField from "$lib/components/ui/FormField.svelte";
|
|
||||||
import { trpc, type RouterOutput } from "$lib/shared/trpc";
|
|
||||||
import MarkdownInput from "$lib/components/ui/MarkdownInput.svelte";
|
|
||||||
|
|
||||||
let room_id: number | null = null;
|
|
||||||
let patient_id: number | null = null;
|
|
||||||
let patient: RouterOutput["patient"]["get"] | null = null;
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:head>
|
|
||||||
<title>Neuer Eintrag</title>
|
|
||||||
</svelte:head>
|
|
||||||
|
|
||||||
<h1 class="heading">Neuer Eintrag</h1>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-2">
|
|
||||||
<FormField label="Zimmer">
|
|
||||||
<Autocomplete
|
|
||||||
inputCls="input input-bordered w-full max-w-xs"
|
|
||||||
items={async () => {
|
|
||||||
return await trpc().room.list.query();
|
|
||||||
}}
|
|
||||||
onSelect={(item) => {
|
|
||||||
// @ts-expect-error ids are always numeric
|
|
||||||
room_id = item.id;
|
|
||||||
// $form.room_id = item.id;
|
|
||||||
return { newValue: item.name ?? "", close: true };
|
|
||||||
}}
|
|
||||||
onUnselect={() => {
|
|
||||||
room_id = null;
|
|
||||||
// $form.room_id = null;
|
|
||||||
}}
|
|
||||||
asTextInput
|
|
||||||
idInputName="room_id"
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
|
|
||||||
<FormField label="Patient">
|
|
||||||
<Autocomplete
|
|
||||||
inputCls="input input-bordered w-full max-w-xs"
|
|
||||||
items={async () => {
|
|
||||||
return await trpc().patient.getNames.query();
|
|
||||||
}}
|
|
||||||
onSelect={(item) => {
|
|
||||||
// @ts-expect-error patient id
|
|
||||||
patient_id = item.id;
|
|
||||||
trpc()
|
|
||||||
// @ts-expect-error patient id
|
|
||||||
.patient.get.query(item.id)
|
|
||||||
.then((p) => {
|
|
||||||
patient = p;
|
|
||||||
});
|
|
||||||
// $form.room_id = item.id;
|
|
||||||
return { newValue: item.name ?? "", close: true };
|
|
||||||
}}
|
|
||||||
onUnselect={() => {
|
|
||||||
patient_id = null;
|
|
||||||
patient = null;
|
|
||||||
}}
|
|
||||||
asTextInput
|
|
||||||
idInputName="room_id"
|
|
||||||
filterFn={(itm) => {
|
|
||||||
// @ts-expect-error patient items have room attr
|
|
||||||
return room_id === null || itm.room_id === room_id;
|
|
||||||
}}
|
|
||||||
placeholder="Neuer Patient"
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-2">
|
|
||||||
<FormField label="Vorname">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="patient_first_name"
|
|
||||||
value={patient?.first_name ?? null}
|
|
||||||
disabled={patient_id !== null}
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
|
|
||||||
<FormField label="Nachname">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="patient_last_name"
|
|
||||||
value={patient?.last_name ?? null}
|
|
||||||
disabled={patient_id !== null}
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
|
|
||||||
<FormField label="Alter">
|
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
name="patient_age"
|
|
||||||
value={patient?.age ?? null}
|
|
||||||
disabled={patient_id !== null}
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-2">
|
|
||||||
<FormField label="Kategorie">
|
|
||||||
<Autocomplete
|
|
||||||
inputCls="input input-bordered w-full max-w-xs"
|
|
||||||
items={async () => {
|
|
||||||
return await trpc().category.list.query();
|
|
||||||
}}
|
|
||||||
onSelect={(item) => {
|
|
||||||
// @ts-expect-erro room items have station attr
|
|
||||||
// station = item.station;
|
|
||||||
// @ts-expect-erro ids are always numeric
|
|
||||||
// $form.room_id = item.id;
|
|
||||||
return { newValue: item.name ?? "", close: true };
|
|
||||||
}}
|
|
||||||
onUnselect={() => {
|
|
||||||
// $form.room_id = null;
|
|
||||||
}}
|
|
||||||
asTextInput
|
|
||||||
idInputName="category_id"
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
|
|
||||||
<div class="form-control w-full max-w-xs">
|
|
||||||
<label class="label cursor-pointer gap-2 justify-start">
|
|
||||||
<span class="label-text text-right">Priorität</span>
|
|
||||||
<input type="checkbox" class="checkbox checkbox-warning" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<MarkdownInput label="Beschreibung" />
|
|
||||||
|
|
||||||
<button class="btn btn-primary max-w-32 mt-4" type="submit">Speichern</button>
|
|
|
@ -1,4 +0,0 @@
|
||||||
import type { Actions } from "./$types";
|
|
||||||
import { signOut } from "$lib/server/auth";
|
|
||||||
|
|
||||||
export const actions: Actions = { default: signOut };
|
|
|
@ -1,11 +1,23 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { enhance } from "$app/forms";
|
import type { PageData } from "./$types";
|
||||||
|
import { page } from "$app/stores";
|
||||||
|
|
||||||
|
export let data: PageData;
|
||||||
|
|
||||||
|
let callbackUrl: string;
|
||||||
|
$: if ($page.url) {
|
||||||
|
let u = new URL($page.url.origin);
|
||||||
|
u.pathname = "/login";
|
||||||
|
u.searchParams.append("noAuto", "1");
|
||||||
|
callbackUrl = u.toString();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="max-w-[100vw] px-6 pb-16 xl:pr-2 text-center">
|
<div class="max-w-[100vw] px-6 pb-16 xl:pr-2 text-center">
|
||||||
<h1 class="text-xl mt-4">Möchten sie sich abmelden?</h1>
|
<h1 class="text-xl mt-4">Möchten sie sich abmelden?</h1>
|
||||||
<form method="POST" use:enhance>
|
<form action="/auth/signout" method="POST">
|
||||||
<input type="hidden" name="redirectTo" value="/login?noAuto=1" />
|
<input type="hidden" name="csrfToken" value={data.csrfToken} />
|
||||||
|
<input type="hidden" name="callbackUrl" value={callbackUrl} />
|
||||||
<button class="btn btn-primary mt-4" type="submit">Abmelden</button>
|
<button class="btn btn-primary mt-4" type="submit">Abmelden</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
8
src/routes/(app)/logout/+page.ts
Normal file
8
src/routes/(app)/logout/+page.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { authjsCsrfToken } from "$lib/shared/util";
|
||||||
|
import type { PageLoad } from "./$types";
|
||||||
|
|
||||||
|
export const load: PageLoad = async ({ fetch }) => {
|
||||||
|
const csrfToken = await authjsCsrfToken(fetch);
|
||||||
|
|
||||||
|
return { csrfToken };
|
||||||
|
};
|
|
@ -11,6 +11,4 @@
|
||||||
<title>Planung</title>
|
<title>Planung</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<FilteredEntryTable baseUrl={URL_ENTRIES} entries={data.entries} query={data.query}>
|
<FilteredEntryTable baseUrl={URL_ENTRIES} entries={data.entries} query={data.query} />
|
||||||
<a class="btn btn-sm btn-primary" href="/entry/new">Neuer Eintrag</a>
|
|
||||||
</FilteredEntryTable>
|
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { browser } from "$app/environment";
|
|
||||||
import { page } from "$app/stores";
|
import { page } from "$app/stores";
|
||||||
|
|
||||||
import ErrorMessage from "$lib/components/ui/ErrorMessage.svelte";
|
import ErrorMessage from "$lib/components/ui/ErrorMessage.svelte";
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
console.log("Fehlerhafte Seite", $page);
|
||||||
if (browser) console.log("Fehlerhafte Seite", $page);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
import type { Actions } from "./$types";
|
|
||||||
import { signIn } from "$lib/server/auth";
|
|
||||||
|
|
||||||
export const actions: Actions = { default: signIn };
|
|
|
@ -1,9 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { PageData } from "./$types";
|
||||||
import { page } from "$app/stores";
|
import { page } from "$app/stores";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { signIn } from "@auth/sveltekit/client";
|
import { signIn } from "@auth/sveltekit/client";
|
||||||
import { goto } from "$app/navigation";
|
import { goto } from "$app/navigation";
|
||||||
import { enhance } from "$app/forms";
|
|
||||||
|
export let data: PageData;
|
||||||
|
|
||||||
let callbackUrl: string;
|
let callbackUrl: string;
|
||||||
$: if ($page.url) {
|
$: if ($page.url) {
|
||||||
|
@ -26,9 +28,9 @@
|
||||||
|
|
||||||
<div class="max-w-[100vw] px-6 pb-16 xl:pr-2 text-center">
|
<div class="max-w-[100vw] px-6 pb-16 xl:pr-2 text-center">
|
||||||
<h1 class="text-4xl mt-4">Visitenbuch</h1>
|
<h1 class="text-4xl mt-4">Visitenbuch</h1>
|
||||||
<form method="POST" use:enhance>
|
<form action="/auth/signin/keycloak" method="POST">
|
||||||
<input type="hidden" name="providerId" value="keycloak" />
|
<input type="hidden" name="csrfToken" value={data.csrfToken} />
|
||||||
<input type="hidden" name="redirectTo" value={callbackUrl} />
|
<input type="hidden" name="callbackUrl" value={callbackUrl} />
|
||||||
<button class="btn btn-primary mt-4" type="submit">Anmelden</button>
|
<button class="btn btn-primary mt-4" type="submit">Anmelden</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
8
src/routes/login/+page.ts
Normal file
8
src/routes/login/+page.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { authjsCsrfToken } from "$lib/shared/util";
|
||||||
|
import type { PageLoad } from "./$types";
|
||||||
|
|
||||||
|
export const load: PageLoad = async ({ fetch }) => {
|
||||||
|
const csrfToken = await authjsCsrfToken(fetch);
|
||||||
|
|
||||||
|
return { csrfToken };
|
||||||
|
};
|
Loading…
Reference in a new issue