aboutsummaryrefslogtreecommitdiffstats
path: root/apps/kit/src/routes/(main)
diff options
context:
space:
mode:
Diffstat (limited to 'apps/kit/src/routes/(main)')
-rw-r--r--apps/kit/src/routes/(main)/(app)/+layout.svelte215
-rw-r--r--apps/kit/src/routes/(main)/(app)/home/+page.svelte1
-rw-r--r--apps/kit/src/routes/(main)/(public)/+layout.svelte1
-rw-r--r--apps/kit/src/routes/(main)/(public)/login/+page.svelte136
-rw-r--r--apps/kit/src/routes/(main)/(public)/reset/+page.svelte104
-rw-r--r--apps/kit/src/routes/(main)/(public)/signup/+page.svelte38
-rw-r--r--apps/kit/src/routes/(main)/+layout.server.ts13
-rw-r--r--apps/kit/src/routes/(main)/+layout.svelte41
-rw-r--r--apps/kit/src/routes/(main)/+layout.ts9
-rw-r--r--apps/kit/src/routes/(main)/+page.svelte2
10 files changed, 560 insertions, 0 deletions
diff --git a/apps/kit/src/routes/(main)/(app)/+layout.svelte b/apps/kit/src/routes/(main)/(app)/+layout.svelte
new file mode 100644
index 0000000..3f60af3
--- /dev/null
+++ b/apps/kit/src/routes/(main)/(app)/+layout.svelte
@@ -0,0 +1,215 @@
+<svelte:options immutable={true}/>
+<svelte:window bind:online={online}/>
+<script lang="ts">
+ import LL, {setLocale} from "$lib/i18n/i18n-svelte";
+ import {Dialog, TransitionChild, TransitionRoot} from '@rgossiaux/svelte-headlessui';
+ import {XIcon, MenuIcon, HomeIcon, DatabaseIcon, AdjustmentsIcon} from "$lib/components/icons";
+
+ let online = true;
+ let sidebarIsOpen = false;
+ const username = "dumb";
+
+ setLocale("nb");
+
+ const navigations = [
+ {
+ name: "Home",
+ icon: HomeIcon
+ },
+ {
+ name: "Data",
+ icon: DatabaseIcon
+ },
+ {
+ name: "Settings",
+ icon: AdjustmentsIcon
+ }
+ ]
+</script>
+{#if !online}
+ <div class="bg-yellow-50 border-l-4 border-yellow-400 p-4">
+ <div class="flex">
+ <div class="flex-shrink-0">
+ <svg class="h-5 w-5 text-yellow-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
+ fill="currentColor" aria-hidden="true">
+ <path fill-rule="evenodd"
+ d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
+ clip-rule="evenodd"/>
+ </svg>
+ </div>
+ <div class="ml-3">
+ <p class="text-sm text-yellow-700">
+ You seem to be offline, please check your internet connection.
+ </p>
+ </div>
+ </div>
+ </div>
+{/if}
+<div class="h-full flex">
+ <TransitionRoot show={sidebarIsOpen}>
+ <Dialog class="relative z-40 lg:hidden" on:close={() => sidebarIsOpen = !sidebarIsOpen}>
+ <TransitionChild
+ enter="transition-opacity ease-linear duration-300"
+ enterFrom="opacity-0"
+ enterTo="opacity-100"
+ leave="transition-opacity ease-linear duration-300"
+ leaveFrom="opacity-100"
+ leaveTo="opacity-0">
+ <div class="fixed inset-0 bg-gray-600 bg-opacity-75"></div>
+ </TransitionChild>
+
+ <div class="fixed inset-0 flex z-40">
+ <TransitionChild
+ enter="transition ease-in-out duration-300 transform"
+ enterFrom="-translate-x-full"
+ enterTo="translate-x-0"
+ leave="transition ease-in-out duration-300 transform"
+ leaveFrom="translate-x-0"
+ leaveTo="-translate-x-full">
+ <DialogPanel class="relative flex-1 flex flex-col max-w-xs w-full bg-white focus:outline-none">
+ <TransitionChild
+ enter="ease-in-out duration-300"
+ enterFrom="opacity-0"
+ enterTo="opacity-100"
+ leave="ease-in-out duration-300"
+ leaveFrom="opacity-100"
+ leaveTo="opacity-0">
+ <div class="absolute top-0 right-0 -mr-12 pt-2">
+ <button type="button"
+ class="ml-1 flex items-center justify-center h-10 w-10 rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
+ on:click={() => sidebarIsOpen = false}>
+ <span class="sr-only">Close sidebar</span>
+ <XIcon class="text-white" aria-hidden="true"/>
+ </button>
+ </div>
+ </TransitionChild>
+ <div class="flex-1 h-0 pt-5 pb-4 overflow-y-auto">
+ <div class="flex-shrink-0 flex items-center px-4">
+ <img class="h-8 w-auto"
+ src="https://tailwindui.com/img/logos/workflow-mark.svg?color=indigo&shade=600"
+ alt="Workflow"
+ />
+ </div>
+ <nav aria-label="Sidebar" class="mt-5">
+ <div class="px-2 space-y-1">
+ {#each navigations as item (item.name)}
+ <a href={item.href}
+ class="{item.current? 'bg-gray-100 text-gray-900': 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'} group flex items-center px-2 py-2 text-base font-medium rounded-md">
+ <svelte:component this="{item.icon}"
+ class="{item.current ? 'text-gray-500' : 'text-gray-400 group-hover:text-gray-500'} mr-4 h-6 w-6"
+ aria-hidden="true"></svelte:component>
+ {item.name}
+ </a>
+ {/each}
+ </div>
+ </nav>
+ </div>
+ <div class="flex-shrink-0 flex border-t border-gray-200 p-4">
+ <a href="#" class="flex-shrink-0 group block">
+ <div class="flex items-center">
+ <div>
+ <img class="inline-block h-10 w-10 rounded-full"
+ src="https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=256&h=256&q=80"
+ alt=""
+ />
+ </div>
+ <div class="ml-3">
+ <p class="text-base font-medium text-gray-700 group-hover:text-gray-900">
+ {username}
+ </p>
+ <p class="text-sm font-medium text-gray-500 group-hover:text-gray-700">
+ {$LL.nav.usermenu.profileTitle()}
+ </p>
+ </div>
+ </div>
+ </a>
+ </div>
+ </DialogPanel>
+ </TransitionChild>
+ <div class="flex-shrink-0 w-14" aria-hidden="true">
+ <!--{/* Force sidebar to shrink to fit close icon */}-->
+ </div>
+ </div>
+ </Dialog>
+ </TransitionRoot>
+
+ <!--{/* Static sidebar for desktop */}-->
+ <div class="hidden lg:flex lg:flex-shrink-0">
+ <div class="flex flex-col w-64">
+ <!--{/* Sidebar component, swap this element with another sidebar if you like */}-->
+ <div class="flex-1 flex flex-col min-h-0 border-r border-gray-200 bg-gray-100">
+ <div class="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
+ <div class="flex items-center flex-shrink-0 px-4">
+ <img class="h-8 w-auto"
+ src="https://tailwindui.com/img/logos/workflow-mark.svg?color=indigo&shade=600"
+ alt="Workflow"
+ />
+ </div>
+ <nav class="mt-5 flex-1" aria-label="Sidebar">
+ <div class="px-2 space-y-1">
+ {#each navigations as item (item.name)}
+ <a key={item.name}
+ href={item.href}
+ class="{item.current ? 'bg-gray-200 text-gray-900' : 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'} group flex items-center px-2 py-2 text-sm font-medium rounded-md">
+ <svelte:component this="{item.icon}"
+ class="{item.current ? 'text-gray-500' : 'text-gray-400 group-hover:text-gray-500'} mr-3 h-6 w-6"
+ aria-hidden="true"></svelte:component>
+ {item.name}
+ </a>
+ {/each}
+ </div>
+ </nav>
+ </div>
+ <div class="flex-shrink-0 flex border-t border-gray-200 p-4">
+ <a href="#" class="flex-shrink-0 w-full group block">
+ <div class="flex items-center">
+ <div>
+ <img class="inline-block h-9 w-9 rounded-full"
+ src="https://images.unsplash.com/photo-1517365830460-955ce3ccd263?ixlib=rb-=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=256&h=256&q=80"
+ alt=""
+ />
+ </div>
+ <div class="ml-3">
+ <p class="text-base font-medium text-gray-700 group-hover:text-gray-900">
+ {username}
+ </p>
+ <p class="text-sm font-medium text-gray-500 group-hover:text-gray-700">
+ {$LL.nav.usermenu.profileTitle()}
+ </p>
+ </div>
+ </div>
+ </a>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="flex flex-col min-w-0 flex-1 overflow-hidden">
+ <div class="lg:hidden">
+ <div class="flex items-center justify-between bg-gray-50 border-b border-gray-200 px-4 py-1.5">
+ <div>
+ <button type="button"
+ class="-mr-3 h-12 w-12 inline-flex items-center justify-center rounded-md text-gray-500 hover:text-gray-900"
+ on:click={() => sidebarIsOpen = true}>
+ <span class="sr-only">Open sidebar</span>
+ <MenuIcon aria-hidden="true"/>
+ </button>
+ </div>
+ </div>
+ </div>
+ <div class="flex-1 relative z-0 flex overflow-hidden">
+ <main class="flex-1 relative z-0 overflow-y-auto focus:outline-none">
+ <!--
+ MAIN CONTENT
+ -->
+ <slot/>
+ </main>
+ <aside class="hidden relative xl:flex xl:flex-col flex-shrink-0 w-96 border-l border-gray-200 overflow-y-auto">
+ <!--{/* Start secondary column (hidden on smaller screens) */}
+ <div class="absolute inset-0 py-6 px-4 sm:px-6 lg:px-8">
+ <div class="h-full border-2 border-gray-200 border-dashed rounded-lg" />
+ </div>
+ {/* End secondary column */}-->
+ </aside>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/apps/kit/src/routes/(main)/(app)/home/+page.svelte b/apps/kit/src/routes/(main)/(app)/home/+page.svelte
new file mode 100644
index 0000000..247ee47
--- /dev/null
+++ b/apps/kit/src/routes/(main)/(app)/home/+page.svelte
@@ -0,0 +1 @@
+<h1>Welcome Home</h1> \ No newline at end of file
diff --git a/apps/kit/src/routes/(main)/(public)/+layout.svelte b/apps/kit/src/routes/(main)/(public)/+layout.svelte
new file mode 100644
index 0000000..49aeb95
--- /dev/null
+++ b/apps/kit/src/routes/(main)/(public)/+layout.svelte
@@ -0,0 +1 @@
+<slot></slot> \ No newline at end of file
diff --git a/apps/kit/src/routes/(main)/(public)/login/+page.svelte b/apps/kit/src/routes/(main)/(public)/login/+page.svelte
new file mode 100644
index 0000000..9e2f6e7
--- /dev/null
+++ b/apps/kit/src/routes/(main)/(public)/login/+page.svelte
@@ -0,0 +1,136 @@
+<script lang="ts">
+ import { goto } from "$app/navigation";
+ import { login } from "$lib/api/user";
+ import LL from "$lib/i18n/i18n-svelte";
+ import type { ErrorResult } from "$lib/models/ErrorResult";
+ import type { LoginPayload } from "$lib/models/LoginPayload";
+
+ const data = {
+ username: "",
+ password: "",
+ } as LoginPayload;
+
+ let error = {
+ text: "",
+ title: "",
+ } as ErrorResult;
+
+ async function submitFormAsync() {
+ error = { text: "", title: "" };
+ const loginResponse = await login(data);
+ if (loginResponse.ok) {
+ await goto("/home");
+ } else {
+ error.title = loginResponse.data.title;
+ error.text = loginResponse.data.text;
+ }
+ }
+</script>
+
+<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
+ <div class="sm:mx-auto sm:w-full sm:max-w-md">
+ <h2
+ class="mt-6 text-center text-3xl tracking-tight font-bold text-gray-900"
+ >
+ {$LL.login.loginToYourAccount()}
+ </h2>
+ <p class="mt-2 text-center text-sm text-gray-600">
+ {$LL.common.or()}
+ <a
+ href="/signup"
+ class="font-medium text-indigo-600 hover:text-indigo-500"
+ >{$LL.login.createANewAccount()}</a
+ >
+ </p>
+ </div>
+ <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
+ <div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
+ {#if error.text || error.title}
+ <div class="rounded-md bg-red-50 p-3 mb-3">
+ {#if error.title}
+ <h3 class="text-sm font-medium text-red-800">
+ {error.title}
+ </h3>
+ {/if}
+ {#if error.text}
+ <div class="mt-2 text-sm text-red-700">
+ {error.text}
+ </div>
+ {/if}
+ </div>
+ {/if}
+ <form class="space-y-6" on:submit|preventDefault={submitFormAsync}>
+ <div>
+ <label
+ for="email"
+ class="block text-sm font-medium text-gray-700"
+ >{$LL.common.emailAddress()}</label
+ >
+ <div class="mt-1">
+ <input
+ id="email"
+ name="email"
+ type="email"
+ autocomplete="email"
+ required
+ value={data.username}
+ class="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
+ />
+ </div>
+ </div>
+
+ <div>
+ <label
+ for="password"
+ class="block text-sm font-medium text-gray-700"
+ >{$LL.login.password()}</label
+ >
+ <div class="mt-1">
+ <input
+ id="password"
+ name="password"
+ type="password"
+ autocomplete="current-password"
+ required
+ value={data.password}
+ class="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
+ />
+ </div>
+ </div>
+
+ <div class="flex items-center justify-between">
+ <div class="flex items-center">
+ <input
+ id="remember-me"
+ name="remember-me"
+ type="checkbox"
+ class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"
+ />
+ <label
+ for="remember-me"
+ class="ml-2 block text-sm text-gray-900"
+ >{$LL.login.notMyComputer()}</label
+ >
+ </div>
+
+ <div class="text-sm">
+ <a
+ href="/reset"
+ class="font-medium text-indigo-600 hover:text-indigo-500"
+ >{$LL.login.forgotPassword()}</a
+ >
+ </div>
+ </div>
+
+ <div>
+ <button
+ type="submit"
+ class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
+ >
+ {$LL.common.logIn()}
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+</div>
diff --git a/apps/kit/src/routes/(main)/(public)/reset/+page.svelte b/apps/kit/src/routes/(main)/(public)/reset/+page.svelte
new file mode 100644
index 0000000..5092b4b
--- /dev/null
+++ b/apps/kit/src/routes/(main)/(public)/reset/+page.svelte
@@ -0,0 +1,104 @@
+<script lang="ts">
+ import { create_forgot_password_request } from "$lib/api/user";
+ import Alert from "$lib/components/alert.svelte";
+ import LL from "$lib/i18n/i18n-svelte";
+ import type { ErrorResult } from "$lib/models/ErrorResult";
+ import { get } from "svelte/store";
+
+ const formData = {
+ email: "",
+ };
+
+ $: showErrorAlert =
+ (errorData?.text.length ?? 0 + errorData?.title.length ?? 0) > 0 &&
+ !showSuccessAlert;
+
+ const errorData = {
+ text: "",
+ title: "",
+ } as ErrorResult;
+
+ let showSuccessAlert = false;
+
+ async function submit() {
+ errorData.text = "";
+ errorData.title = "";
+ showSuccessAlert = false;
+ const request = await create_forgot_password_request(formData.email);
+ if (!request.ok) {
+ errorData.text = request.data.text ?? $LL.common.tryAgainSoon();
+ errorData.title = request.data.title ?? $LL.common.unexpectedError();
+ return;
+ }
+ showSuccessAlert = true;
+ }
+</script>
+
+<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
+ <div class="sm:mx-auto sm:w-full sm:max-w-md">
+ <h2
+ class="mt-6 text-center text-3xl tracking-tight font-bold text-gray-900"
+ >
+ {$LL.reset.resetPassword()}
+ </h2>
+ <p class="mt-2 text-center text-sm text-gray-600">
+ {$LL.common.or()}
+ <a
+ href="/login"
+ class="font-medium text-indigo-600 hover:text-indigo-500"
+ >{$LL.reset.gotoLoginPage().toLowerCase()}</a
+ >
+ </p>
+ </div>
+
+ <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
+ <div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
+ <form
+ class="space-y-6"
+ on:submit|preventDefault={submit}
+ method="POST"
+ >
+ <Alert
+ title={errorData.title}
+ message={errorData.text}
+ type="error"
+ visible={showErrorAlert}
+ />
+
+ <Alert
+ type="success"
+ title={$LL.common.success()}
+ message={$LL.reset.requestSentMessage()}
+ visible={showSuccessAlert}
+ />
+ <div>
+ <label
+ for="email"
+ class="block text-sm font-medium text-gray-700"
+ >
+ {$LL.common.emailAddress()}</label
+ >
+ <div class="mt-1">
+ <input
+ id="email"
+ name="email"
+ type="email"
+ autocomplete="email"
+ required
+ bind:value={formData.email}
+ class="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
+ />
+ </div>
+ </div>
+ <div>
+ <button
+ type="submit"
+ class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
+ >
+ {$LL.common.send($LL.common.request().toLowerCase())}
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+</div>
diff --git a/apps/kit/src/routes/(main)/(public)/signup/+page.svelte b/apps/kit/src/routes/(main)/(public)/signup/+page.svelte
new file mode 100644
index 0000000..d4a1bda
--- /dev/null
+++ b/apps/kit/src/routes/(main)/(public)/signup/+page.svelte
@@ -0,0 +1,38 @@
+<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
+ <div class="sm:mx-auto sm:w-full sm:max-w-md">
+ <h2 class="mt-6 text-center text-3xl tracking-tight font-bold text-gray-900">Create your new account</h2>
+ <p class="mt-2 text-center text-sm text-gray-600">
+ Or
+ <a href="/login" class="font-medium text-indigo-600 hover:text-indigo-500">go to login page</a>
+ </p>
+ </div>
+
+ <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
+ <div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
+ <form class="space-y-6" action="#" method="POST">
+ <div>
+ <label for="email" class="block text-sm font-medium text-gray-700"> Email address </label>
+ <div class="mt-1">
+ <input id="email" name="email" type="email" autocomplete="email" required
+ class="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
+ </div>
+ </div>
+
+ <div>
+ <label for="password" class="block text-sm font-medium text-gray-700"> Password </label>
+ <div class="mt-1">
+ <input id="password" name="password" type="password" autocomplete="current-password" required
+ class="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
+ </div>
+ </div>
+
+ <div>
+ <button type="submit"
+ class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
+ Create account
+ </button>
+ </div>
+ </form>
+ </div>
+ </div>
+</div>
diff --git a/apps/kit/src/routes/(main)/+layout.server.ts b/apps/kit/src/routes/(main)/+layout.server.ts
new file mode 100644
index 0000000..01aae89
--- /dev/null
+++ b/apps/kit/src/routes/(main)/+layout.server.ts
@@ -0,0 +1,13 @@
+// import {is_active} from "$lib/session";
+// import {redirect} from "@sveltejs/kit";
+// import type {LayoutServerLoad} from "./$types";
+//
+// export const load: LayoutServerLoad = async ({routeId}) => {
+// const sessionIsValid = await is_active();
+// const isPublicRoute = routeId?.startsWith("(public)");
+// if (sessionIsValid && isPublicRoute) {
+// throw redirect(302, "/home");
+// } else if (!sessionIsValid && !isPublicRoute) {
+// throw redirect(302, "/login");
+// }
+// }; \ No newline at end of file
diff --git a/apps/kit/src/routes/(main)/+layout.svelte b/apps/kit/src/routes/(main)/+layout.svelte
new file mode 100644
index 0000000..e5b177e
--- /dev/null
+++ b/apps/kit/src/routes/(main)/+layout.svelte
@@ -0,0 +1,41 @@
+<script lang="ts">
+ import "../../app.pcss";
+ import { afterNavigate, beforeNavigate, goto } from "$app/navigation";
+ import { is_active } from "$lib/session";
+ import type { Navigation } from "@sveltejs/kit";
+ import { setLocale } from "$lib/i18n/i18n-svelte";
+ import { onMount } from "svelte";
+ import type { LayoutData } from "./$types";
+ import LocaleSwitcher from "$lib/components/locale-switcher.svelte";
+
+ export let data: LayoutData;
+ onMount(() => setLocale(data.locale));
+
+ async function redirect_if_necessary(ticket: Navigation) {
+ const sessionIsValid = await is_active();
+ // TODO: ticket.to can be empty while navigating, so coalesce could probably cause non-public routes to cause a redir to /login...
+ const isPublicRoute =
+ (ticket.to?.routeId?.startsWith("(public)") ?? true) ||
+ ticket.to?.routeId?.startsWith("book");
+
+ console.log("redir: ", {
+ isPublicRoute,
+ sessionIsValid,
+ });
+
+ if (sessionIsValid && isPublicRoute) {
+ await goto("/home");
+ } else if (!sessionIsValid && !isPublicRoute) {
+ await goto("/login");
+ }
+ }
+
+ // This should probably be removed in favor of the logic in layout.server.ts.
+ // That requires a more sophisticated server side implementation of session handling,
+ // and i don't want that tbh, i want to stay as much in the browser as possible.
+ afterNavigate(redirect_if_necessary);
+ beforeNavigate(redirect_if_necessary);
+</script>
+
+<LocaleSwitcher />
+<slot />
diff --git a/apps/kit/src/routes/(main)/+layout.ts b/apps/kit/src/routes/(main)/+layout.ts
new file mode 100644
index 0000000..de8a5c0
--- /dev/null
+++ b/apps/kit/src/routes/(main)/+layout.ts
@@ -0,0 +1,9 @@
+import type {Locales} from "$lib/i18n/i18n-types";
+import {loadLocaleAsync} from "$lib/i18n/i18n-util.async";
+import type {LayoutLoad} from "./$types";
+
+export const load: LayoutLoad<{ locale: Locales }> = async ({url, params}) => {
+ let lang = "en" as Locales;
+ await loadLocaleAsync(lang);
+ return {locale: lang};
+}; \ No newline at end of file
diff --git a/apps/kit/src/routes/(main)/+page.svelte b/apps/kit/src/routes/(main)/+page.svelte
new file mode 100644
index 0000000..85a4d2d
--- /dev/null
+++ b/apps/kit/src/routes/(main)/+page.svelte
@@ -0,0 +1,2 @@
+
+<p class="text-bold p-1">Hold on...</p>