mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Moving to TS tailwindcss, move to Sonner toast.
This commit is contained in:
parent
4032838f49
commit
ad74bc0f85
17 changed files with 217 additions and 358 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
"$schema": "https://shadcn-svelte.com/schema.json",
|
"$schema": "https://shadcn-svelte.com/schema.json",
|
||||||
"style": "default",
|
"style": "default",
|
||||||
"tailwind": {
|
"tailwind": {
|
||||||
"config": "tailwind.config.js",
|
"config": "tailwind.config.ts",
|
||||||
"css": "src/lib/styles/app.pcss",
|
"css": "src/lib/styles/app.pcss",
|
||||||
"baseColor": "slate"
|
"baseColor": "slate"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
version: '3.8'
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
image: postgres:latest
|
image: postgres:latest
|
||||||
|
container_name: boredgame_postgres
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
|
|
@ -11,6 +13,7 @@ services:
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
redis:
|
redis:
|
||||||
image: redis:latest
|
image: redis:latest
|
||||||
|
container_name: boredgame_redis
|
||||||
ports:
|
ports:
|
||||||
- '6379:6379'
|
- '6379:6379'
|
||||||
volumes:
|
volumes:
|
||||||
|
|
@ -23,18 +26,6 @@ services:
|
||||||
# - '3592:3592'
|
# - '3592:3592'
|
||||||
# volumes:
|
# volumes:
|
||||||
# - ./policies:/policies
|
# - ./policies:/policies
|
||||||
# caddy:
|
|
||||||
# image: caddy:latest
|
|
||||||
# restart: unless-stopped
|
|
||||||
# ports:
|
|
||||||
# - "80:80"
|
|
||||||
# - "443:443"
|
|
||||||
# - "443:443/udp"
|
|
||||||
# volumes:
|
|
||||||
# - ./Caddyfile:/etc/caddy/Caddyfile
|
|
||||||
# - ./site:/srv
|
|
||||||
# - caddy_data:/data
|
|
||||||
# - caddy_config:/config
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
redis_data:
|
redis_data:
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,7 @@
|
||||||
"just-capitalize": "^3.2.0",
|
"just-capitalize": "^3.2.0",
|
||||||
"just-kebab-case": "^4.2.0",
|
"just-kebab-case": "^4.2.0",
|
||||||
"loader": "^2.1.1",
|
"loader": "^2.1.1",
|
||||||
|
"mode-watcher": "^0.4.1",
|
||||||
"open-props": "^1.7.6",
|
"open-props": "^1.7.6",
|
||||||
"oslo": "^1.2.1",
|
"oslo": "^1.2.1",
|
||||||
"pg": "^8.13.0",
|
"pg": "^8.13.0",
|
||||||
|
|
@ -124,8 +125,8 @@
|
||||||
"radix-svelte": "^0.9.0",
|
"radix-svelte": "^0.9.0",
|
||||||
"rate-limit-redis": "^4.2.0",
|
"rate-limit-redis": "^4.2.0",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"svelte-french-toast": "^1.2.0",
|
|
||||||
"svelte-lazy-loader": "^1.0.0",
|
"svelte-lazy-loader": "^1.0.0",
|
||||||
|
"svelte-sonner": "^0.3.28",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"tailwind-variants": "^0.2.1",
|
"tailwind-variants": "^0.2.1",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,9 @@ importers:
|
||||||
loader:
|
loader:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
mode-watcher:
|
||||||
|
specifier: ^0.4.1
|
||||||
|
version: 0.4.1(svelte@5.0.0-next.175)
|
||||||
open-props:
|
open-props:
|
||||||
specifier: ^1.7.6
|
specifier: ^1.7.6
|
||||||
version: 1.7.6
|
version: 1.7.6
|
||||||
|
|
@ -158,12 +161,12 @@ importers:
|
||||||
reflect-metadata:
|
reflect-metadata:
|
||||||
specifier: ^0.2.2
|
specifier: ^0.2.2
|
||||||
version: 0.2.2
|
version: 0.2.2
|
||||||
svelte-french-toast:
|
|
||||||
specifier: ^1.2.0
|
|
||||||
version: 1.2.0(svelte@5.0.0-next.175)
|
|
||||||
svelte-lazy-loader:
|
svelte-lazy-loader:
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
svelte-sonner:
|
||||||
|
specifier: ^0.3.28
|
||||||
|
version: 0.3.28(svelte@5.0.0-next.175)
|
||||||
tailwind-merge:
|
tailwind-merge:
|
||||||
specifier: ^2.5.2
|
specifier: ^2.5.2
|
||||||
version: 2.5.2
|
version: 2.5.2
|
||||||
|
|
@ -3454,6 +3457,11 @@ packages:
|
||||||
mlly@1.7.1:
|
mlly@1.7.1:
|
||||||
resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==}
|
resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==}
|
||||||
|
|
||||||
|
mode-watcher@0.4.1:
|
||||||
|
resolution: {integrity: sha512-bNC+1NXmwEFZtziCdZSgP7HFQTpqJPcQn9GwwJQGSf6SBF3neEPYV1uRwkYuAQwbsvsXIYtzaqgedDzJ7D1mhg==}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^4.0.0 || ^5.0.0-next.1
|
||||||
|
|
||||||
mri@1.2.0:
|
mri@1.2.0:
|
||||||
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
|
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
@ -4369,11 +4377,6 @@ packages:
|
||||||
svelte:
|
svelte:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
svelte-french-toast@1.2.0:
|
|
||||||
resolution: {integrity: sha512-5PW+6RFX3xQPbR44CngYAP1Sd9oCq9P2FOox4FZffzJuZI2mHOB7q5gJBVnOiLF5y3moVGZ7u2bYt7+yPAgcEQ==}
|
|
||||||
peerDependencies:
|
|
||||||
svelte: ^3.57.0 || ^4.0.0
|
|
||||||
|
|
||||||
svelte-headless-table@0.18.2:
|
svelte-headless-table@0.18.2:
|
||||||
resolution: {integrity: sha512-dnDTaXW5CNzRUjHVbc/Hb0Zv80zU4VcIUnAja6OuZriXvim1AqcWYQCHPRzBGwqj1m3YEHHNvspSzY0o5HzA0A==}
|
resolution: {integrity: sha512-dnDTaXW5CNzRUjHVbc/Hb0Zv80zU4VcIUnAja6OuZriXvim1AqcWYQCHPRzBGwqj1m3YEHHNvspSzY0o5HzA0A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -4480,16 +4483,16 @@ packages:
|
||||||
resolution: {integrity: sha512-DIFm0kSNscVxtBmKkBiygAHB5otoqN1aVmJ3t57jZhJfCB7Np/lUSoTtSrvPFjmlBbMeOsb1VQ06cut1+rBYOg==}
|
resolution: {integrity: sha512-DIFm0kSNscVxtBmKkBiygAHB5otoqN1aVmJ3t57jZhJfCB7Np/lUSoTtSrvPFjmlBbMeOsb1VQ06cut1+rBYOg==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
|
svelte-sonner@0.3.28:
|
||||||
|
resolution: {integrity: sha512-K3AmlySeFifF/cKgsYNv5uXqMVNln0NBAacOYgmkQStLa/UoU0LhfAACU6Gr+YYC8bOCHdVmFNoKuDbMEsppJg==}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.1
|
||||||
|
|
||||||
svelte-subscribe@2.0.1:
|
svelte-subscribe@2.0.1:
|
||||||
resolution: {integrity: sha512-eKXIjLxB4C7eQWPqKEdxcGfNXm2g/qJ67zmEZK/GigCZMfrTR3m7DPY93R6MX+5uoqM1FRYxl8LZ1oy4URWi2A==}
|
resolution: {integrity: sha512-eKXIjLxB4C7eQWPqKEdxcGfNXm2g/qJ67zmEZK/GigCZMfrTR3m7DPY93R6MX+5uoqM1FRYxl8LZ1oy4URWi2A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^4.0.0
|
svelte: ^4.0.0
|
||||||
|
|
||||||
svelte-writable-derived@3.1.1:
|
|
||||||
resolution: {integrity: sha512-w4LR6/bYZEuCs7SGr+M54oipk/UQKtiMadyOhW0PTwAtJ/Ai12QS77sLngEcfBx2q4H8ZBQucc9ktSA5sUGZWw==}
|
|
||||||
peerDependencies:
|
|
||||||
svelte: ^3.2.1 || ^4.0.0-next.1 || ^5.0.0-next.94
|
|
||||||
|
|
||||||
svelte@4.2.19:
|
svelte@4.2.19:
|
||||||
resolution: {integrity: sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==}
|
resolution: {integrity: sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
|
@ -7753,6 +7756,10 @@ snapshots:
|
||||||
pkg-types: 1.2.0
|
pkg-types: 1.2.0
|
||||||
ufo: 1.5.4
|
ufo: 1.5.4
|
||||||
|
|
||||||
|
mode-watcher@0.4.1(svelte@5.0.0-next.175):
|
||||||
|
dependencies:
|
||||||
|
svelte: 5.0.0-next.175
|
||||||
|
|
||||||
mri@1.2.0: {}
|
mri@1.2.0: {}
|
||||||
|
|
||||||
mrmime@2.0.0: {}
|
mrmime@2.0.0: {}
|
||||||
|
|
@ -8719,11 +8726,6 @@ snapshots:
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
svelte: 5.0.0-next.175
|
svelte: 5.0.0-next.175
|
||||||
|
|
||||||
svelte-french-toast@1.2.0(svelte@5.0.0-next.175):
|
|
||||||
dependencies:
|
|
||||||
svelte: 5.0.0-next.175
|
|
||||||
svelte-writable-derived: 3.1.1(svelte@5.0.0-next.175)
|
|
||||||
|
|
||||||
svelte-headless-table@0.18.2(svelte@5.0.0-next.175):
|
svelte-headless-table@0.18.2(svelte@5.0.0-next.175):
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.0.0-next.175
|
svelte: 5.0.0-next.175
|
||||||
|
|
@ -8781,11 +8783,11 @@ snapshots:
|
||||||
svelte: 4.2.19
|
svelte: 4.2.19
|
||||||
tslib: 2.7.0
|
tslib: 2.7.0
|
||||||
|
|
||||||
svelte-subscribe@2.0.1(svelte@5.0.0-next.175):
|
svelte-sonner@0.3.28(svelte@5.0.0-next.175):
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.0.0-next.175
|
svelte: 5.0.0-next.175
|
||||||
|
|
||||||
svelte-writable-derived@3.1.1(svelte@5.0.0-next.175):
|
svelte-subscribe@2.0.1(svelte@5.0.0-next.175):
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.0.0-next.175
|
svelte: 5.0.0-next.175
|
||||||
|
|
||||||
|
|
|
||||||
17
src/app.d.ts
vendored
17
src/app.d.ts
vendored
|
|
@ -21,17 +21,12 @@ declare global {
|
||||||
parseApiResponse: typeof parseApiResponse;
|
parseApiResponse: typeof parseApiResponse;
|
||||||
getAuthedUser: () => Promise<Returned<User> | null>;
|
getAuthedUser: () => Promise<Returned<User> | null>;
|
||||||
getAuthedUserOrThrow: () => Promise<Returned<User>>;
|
getAuthedUserOrThrow: () => Promise<Returned<User>>;
|
||||||
auth: import('lucia').AuthRequest;
|
}
|
||||||
user: import('lucia').User | null;
|
namespace Superforms {
|
||||||
session: import('lucia').Session | null;
|
type Message = {
|
||||||
startTimer: number;
|
type: 'error' | 'success' | 'info',
|
||||||
ip: string;
|
text: string
|
||||||
country: string;
|
}
|
||||||
error: string;
|
|
||||||
errorId: string;
|
|
||||||
errorStackTrace: string;
|
|
||||||
message: unknown;
|
|
||||||
track: unknown;
|
|
||||||
}
|
}
|
||||||
interface Error {
|
interface Error {
|
||||||
code?: string;
|
code?: string;
|
||||||
|
|
|
||||||
35
src/app.html
35
src/app.html
|
|
@ -6,40 +6,11 @@
|
||||||
<meta name="description" content="Bored? Find a game! Bored Game!" />
|
<meta name="description" content="Bored? Find a game! Bored Game!" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon-bored-game.svg" />
|
<link rel="icon" href="%sveltekit.assets%/favicon-bored-game.svg" />
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<script>
|
<noscript>Please enable JavaScript.</noscript>
|
||||||
// const htmlElement = document.documentElement;
|
|
||||||
// const userTheme = localStorage.theme;
|
|
||||||
// const userFont = localStorage.font;
|
|
||||||
|
|
||||||
// const prefersDarkMode = window.matchMedia('prefers-color-scheme: dark').matches;
|
|
||||||
// const prefersLightMode = window.matchMedia('prefers-color-scheme: light').matches;
|
|
||||||
|
|
||||||
// // check if the user set a theme
|
|
||||||
// if (userTheme) {
|
|
||||||
// htmlElement.dataset.theme = userTheme;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // otherwise check for user preference
|
|
||||||
// if (!userTheme && prefersDarkMode) {
|
|
||||||
// htmlElement.dataset.theme = '🌛 Night';
|
|
||||||
// localStorage.theme = '🌛 Night';
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (!userTheme && prefersLightMode) {
|
|
||||||
// htmlElement.dataset.theme = '☀️ Daylight';
|
|
||||||
// localStorage.theme = '☀️ Daylight';
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // if nothing is set default to dark mode
|
|
||||||
// if (!userTheme && !prefersDarkMode && !prefersLightMode) {
|
|
||||||
// htmlElement.dataset.theme = '🌛 Night';
|
|
||||||
// localStorage.theme = '🌛 Night';
|
|
||||||
// }
|
|
||||||
</script>
|
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body data-sveltekit-preload-data="hover">
|
||||||
<div id="svelte">%sveltekit.body%</div>
|
<div id="svelte" style="display: contents">%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { applyAction, enhance } from '$app/forms'
|
import { applyAction, enhance } from '$app/forms';
|
||||||
import { invalidateAll } from '$app/navigation'
|
import { invalidateAll } from '$app/navigation';
|
||||||
import Logo from '$components/logo.svelte'
|
import Logo from '$components/logo.svelte';
|
||||||
import * as Avatar from '$components/ui/avatar'
|
import * as Avatar from '$components/ui/avatar';
|
||||||
import * as DropdownMenu from '$components/ui/dropdown-menu'
|
import * as DropdownMenu from '$components/ui/dropdown-menu';
|
||||||
import { ListChecks, ListTodo, LogOut, Settings } from 'lucide-svelte'
|
import { ListChecks, ListTodo, LogOut, Settings } from 'lucide-svelte';
|
||||||
import toast from 'svelte-french-toast'
|
|
||||||
|
|
||||||
let { user = null } = $props()
|
let { user = null } = $props();
|
||||||
|
|
||||||
let avatar: string = $derived(user?.username?.slice(0, 1).toUpperCase() || ':)')
|
let avatar: string = $derived(user?.username?.slice(0, 1).toUpperCase() || ':)');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
|
|
@ -63,26 +62,7 @@ let avatar: string = $derived(user?.username?.slice(0, 1).toUpperCase() || ':)')
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
</a>
|
</a>
|
||||||
<DropdownMenu.Item>
|
<DropdownMenu.Item>
|
||||||
<form
|
<form action="/logout" method="POST">
|
||||||
use:enhance={() => {
|
|
||||||
return async ({ result }) => {
|
|
||||||
console.log(result);
|
|
||||||
if (result.type === 'success' || result.type === 'redirect') {
|
|
||||||
toast.success('Logged Out');
|
|
||||||
} else if (result.type === 'error') {
|
|
||||||
console.log(result);
|
|
||||||
toast.error(`Error: ${result.error.message}`);
|
|
||||||
} else {
|
|
||||||
toast.error(`Something went wrong.`);
|
|
||||||
console.log(result);
|
|
||||||
}
|
|
||||||
await invalidateAll();
|
|
||||||
await applyAction(result);
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
action="/logout"
|
|
||||||
method="POST"
|
|
||||||
>
|
|
||||||
<button type="submit">
|
<button type="submit">
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<LogOut class="mr-2 h-4 w-4" />
|
<LogOut class="mr-2 h-4 w-4" />
|
||||||
|
|
|
||||||
1
src/lib/components/ui/sonner/index.ts
Normal file
1
src/lib/components/ui/sonner/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { default as Toaster } from "./sonner.svelte";
|
||||||
20
src/lib/components/ui/sonner/sonner.svelte
Normal file
20
src/lib/components/ui/sonner/sonner.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { Toaster as Sonner, type ToasterProps as SonnerProps } from "svelte-sonner";
|
||||||
|
import { mode } from "mode-watcher";
|
||||||
|
|
||||||
|
type $$Props = SonnerProps;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Sonner
|
||||||
|
theme={$mode}
|
||||||
|
class="toaster group"
|
||||||
|
toastOptions={{
|
||||||
|
classes: {
|
||||||
|
toast: "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
|
||||||
|
description: "group-[.toast]:text-muted-foreground",
|
||||||
|
actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
|
||||||
|
cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
{...$$restProps}
|
||||||
|
/>
|
||||||
20
src/lib/utils/superforms.ts
Normal file
20
src/lib/utils/superforms.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { toast } from 'svelte-sonner'
|
||||||
|
import { message, type ErrorStatus, type SuperValidated } from 'sveltekit-superforms'
|
||||||
|
|
||||||
|
export type Message = {
|
||||||
|
type: 'error' | 'success' | 'info'
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function errorMessage(form: SuperValidated<any>, text: string | null, status: ErrorStatus = 500) {
|
||||||
|
return message(form, { text: text || 'Error', type: 'error' }, { status })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function successMessage(form: SuperValidated<any>, text: string | null) {
|
||||||
|
return message(form, { text: text || 'Success', type: 'success' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toastMessage(message: Message | undefined) {
|
||||||
|
if (!message) return
|
||||||
|
toast[message.type](message.text)
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages'
|
import { forbiddenMessage, notSignedInMessage } from '$lib/flashMessages'
|
||||||
import { user_roles } from '$lib/server/api/databases/tables'
|
import { user_roles } from '$lib/server/api/databases/tables'
|
||||||
import { db } from '$lib/server/api/packages/drizzle'
|
import { db } from '$lib/server/api/packages/drizzle'
|
||||||
|
import { errorMessage } from '$lib/utils/superforms'
|
||||||
import { eq } from 'drizzle-orm'
|
import { eq } from 'drizzle-orm'
|
||||||
import { loadFlash, redirect } from 'sveltekit-flash-message/server'
|
import { loadFlash, redirect } from 'sveltekit-flash-message/server'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { theme } from '$state/theme';
|
import { theme } from '$state/theme';
|
||||||
import toast, { Toaster } from 'svelte-french-toast';
|
import { toastMessage } from '$lib/utils/superforms.js';
|
||||||
|
|
||||||
const { data } = $props();
|
const { data } = $props();
|
||||||
const { user } = data;
|
const { user } = data;
|
||||||
|
|
@ -19,29 +19,13 @@
|
||||||
$theme = user?.theme || 'system';
|
$theme = user?.theme || 'system';
|
||||||
document.querySelector('html')?.setAttribute('data-theme', $theme);
|
document.querySelector('html')?.setAttribute('data-theme', $theme);
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if ($flash) {
|
|
||||||
if ($flash.type === 'success') {
|
|
||||||
toast.success($flash.message);
|
|
||||||
} else {
|
|
||||||
toast.error($flash.message, {
|
|
||||||
duration: 5000
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clearing the flash message could sometimes
|
|
||||||
// be required here to avoid double-toasting.
|
|
||||||
flash.set(undefined);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1>Do the admin stuff</h1>
|
<h1>Do the admin stuff</h1>
|
||||||
|
|
||||||
{@render children()}
|
{@render children()}
|
||||||
|
|
||||||
<Toaster />
|
<!-- <Toaster /> -->
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
:global(main) {
|
:global(main) {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ const { data, children } = $props()
|
||||||
<Header user={data.authedUser} />
|
<Header user={data.authedUser} />
|
||||||
|
|
||||||
<main
|
<main
|
||||||
class="flex min-h-[calc(100vh_-_theme(spacing.16))] flex-1 flex-col gap-4 p-4 md:gap-8 md:p-10"
|
class="flex min-h-[calc(100vh-theme(spacing.16))] flex-1 flex-col gap-4 p-4 md:gap-8 md:p-10"
|
||||||
>
|
>
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</main>
|
</main>
|
||||||
|
|
@ -19,27 +19,4 @@ const { data, children } = $props()
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
/*main {*/
|
|
||||||
/* flex: 1;*/
|
|
||||||
/* display: flex;*/
|
|
||||||
/* flex-direction: column;*/
|
|
||||||
/* max-width: 850px;*/
|
|
||||||
/* margin: 0 auto;*/
|
|
||||||
/* padding: 2rem 0rem;*/
|
|
||||||
/* max-width: 80vw;*/
|
|
||||||
|
|
||||||
/* @media (min-width: 1600px) {*/
|
|
||||||
/* max-width: 70vw;*/
|
|
||||||
/* }*/
|
|
||||||
|
|
||||||
/* box-sizing: border-box;*/
|
|
||||||
/*}*/
|
|
||||||
|
|
||||||
:global(.dialog-overlay) {
|
|
||||||
position: fixed;
|
|
||||||
inset: 0;
|
|
||||||
z-index: 100;
|
|
||||||
background-color: rgb(0 0 0);
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -12,21 +12,11 @@ export const load: PageServerLoad = async (event) => {
|
||||||
const authedUser = await locals.getAuthedUser()
|
const authedUser = await locals.getAuthedUser()
|
||||||
|
|
||||||
if (authedUser) {
|
if (authedUser) {
|
||||||
|
console.log('user already signed in')
|
||||||
const message = { type: 'success', message: 'You are already signed in' } as const
|
const message = { type: 'success', message: 'You are already signed in' } as const
|
||||||
throw redirect('/', message, event)
|
throw redirect('/', message, event)
|
||||||
|
// redirect(302, '/', message, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (userFullyAuthenticated(user, session)) {
|
|
||||||
// const message = { type: 'success', message: 'You are already signed in' } as const;
|
|
||||||
// throw redirect('/', message, event);
|
|
||||||
// } else if (userNotFullyAuthenticated(user, session)) {
|
|
||||||
// await lucia.invalidateSession(locals.session!.id!);
|
|
||||||
// const sessionCookie = lucia.createBlankSessionCookie();
|
|
||||||
// cookies.set(sessionCookie.name, sessionCookie.value, {
|
|
||||||
// path: '.',
|
|
||||||
// ...sessionCookie.attributes,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
const form = await superValidate(event, zod(signinUsernameDto))
|
const form = await superValidate(event, zod(signinUsernameDto))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -59,81 +49,6 @@ export const actions: Actions = {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// let session;
|
|
||||||
// let sessionCookie;
|
|
||||||
// const user: Users | undefined = await db.query.usersTable.findFirst({
|
|
||||||
// where: or(eq(usersTable.username, form.data.username), eq(usersTable.email, form.data.username)),
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// if (!user) {
|
|
||||||
// form.data.password = '';
|
|
||||||
// return setError(form, 'username', 'Your username or password is incorrect.');
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let twoFactorDetails;
|
|
||||||
//
|
|
||||||
try {
|
|
||||||
// const password = form.data.password;
|
|
||||||
// console.log('user', JSON.stringify(user, null, 2));
|
|
||||||
//
|
|
||||||
// if (!user?.hashed_password) {
|
|
||||||
// console.log('invalid username/password');
|
|
||||||
// form.data.password = '';
|
|
||||||
// return setError(form, 'password', 'Your username or password is incorrect.');
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// const validPassword = await new Argon2id().verify(user.hashed_password, password);
|
|
||||||
// if (!validPassword) {
|
|
||||||
// console.log('invalid password');
|
|
||||||
// form.data.password = '';
|
|
||||||
// return setError(form, 'password', 'Your username or password is incorrect.');
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// console.log('ip', locals.ip);
|
|
||||||
// console.log('country', locals.country);
|
|
||||||
//
|
|
||||||
// twoFactorDetails = await db.query.twoFactor.findFirst({
|
|
||||||
// where: eq(twoFactor.userId, user?.id),
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// if (twoFactorDetails?.secret && twoFactorDetails?.enabled) {
|
|
||||||
// await db.update(twoFactor).set({
|
|
||||||
// initiatedTime: new Date(),
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// session = await lucia.createSession(user.id, {
|
|
||||||
// ip_country: locals.country,
|
|
||||||
// ip_address: locals.ip,
|
|
||||||
// twoFactorAuthEnabled:
|
|
||||||
// twoFactorDetails?.enabled &&
|
|
||||||
// twoFactorDetails?.secret !== null &&
|
|
||||||
// twoFactorDetails?.secret !== '',
|
|
||||||
// isTwoFactorAuthenticated: false,
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// session = await lucia.createSession(user.id, {
|
|
||||||
// ip_country: locals.country,
|
|
||||||
// ip_address: locals.ip,
|
|
||||||
// twoFactorAuthEnabled: false,
|
|
||||||
// isTwoFactorAuthenticated: false,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// console.log('logging in session', session);
|
|
||||||
// sessionCookie = lucia.createSessionCookie(session.id);
|
|
||||||
// console.log('logging in session cookie', sessionCookie);
|
|
||||||
} catch (e) {
|
|
||||||
// TODO: need to return error message to the client
|
|
||||||
console.error(e)
|
|
||||||
form.data.password = ''
|
|
||||||
return setError(form, '', 'Your username or password is incorrect.')
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log('setting session cookie', sessionCookie);
|
|
||||||
// event.cookies.set(sessionCookie.name, sessionCookie.value, {
|
|
||||||
// path: '.',
|
|
||||||
// ...sessionCookie.attributes,
|
|
||||||
// });
|
|
||||||
|
|
||||||
form.data.username = ''
|
form.data.username = ''
|
||||||
form.data.password = ''
|
form.data.password = ''
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,24 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import '$lib/styles/app.pcss'
|
import '$lib/styles/app.pcss';
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte';
|
||||||
import toast, { Toaster } from 'svelte-french-toast'
|
import { MetaTags } from 'svelte-meta-tags';
|
||||||
import { MetaTags } from 'svelte-meta-tags'
|
import { getFlash } from 'sveltekit-flash-message/client';
|
||||||
import { getFlash } from 'sveltekit-flash-message/client'
|
import 'iconify-icon';
|
||||||
import 'iconify-icon'
|
import { ModeWatcher } from 'mode-watcher';
|
||||||
import { onNavigate } from '$app/navigation'
|
import { Toaster } from '$lib/components/ui/sonner';
|
||||||
import { page } from '$app/stores'
|
import { onNavigate } from '$app/navigation';
|
||||||
import Analytics from '$components/Analytics.svelte'
|
import { page } from '$app/stores';
|
||||||
import PageLoadingIndicator from '$lib/page_loading_indicator.svelte'
|
import Analytics from '$components/Analytics.svelte';
|
||||||
import { theme } from '$state/theme'
|
import PageLoadingIndicator from '$lib/page_loading_indicator.svelte';
|
||||||
|
import { theme } from '$state/theme';
|
||||||
|
import { toastMessage } from '$lib/utils/superforms.js';
|
||||||
|
|
||||||
const dev = process.env.NODE_ENV !== 'production'
|
const dev = process.env.NODE_ENV !== 'production';
|
||||||
|
|
||||||
const { data, children } = $props()
|
const { data, children } = $props();
|
||||||
const { user } = data
|
const { user } = data;
|
||||||
|
|
||||||
const metaTags = $derived({
|
const metaTags = $derived({
|
||||||
titleTemplate: '%s | Bored Game',
|
titleTemplate: '%s | Bored Game',
|
||||||
description: 'Bored Game, keep track of your gamesTable.',
|
description: 'Bored Game, keep track of your gamesTable.',
|
||||||
openGraph: {
|
openGraph: {
|
||||||
|
|
@ -26,46 +28,40 @@ const metaTags = $derived({
|
||||||
description: 'Bored Game, keep track of your gamesTable',
|
description: 'Bored Game, keep track of your gamesTable',
|
||||||
},
|
},
|
||||||
...$page.data.metaTagsChild,
|
...$page.data.metaTagsChild,
|
||||||
})
|
});
|
||||||
|
|
||||||
const flash = getFlash(page, {
|
const flash = getFlash(page, {
|
||||||
clearOnNavigate: true,
|
clearOnNavigate: true,
|
||||||
clearAfterMs: 3000,
|
clearAfterMs: 3000,
|
||||||
clearArray: true,
|
clearArray: true,
|
||||||
})
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
// set the theme to the user's active theme
|
// set the theme to the user's active theme
|
||||||
$theme = user?.theme || 'system'
|
$theme = user?.theme || 'system';
|
||||||
document.querySelector('html')?.setAttribute('data-theme', $theme)
|
document.querySelector('html')?.setAttribute('data-theme', $theme);
|
||||||
})
|
});
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
console.log('flash', $flash);
|
||||||
if ($flash) {
|
if ($flash) {
|
||||||
if ($flash.type === 'success') {
|
toastMessage({ type: $flash.type, text: $flash.message });
|
||||||
toast.success($flash.message)
|
|
||||||
} else {
|
|
||||||
toast.error($flash.message, {
|
|
||||||
duration: 5000,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clearing the flash message could sometimes
|
// Clearing the flash message could sometimes
|
||||||
// be required here to avoid double-toasting.
|
// be required here to avoid double-toasting.
|
||||||
flash.set(undefined)
|
flash.set(undefined);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
onNavigate(async (navigation) => {
|
onNavigate(async (navigation) => {
|
||||||
if (!document.startViewTransition) return
|
if (!document.startViewTransition) return;
|
||||||
|
|
||||||
return new Promise((oldStateCaptureResolve) => {
|
return new Promise((oldStateCaptureResolve) => {
|
||||||
document.startViewTransition(async () => {
|
document.startViewTransition(async () => {
|
||||||
oldStateCaptureResolve()
|
oldStateCaptureResolve();
|
||||||
await navigation.complete
|
await navigation.complete;
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if !dev}
|
{#if !dev}
|
||||||
|
|
@ -74,5 +70,6 @@ onNavigate(async (navigation) => {
|
||||||
|
|
||||||
<MetaTags {...metaTags} />
|
<MetaTags {...metaTags} />
|
||||||
<PageLoadingIndicator />
|
<PageLoadingIndicator />
|
||||||
{@render children()}
|
<!-- <ModeWatcher /> -->
|
||||||
<Toaster />
|
<Toaster />
|
||||||
|
{@render children()}
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
import { fontFamily } from "tailwindcss/defaultTheme";
|
|
||||||
import tailwindcssAnimate from "tailwindcss-animate";
|
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
|
||||||
const config = {
|
|
||||||
darkMode: ["class"],
|
|
||||||
content: ["./src/**/*.{html,js,svelte,ts}"],
|
|
||||||
theme: {
|
|
||||||
container: {
|
|
||||||
center: true,
|
|
||||||
padding: "2rem",
|
|
||||||
screens: {
|
|
||||||
"2xl": "1400px"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
extend: {
|
|
||||||
colors: {
|
|
||||||
border: "hsl(var(--border))",
|
|
||||||
input: "hsl(var(--input))",
|
|
||||||
ring: "hsl(var(--ring))",
|
|
||||||
background: "hsl(var(--background))",
|
|
||||||
foreground: "hsl(var(--foreground))",
|
|
||||||
primary: {
|
|
||||||
DEFAULT: "hsl(var(--primary))",
|
|
||||||
foreground: "hsl(var(--primary-foreground))"
|
|
||||||
},
|
|
||||||
secondary: {
|
|
||||||
DEFAULT: "hsl(var(--secondary))",
|
|
||||||
foreground: "hsl(var(--secondary-foreground))"
|
|
||||||
},
|
|
||||||
destructive: {
|
|
||||||
DEFAULT: "hsl(var(--destructive))",
|
|
||||||
foreground: "hsl(var(--destructive-foreground))"
|
|
||||||
},
|
|
||||||
muted: {
|
|
||||||
DEFAULT: "hsl(var(--muted))",
|
|
||||||
foreground: "hsl(var(--muted-foreground))"
|
|
||||||
},
|
|
||||||
accent: {
|
|
||||||
DEFAULT: "hsl(var(--accent))",
|
|
||||||
foreground: "hsl(var(--accent-foreground))"
|
|
||||||
},
|
|
||||||
popover: {
|
|
||||||
DEFAULT: "hsl(var(--popover))",
|
|
||||||
foreground: "hsl(var(--popover-foreground))"
|
|
||||||
},
|
|
||||||
card: {
|
|
||||||
DEFAULT: "hsl(var(--card))",
|
|
||||||
foreground: "hsl(var(--card-foreground))"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
borderRadius: {
|
|
||||||
lg: "var(--radius)",
|
|
||||||
md: "calc(var(--radius) - 2px)",
|
|
||||||
sm: "calc(var(--radius) - 4px)"
|
|
||||||
},
|
|
||||||
fontFamily: {
|
|
||||||
sans: [...fontFamily.sans]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: [tailwindcssAnimate]
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
||||||
69
tailwind.config.ts
Normal file
69
tailwind.config.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { fontFamily } from 'tailwindcss/defaultTheme'
|
||||||
|
import type { Config } from 'tailwindcss'
|
||||||
|
import tailwindcssAnimate from 'tailwindcss-animate'
|
||||||
|
|
||||||
|
const config: Config = {
|
||||||
|
darkMode: ['class'],
|
||||||
|
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||||
|
safelist: ['dark'],
|
||||||
|
theme: {
|
||||||
|
spacing: {
|
||||||
|
'16': '4rem',
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
center: true,
|
||||||
|
padding: '2rem',
|
||||||
|
screens: {
|
||||||
|
'2xl': '1400px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
border: 'hsl(var(--border))',
|
||||||
|
input: 'hsl(var(--input))',
|
||||||
|
ring: 'hsl(var(--ring))',
|
||||||
|
background: 'hsl(var(--background))',
|
||||||
|
foreground: 'hsl(var(--foreground))',
|
||||||
|
primary: {
|
||||||
|
DEFAULT: 'hsl(var(--primary))',
|
||||||
|
foreground: 'hsl(var(--primary-foreground))',
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
DEFAULT: 'hsl(var(--secondary))',
|
||||||
|
foreground: 'hsl(var(--secondary-foreground))',
|
||||||
|
},
|
||||||
|
destructive: {
|
||||||
|
DEFAULT: 'hsl(var(--destructive))',
|
||||||
|
foreground: 'hsl(var(--destructive-foreground))',
|
||||||
|
},
|
||||||
|
muted: {
|
||||||
|
DEFAULT: 'hsl(var(--muted))',
|
||||||
|
foreground: 'hsl(var(--muted-foreground))',
|
||||||
|
},
|
||||||
|
accent: {
|
||||||
|
DEFAULT: 'hsl(var(--accent))',
|
||||||
|
foreground: 'hsl(var(--accent-foreground))',
|
||||||
|
},
|
||||||
|
popover: {
|
||||||
|
DEFAULT: 'hsl(var(--popover))',
|
||||||
|
foreground: 'hsl(var(--popover-foreground))',
|
||||||
|
},
|
||||||
|
card: {
|
||||||
|
DEFAULT: 'hsl(var(--card))',
|
||||||
|
foreground: 'hsl(var(--card-foreground))',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
lg: 'var(--radius)',
|
||||||
|
md: 'calc(var(--radius) - 2px)',
|
||||||
|
sm: 'calc(var(--radius) - 4px)',
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: [...fontFamily.sans],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [tailwindcssAnimate],
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
||||||
Loading…
Reference in a new issue