Compare commits
3 commits
12dbf7227b
...
e68681129f
Author | SHA1 | Date | |
---|---|---|---|
e68681129f | |||
f0b401c216 | |||
97cdfe4d88 |
24 changed files with 1015 additions and 570 deletions
49
package.json
49
package.json
|
@ -17,49 +17,50 @@
|
|||
"test:e2e": "playwright test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@auth/core": "^0.18.6",
|
||||
"@auth/sveltekit": "^0.5.3",
|
||||
"@auth/core": "^0.27.0",
|
||||
"@auth/sveltekit": "^0.13.0",
|
||||
"@floating-ui/core": "^1.6.0",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@prisma/client": "^5.8.1",
|
||||
"@prisma/client": "^5.9.1",
|
||||
"isomorphic-dompurify": "^2.3.0",
|
||||
"marked": "^12.0.0",
|
||||
"svelte-floating-ui": "^1.5.8",
|
||||
"svelte-markdown": "^0.4.1",
|
||||
"zod": "^3.22.4",
|
||||
"zod-form-data": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.4.0",
|
||||
"@playwright/test": "^1.41.1",
|
||||
"@sveltejs/adapter-node": "^2.1.2",
|
||||
"@faker-js/faker": "^8.4.1",
|
||||
"@playwright/test": "^1.41.2",
|
||||
"@sveltejs/adapter-node": "^4.0.1",
|
||||
"@sveltejs/kit": "^2.5.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.2",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@trpc/client": "^10.45.0",
|
||||
"@trpc/server": "^10.45.0",
|
||||
"@types/node": "^20.11.7",
|
||||
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
||||
"@typescript-eslint/parser": "^6.19.1",
|
||||
"@trpc/client": "^10.45.1",
|
||||
"@trpc/server": "^10.45.1",
|
||||
"@types/node": "^20.11.19",
|
||||
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
||||
"@typescript-eslint/parser": "^7.0.1",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"daisyui": "^4.6.0",
|
||||
"daisyui": "^4.7.2",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-no-relative-import-paths": "^1.5.3",
|
||||
"eslint-plugin-svelte": "^2.35.1",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-import": "^16.0.1",
|
||||
"postcss-nesting": "^12.0.2",
|
||||
"prettier": "^3.2.4",
|
||||
"prettier-plugin-svelte": "^3.1.2",
|
||||
"prisma": "^5.8.1",
|
||||
"svelte": "^4.2.9",
|
||||
"svelte-check": "^3.6.3",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-svelte": "^3.2.1",
|
||||
"prisma": "^5.9.1",
|
||||
"svelte": "^4.2.11",
|
||||
"svelte-check": "^3.6.4",
|
||||
"sveltekit-superforms": "^1.13.4",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"trpc-sveltekit": "^3.5.22",
|
||||
"trpc-sveltekit": "^3.5.26",
|
||||
"tslib": "^2.6.2",
|
||||
"tsx": "^4.7.0",
|
||||
"tsx": "^4.7.1",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.0.12",
|
||||
"vitest": "^1.2.2"
|
||||
"vite": "^5.1.3",
|
||||
"vitest": "^1.3.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
|
1155
pnpm-lock.yaml
1155
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,18 +1,10 @@
|
|||
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 { sequence } from "@sveltejs/kit/hooks";
|
||||
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 { router } from "$lib/server/trpc/router";
|
||||
import { skAuthHandle } from "$lib/server/auth";
|
||||
|
||||
/**
|
||||
* Protect the application against unauthorized access.
|
||||
|
@ -34,40 +26,7 @@ const authorization: Handle = async ({ event, resolve }) => {
|
|||
};
|
||||
|
||||
export const handle = sequence(
|
||||
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,
|
||||
}),
|
||||
skAuthHandle,
|
||||
authorization,
|
||||
createTRPCHandle({ router, createContext })
|
||||
);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
export let inputCls = "w-full bg-transparent outline-none";
|
||||
export let asTextInput = false;
|
||||
export let idInputName: string | undefined = undefined;
|
||||
export let filterFn: (item: BaseItem) => boolean = () => true;
|
||||
|
||||
/** Selection callback. Returns the new input value after selection */
|
||||
export let onSelect: (item: SelectionOrText, kb: boolean) => OnSelectResult = (
|
||||
|
@ -122,9 +123,11 @@
|
|||
!selection && searchWord.length > 0
|
||||
? srcItems.filter(
|
||||
(it) =>
|
||||
!hiddenIds.has(it.id) && it.name.toLowerCase().includes(searchWord)
|
||||
!hiddenIds.has(it.id) &&
|
||||
filterFn(it) &&
|
||||
it.name.toLowerCase().includes(searchWord)
|
||||
)
|
||||
: srcItems.filter((it) => !hiddenIds.has(it.id));
|
||||
: srcItems.filter((it) => !hiddenIds.has(it.id) && filterFn(it));
|
||||
markSelection();
|
||||
}
|
||||
}
|
||||
|
@ -256,10 +259,12 @@
|
|||
bind:this={inputElm}
|
||||
on:input={onInput}
|
||||
on:click={open}
|
||||
on:focus={open}
|
||||
on:keydown={onKeyDown}
|
||||
on:keypress={onKeyPress}
|
||||
on:blur={onBlur}
|
||||
use:floatingRef
|
||||
value=""
|
||||
/>
|
||||
|
||||
{#if opened && filteredItems.length > 0}
|
||||
|
|
|
@ -81,15 +81,14 @@
|
|||
/>
|
||||
</FormField>
|
||||
|
||||
<label class="form-control w-full max-w-xs">
|
||||
<div class="label">Station</div>
|
||||
<FormField label="Station">
|
||||
<input
|
||||
type="text"
|
||||
class="input input-bordered w-full max-w-xs"
|
||||
disabled
|
||||
value={station?.name ?? ""}
|
||||
/>
|
||||
</label>
|
||||
</FormField>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-2">
|
||||
|
|
|
@ -84,9 +84,9 @@
|
|||
|
||||
<style lang="postcss">
|
||||
.done {
|
||||
@apply bg-green-500/20 !important;
|
||||
@apply bg-success/20 !important;
|
||||
}
|
||||
.priority {
|
||||
@apply bg-red-500/20;
|
||||
@apply bg-warning/20;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -73,6 +73,6 @@
|
|||
|
||||
<style lang="postcss">
|
||||
.p-hidden {
|
||||
@apply bg-red-500/20;
|
||||
@apply bg-error/20;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
<script lang="ts">
|
||||
export let label = "";
|
||||
export let errors: string[] | undefined = undefined;
|
||||
export let fullWidth = false;
|
||||
</script>
|
||||
|
||||
<label class="v-form-field form-control w-full max-w-xs">
|
||||
<div class="label">{label}</div>
|
||||
<label class="v-form-field form-control w-full" class:max-w-xs={!fullWidth}>
|
||||
<div class="label">
|
||||
<span class="label-text">{label}</span>
|
||||
<slot name="topLabel" />
|
||||
</div>
|
||||
<slot />
|
||||
{#if errors}
|
||||
<div class="label flex-col items-start">
|
||||
|
|
13
src/lib/components/ui/Markdown.svelte
Normal file
13
src/lib/components/ui/Markdown.svelte
Normal file
|
@ -0,0 +1,13 @@
|
|||
<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>
|
28
src/lib/components/ui/MarkdownInput.svelte
Normal file
28
src/lib/components/ui/MarkdownInput.svelte
Normal file
|
@ -0,0 +1,28 @@
|
|||
<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>
|
43
src/lib/server/auth.ts
Normal file
43
src/lib/server/auth.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
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,12 +55,21 @@ export async function getPatient(id: number): Promise<Patient> {
|
|||
|
||||
export async function getPatientNames(): Promise<PatientTag[]> {
|
||||
const patients = await prisma.patient.findMany({
|
||||
select: { id: true, first_name: true, last_name: true },
|
||||
select: {
|
||||
id: true,
|
||||
first_name: true,
|
||||
last_name: true,
|
||||
room_id: true,
|
||||
},
|
||||
where: { hidden: false },
|
||||
orderBy: { last_name: "asc" },
|
||||
});
|
||||
return patients.map((p) => {
|
||||
return { id: p.id, name: p.first_name + " " + p.last_name };
|
||||
return {
|
||||
id: p.id,
|
||||
name: p.first_name + " " + p.last_name,
|
||||
room_id: p.room_id,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
import type { Renderers } from "svelte-markdown";
|
||||
|
||||
export const PAGINATION_LIMIT = 20;
|
||||
export const MARKDOWN_RENDERERS: Partial<Renderers> = { image: undefined };
|
||||
|
||||
export const URL_ENTRIES = "/plan";
|
||||
export const URL_PATIENTS = "/patients";
|
||||
|
|
|
@ -75,6 +75,7 @@ export type Patient = {
|
|||
export type PatientTag = {
|
||||
id: number;
|
||||
name: string;
|
||||
room_id: number | null;
|
||||
};
|
||||
|
||||
export type PatientNew = {
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
<script lang="ts">
|
||||
import SvelteMarkdown from "svelte-markdown";
|
||||
import type { PageData } from "./$types";
|
||||
import { formatDate } from "$lib/shared/util";
|
||||
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";
|
||||
import Markdown from "$lib/components/ui/Markdown.svelte";
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
@ -34,10 +33,7 @@
|
|||
|
||||
<div class="box">
|
||||
<p class="prose">
|
||||
<SvelteMarkdown
|
||||
source={data.entry.current_version.text}
|
||||
renderers={MARKDOWN_RENDERERS}
|
||||
/>
|
||||
<Markdown src={data.entry.current_version.text} />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
@ -49,10 +45,7 @@
|
|||
</p>
|
||||
|
||||
<p class="prose">
|
||||
<SvelteMarkdown
|
||||
source={data.entry.execution?.text}
|
||||
renderers={MARKDOWN_RENDERERS}
|
||||
/>
|
||||
<Markdown src={data.entry.execution?.text} />
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
134
src/routes/(app)/entry/new/+page.svelte
Normal file
134
src/routes/(app)/entry/new/+page.svelte
Normal file
|
@ -0,0 +1,134 @@
|
|||
<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>
|
4
src/routes/(app)/logout/+page.server.ts
Normal file
4
src/routes/(app)/logout/+page.server.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import type { Actions } from "./$types";
|
||||
import { signOut } from "$lib/server/auth";
|
||||
|
||||
export const actions: Actions = { default: signOut };
|
|
@ -1,23 +1,11 @@
|
|||
<script lang="ts">
|
||||
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();
|
||||
}
|
||||
import { enhance } from "$app/forms";
|
||||
</script>
|
||||
|
||||
<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>
|
||||
<form action="/auth/signout" method="POST">
|
||||
<input type="hidden" name="csrfToken" value={data.csrfToken} />
|
||||
<input type="hidden" name="callbackUrl" value={callbackUrl} />
|
||||
<form method="POST" use:enhance>
|
||||
<input type="hidden" name="redirectTo" value="/login?noAuto=1" />
|
||||
<button class="btn btn-primary mt-4" type="submit">Abmelden</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
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,4 +11,6 @@
|
|||
<title>Planung</title>
|
||||
</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,9 +1,11 @@
|
|||
<script lang="ts">
|
||||
import { browser } from "$app/environment";
|
||||
import { page } from "$app/stores";
|
||||
|
||||
import ErrorMessage from "$lib/components/ui/ErrorMessage.svelte";
|
||||
|
||||
console.log("Fehlerhafte Seite", $page);
|
||||
// eslint-disable-next-line no-console
|
||||
if (browser) console.log("Fehlerhafte Seite", $page);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
|
4
src/routes/login/+page.server.ts
Normal file
4
src/routes/login/+page.server.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import type { Actions } from "./$types";
|
||||
import { signIn } from "$lib/server/auth";
|
||||
|
||||
export const actions: Actions = { default: signIn };
|
|
@ -1,11 +1,9 @@
|
|||
<script lang="ts">
|
||||
import type { PageData } from "./$types";
|
||||
import { page } from "$app/stores";
|
||||
import { onMount } from "svelte";
|
||||
import { signIn } from "@auth/sveltekit/client";
|
||||
import { goto } from "$app/navigation";
|
||||
|
||||
export let data: PageData;
|
||||
import { enhance } from "$app/forms";
|
||||
|
||||
let callbackUrl: string;
|
||||
$: if ($page.url) {
|
||||
|
@ -28,9 +26,9 @@
|
|||
|
||||
<div class="max-w-[100vw] px-6 pb-16 xl:pr-2 text-center">
|
||||
<h1 class="text-4xl mt-4">Visitenbuch</h1>
|
||||
<form action="/auth/signin/keycloak" method="POST">
|
||||
<input type="hidden" name="csrfToken" value={data.csrfToken} />
|
||||
<input type="hidden" name="callbackUrl" value={callbackUrl} />
|
||||
<form method="POST" use:enhance>
|
||||
<input type="hidden" name="providerId" value="keycloak" />
|
||||
<input type="hidden" name="redirectTo" value={callbackUrl} />
|
||||
<button class="btn btn-primary mt-4" type="submit">Anmelden</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
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