mirror of
https://github.com/jakobkordez/ham-reserve.git
synced 2025-05-16 16:50:29 +00:00
Auth state rework
This commit is contained in:
parent
5b9bce4796
commit
dde9f5d4fb
@ -14,30 +14,30 @@
|
|||||||
"format": "prettier --write ."
|
"format": "prettier --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
"@fortawesome/free-solid-svg-icons": "^6.7.1",
|
||||||
"@sveltejs/adapter-auto": "^3.3.1",
|
"@sveltejs/adapter-auto": "^3.3.1",
|
||||||
"@sveltejs/kit": "^2.7.3",
|
"@sveltejs/kit": "^2.9.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
"@sveltejs/vite-plugin-svelte": "^4.0.2",
|
||||||
"@types/async-lock": "^1.4.2",
|
"@types/async-lock": "^1.4.2",
|
||||||
"@types/eslint": "^9.6.1",
|
"@types/eslint": "^9.6.1",
|
||||||
"async-lock": "^1.4.1",
|
"async-lock": "^1.4.1",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.9",
|
||||||
"daisyui": "^4.12.13",
|
"daisyui": "^4.12.20",
|
||||||
"eslint": "^9.13.0",
|
"eslint": "^9.16.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-svelte": "^2.46.0",
|
"eslint-plugin-svelte": "^2.46.1",
|
||||||
"globals": "^15.11.0",
|
"globals": "^15.13.0",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.4.2",
|
||||||
"prettier-plugin-svelte": "^3.2.7",
|
"prettier-plugin-svelte": "^3.3.2",
|
||||||
"svelte": "^5.1.2",
|
"svelte": "^5.10.0",
|
||||||
"svelte-check": "^4.0.5",
|
"svelte-check": "^4.1.1",
|
||||||
"svelte-fa": "^4.0.3",
|
"svelte-fa": "^4.0.3",
|
||||||
"tailwindcss": "^3.4.14",
|
"tailwindcss": "^3.4.16",
|
||||||
"typescript": "^5.6.3",
|
"typescript": "^5.7.2",
|
||||||
"typescript-eslint": "^8.11.0",
|
"typescript-eslint": "^8.18.0",
|
||||||
"vite": "^5.4.10",
|
"vite": "^5.4.11",
|
||||||
"vitest": "^2.1.3"
|
"vitest": "^2.1.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1072
svelte-app/pnpm-lock.yaml
generated
1072
svelte-app/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,13 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Role } from '$lib/enums/role.enum';
|
import { Role } from '$lib/enums/role.enum';
|
||||||
import { logout } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import { userStore } from '$lib/stores/user-store';
|
|
||||||
import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
|
import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
|
||||||
import Fa from 'svelte-fa';
|
import Fa from 'svelte-fa';
|
||||||
import Clock from './clock.svelte';
|
import Clock from './clock.svelte';
|
||||||
|
|
||||||
let userDropOpen = $state(false);
|
let userDropOpen = $state(false);
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -20,11 +21,11 @@
|
|||||||
<!-- <ThemeToggle /> -->
|
<!-- <ThemeToggle /> -->
|
||||||
|
|
||||||
<!-- User -->
|
<!-- User -->
|
||||||
{#if $userStore}
|
{#if auth.user}
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<button class="btn btn-ghost" onclick={() => (userDropOpen = !userDropOpen)}>
|
<button class="btn btn-ghost" onclick={() => (userDropOpen = !userDropOpen)}>
|
||||||
<Fa icon={faUserCircle} class="h-5 w-5" />
|
<Fa icon={faUserCircle} class="h-5 w-5" />
|
||||||
<span>{$userStore.username}</span>
|
<span>{auth.user.username}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="absolute right-2 top-full z-[1] pt-1 {userDropOpen ? '' : 'hidden'}">
|
<div class="absolute right-2 top-full z-[1] pt-1 {userDropOpen ? '' : 'hidden'}">
|
||||||
@ -40,7 +41,7 @@
|
|||||||
<li>
|
<li>
|
||||||
<a href="/profile" onclick={() => (userDropOpen = false)}>Profil</a>
|
<a href="/profile" onclick={() => (userDropOpen = false)}>Profil</a>
|
||||||
</li>
|
</li>
|
||||||
{#if $userStore.roles.includes(Role.Admin)}
|
{#if auth.user.roles.includes(Role.Admin)}
|
||||||
<li>
|
<li>
|
||||||
<a href="/admin" class="btn-warning" onclick={() => (userDropOpen = false)}>
|
<a href="/admin" class="btn-warning" onclick={() => (userDropOpen = false)}>
|
||||||
Admin panel
|
Admin panel
|
||||||
@ -50,8 +51,8 @@
|
|||||||
<li>
|
<li>
|
||||||
<button
|
<button
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
logout();
|
auth.logout();
|
||||||
window.location.reload();
|
userDropOpen = false;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Odjava
|
Odjava
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Fa } from 'svelte-fa';
|
import { Fa } from 'svelte-fa';
|
||||||
import { faWarning } from '@fortawesome/free-solid-svg-icons';
|
import { faWarning } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { userStore } from '$lib/stores/user-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
|
|
||||||
const { class: className = '' }: { class?: string } = $props();
|
const { class: className = '' }: { class?: string } = $props();
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $userStore?.passwordResetRequired === true}
|
{#if auth.user?.passwordResetRequired === true}
|
||||||
<div class="alert alert-warning {className}">
|
<div class="alert alert-warning {className}">
|
||||||
<Fa icon={faWarning} class="shrink-0 h-6 w-6" />
|
<Fa icon={faWarning} class="shrink-0 h-6 w-6" />
|
||||||
<span>Opozorilo: Prosim, zamenjaj geslo!</span>
|
<span>Opozorilo: Prosim, zamenjaj geslo!</span>
|
||||||
|
111
svelte-app/src/lib/stores/auth-state.svelte.ts
Normal file
111
svelte-app/src/lib/stores/auth-state.svelte.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { apiFunctions } from '$lib/api';
|
||||||
|
import AsyncLock from 'async-lock';
|
||||||
|
import { isAxiosError } from 'axios';
|
||||||
|
import { getContext, setContext, tick } from 'svelte';
|
||||||
|
import { jwtDecode } from 'jwt-decode';
|
||||||
|
import { browser } from '$app/environment';
|
||||||
|
import { type User } from '$lib/interfaces/user.interface';
|
||||||
|
|
||||||
|
type AuthState = {
|
||||||
|
accessToken: string | null;
|
||||||
|
refreshToken: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function createAuthState() {
|
||||||
|
const lock = new AsyncLock();
|
||||||
|
let authState = $state<AuthState>(fetchStorage());
|
||||||
|
let user = $state<User | null>();
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (!browser) return;
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(authState));
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshUser();
|
||||||
|
|
||||||
|
function login(username: string, password: string) {
|
||||||
|
return apiFunctions
|
||||||
|
.login(username, password)
|
||||||
|
.then((res) => {
|
||||||
|
authState = res;
|
||||||
|
tick().then(refreshUser);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshUser() {
|
||||||
|
console.log('Refreshing user');
|
||||||
|
return getAccessToken().then((token) => {
|
||||||
|
if (!token) user = null;
|
||||||
|
else apiFunctions.getMe(token).then((res) => (user = res));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAccessToken(): Promise<string | null> {
|
||||||
|
return await lock.acquire('isValid', async () => {
|
||||||
|
const { accessToken } = authState;
|
||||||
|
// Check access token validity
|
||||||
|
if (accessToken) {
|
||||||
|
const exp = jwtDecode(accessToken).exp ?? 0;
|
||||||
|
if (Date.now() < exp * 1000) {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { refreshToken } = fetchStorage();
|
||||||
|
// Check refresh token validity
|
||||||
|
if (refreshToken) {
|
||||||
|
const exp = jwtDecode(refreshToken).exp ?? 0;
|
||||||
|
if (Date.now() < exp * 1000) {
|
||||||
|
// Try to refresh
|
||||||
|
try {
|
||||||
|
authState = await apiFunctions.refresh(refreshToken);
|
||||||
|
console.log('Token refreshed');
|
||||||
|
return authState.accessToken;
|
||||||
|
} catch (e) {
|
||||||
|
if (isAxiosError(e) && e.response?.status === 401) authState.refreshToken = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
get user() {
|
||||||
|
return user;
|
||||||
|
},
|
||||||
|
login,
|
||||||
|
getAccessToken,
|
||||||
|
refreshUser,
|
||||||
|
async logout(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await apiFunctions.logout((await getAccessToken())!);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
authState = { accessToken: null, refreshToken: null };
|
||||||
|
tick().then(refreshUser);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'auth-store';
|
||||||
|
|
||||||
|
function fetchStorage(): AuthState {
|
||||||
|
if (!browser) return { accessToken: null, refreshToken: null };
|
||||||
|
const value = localStorage.getItem(STORAGE_KEY) || '{}';
|
||||||
|
return JSON.parse(value) as AuthState;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONTEXT_KEY = 'AUTH_CONTEXT';
|
||||||
|
|
||||||
|
export function createAuthContext() {
|
||||||
|
return setContext(CONTEXT_KEY, createAuthState());
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAuthContext() {
|
||||||
|
return getContext<ReturnType<typeof createAuthContext>>(CONTEXT_KEY);
|
||||||
|
}
|
@ -1,71 +0,0 @@
|
|||||||
import { apiFunctions } from '$lib/api';
|
|
||||||
import AsyncLock from 'async-lock';
|
|
||||||
import { isAxiosError } from 'axios';
|
|
||||||
import { get, writable } from 'svelte/store';
|
|
||||||
import { jwtDecode } from 'jwt-decode';
|
|
||||||
import { browser } from '$app/environment';
|
|
||||||
|
|
||||||
const lock = new AsyncLock();
|
|
||||||
|
|
||||||
type AuthStore = {
|
|
||||||
accessToken: string | null;
|
|
||||||
refreshToken: string | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const state: AuthStore = (() => {
|
|
||||||
if (!browser) return { accessToken: null, refreshToken: null };
|
|
||||||
const value = localStorage.getItem('auth-store') || '{}';
|
|
||||||
return JSON.parse(value) as AuthStore;
|
|
||||||
})();
|
|
||||||
|
|
||||||
export const authStore = writable<AuthStore>(state);
|
|
||||||
|
|
||||||
authStore.subscribe((value) => {
|
|
||||||
if (!browser) return;
|
|
||||||
localStorage.setItem('auth-store', JSON.stringify(value));
|
|
||||||
});
|
|
||||||
|
|
||||||
export async function isAuthValid(): Promise<boolean> {
|
|
||||||
return lock.acquire<boolean>('isValid', async () => {
|
|
||||||
const { accessToken, refreshToken } = get(authStore);
|
|
||||||
|
|
||||||
// Check access token validity
|
|
||||||
if (accessToken) {
|
|
||||||
const exp = jwtDecode(accessToken).exp ?? 0;
|
|
||||||
if (Date.now() < exp * 1000) {
|
|
||||||
return true;
|
|
||||||
} else authStore.update((v) => ({ ...v, accessToken: null }));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check refresh token validity
|
|
||||||
if (refreshToken) {
|
|
||||||
const exp = jwtDecode(refreshToken).exp ?? 0;
|
|
||||||
if (Date.now() < exp * 1000) {
|
|
||||||
// Try to refresh
|
|
||||||
try {
|
|
||||||
authStore.set(await apiFunctions.refresh(refreshToken));
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
if (isAxiosError(e) && e.response?.status === 401)
|
|
||||||
authStore.update((v) => ({ ...v, refreshToken: null }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getAccessToken(): Promise<string | null> {
|
|
||||||
const isValid = await isAuthValid();
|
|
||||||
return isValid ? get(authStore).accessToken : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function logout(): void {
|
|
||||||
try {
|
|
||||||
apiFunctions.logout(get(authStore).accessToken!);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
authStore.set({ accessToken: null, refreshToken: null });
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
import { apiFunctions } from '$lib/api';
|
|
||||||
import type { User } from '$lib/interfaces/user.interface';
|
|
||||||
import { get, writable } from 'svelte/store';
|
|
||||||
import { authStore, getAccessToken } from './auth-store';
|
|
||||||
|
|
||||||
export const userStore = writable<User | null>();
|
|
||||||
|
|
||||||
export const refreshUser = () =>
|
|
||||||
getAccessToken()
|
|
||||||
.then((token) => {
|
|
||||||
if (!token) return null;
|
|
||||||
return apiFunctions.getMe(token);
|
|
||||||
})
|
|
||||||
.then(userStore.set);
|
|
||||||
|
|
||||||
authStore.subscribe((auth) => {
|
|
||||||
if (!auth.refreshToken) return userStore.set(null);
|
|
||||||
if (!get(userStore)) refreshUser();
|
|
||||||
});
|
|
@ -1,6 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Header from '$lib/components/header.svelte';
|
import Header from '$lib/components/header.svelte';
|
||||||
|
import { createAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import '../app.css';
|
import '../app.css';
|
||||||
|
|
||||||
|
createAuthContext();
|
||||||
|
|
||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { Role } from '$lib/enums/role.enum';
|
import { Role } from '$lib/enums/role.enum';
|
||||||
import { userStore } from '$lib/stores/user-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
|
|
||||||
const pathname = $derived($page.url.pathname);
|
const pathname = $derived($page.url.pathname);
|
||||||
|
|
||||||
@ -18,8 +20,8 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if ($userStore === undefined) return;
|
if (auth.user === undefined) return;
|
||||||
if (!$userStore?.roles.includes(Role.Admin)) goto('/');
|
if (!auth.user?.roles.includes(Role.Admin)) goto('/');
|
||||||
});
|
});
|
||||||
|
|
||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
|
@ -2,11 +2,22 @@
|
|||||||
import { faAdd } from '@fortawesome/free-solid-svg-icons';
|
import { faAdd } from '@fortawesome/free-solid-svg-icons';
|
||||||
import Fa from 'svelte-fa';
|
import Fa from 'svelte-fa';
|
||||||
import { getUTCString } from '$lib/util/date.util';
|
import { getUTCString } from '$lib/util/date.util';
|
||||||
import type { PageData } from './$types';
|
|
||||||
import PrivateTag from '$lib/components/private-tag.svelte';
|
import PrivateTag from '$lib/components/private-tag.svelte';
|
||||||
import Loading from '$lib/components/loading.svelte';
|
import Loading from '$lib/components/loading.svelte';
|
||||||
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
|
import { apiFunctions } from '$lib/api';
|
||||||
|
|
||||||
export let data: PageData;
|
const auth = getAuthContext();
|
||||||
|
|
||||||
|
const eventsP = auth
|
||||||
|
.getAccessToken()
|
||||||
|
.then((token) => {
|
||||||
|
if (!token) return [];
|
||||||
|
return apiFunctions.getAllEvents(token);
|
||||||
|
})
|
||||||
|
.then((events) => {
|
||||||
|
return events.sort((a, b) => b.createdAt.valueOf() - a.createdAt.valueOf());
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -19,7 +30,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#await data.events}
|
{#await eventsP}
|
||||||
<Loading />
|
<Loading />
|
||||||
{:then events}
|
{:then events}
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
import { apiFunctions } from '$lib/api';
|
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
|
||||||
import type { PageLoad } from './$types';
|
|
||||||
|
|
||||||
export const load: PageLoad = () => {
|
|
||||||
return {
|
|
||||||
events: getAccessToken()
|
|
||||||
.then((token) => {
|
|
||||||
if (!token) return [];
|
|
||||||
return apiFunctions.getAllEvents(token);
|
|
||||||
})
|
|
||||||
.then((events) => {
|
|
||||||
return events.sort((a, b) => b.createdAt.valueOf() - a.createdAt.valueOf());
|
|
||||||
})
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,23 +1,24 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { invalidateAll } from '$app/navigation';
|
|
||||||
import { apiFunctions } from '$lib/api';
|
import { apiFunctions } from '$lib/api';
|
||||||
import Loading from '$lib/components/loading.svelte';
|
import Loading from '$lib/components/loading.svelte';
|
||||||
import { uppercaseInput } from '$lib/input-helpers';
|
import { uppercaseInput } from '$lib/input-helpers';
|
||||||
import type { Event } from '$lib/interfaces/event.interface';
|
import type { Event } from '$lib/interfaces/event.interface';
|
||||||
import type { User } from '$lib/interfaces/user.interface';
|
import type { User } from '$lib/interfaces/user.interface';
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import { faPlus, faRemove } from '@fortawesome/free-solid-svg-icons';
|
import { faPlus, faRemove } from '@fortawesome/free-solid-svg-icons';
|
||||||
import Fa from 'svelte-fa';
|
import Fa from 'svelte-fa';
|
||||||
|
|
||||||
const { event }: { event: Event } = $props();
|
const { event }: { event: Event } = $props();
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
|
|
||||||
let users = $state<User[]>();
|
let users = $state<User[]>();
|
||||||
let error = $state<string>();
|
let error = $state<string>();
|
||||||
|
|
||||||
let usernameInput = $state('');
|
let usernameInput = $state('');
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
getAccessToken().then((token) => {
|
auth.getAccessToken().then((token) => {
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
apiFunctions
|
apiFunctions
|
||||||
.getManyUsers(token, event.access)
|
.getManyUsers(token, event.access)
|
||||||
@ -29,11 +30,11 @@
|
|||||||
async function grantAccess() {
|
async function grantAccess() {
|
||||||
error = undefined;
|
error = undefined;
|
||||||
try {
|
try {
|
||||||
const token = await getAccessToken();
|
const token = await auth.getAccessToken();
|
||||||
if (!token) throw Error('Unauthenticated');
|
if (!token) throw Error('Unauthenticated');
|
||||||
const user = await apiFunctions.findByUsername(token, usernameInput.toUpperCase());
|
const user = await apiFunctions.findByUsername(token, usernameInput.toUpperCase());
|
||||||
await apiFunctions.grantEventAccess(token, event._id, user._id);
|
await apiFunctions.grantEventAccess(token, event._id, user._id);
|
||||||
invalidateAll();
|
users?.push(user);
|
||||||
usernameInput = '';
|
usernameInput = '';
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@ -45,10 +46,10 @@
|
|||||||
|
|
||||||
async function revokeAccess(userId: string) {
|
async function revokeAccess(userId: string) {
|
||||||
try {
|
try {
|
||||||
const token = await getAccessToken();
|
const token = await auth.getAccessToken();
|
||||||
if (!token) throw Error('Unauthenticated');
|
if (!token) throw Error('Unauthenticated');
|
||||||
await apiFunctions.revokeEventAccess(token, event._id, userId);
|
await apiFunctions.revokeEventAccess(token, event._id, userId);
|
||||||
invalidateAll();
|
users = users?.filter((u) => u._id !== userId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
@ -4,13 +4,15 @@
|
|||||||
import type { Event } from '$lib/interfaces/event.interface';
|
import type { Event } from '$lib/interfaces/event.interface';
|
||||||
import type { Reservation } from '$lib/interfaces/reservation.interface';
|
import type { Reservation } from '$lib/interfaces/reservation.interface';
|
||||||
import type { User } from '$lib/interfaces/user.interface';
|
import type { User } from '$lib/interfaces/user.interface';
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import { dayInMs, getUTCDateString, getUTCTimeString } from '$lib/util/date.util';
|
import { dayInMs, getUTCDateString, getUTCTimeString } from '$lib/util/date.util';
|
||||||
import { faFileCircleCheck, faFileCircleExclamation } from '@fortawesome/free-solid-svg-icons';
|
import { faFileCircleCheck, faFileCircleExclamation } from '@fortawesome/free-solid-svg-icons';
|
||||||
import Fa from 'svelte-fa';
|
import Fa from 'svelte-fa';
|
||||||
|
|
||||||
const { event }: { event: Event } = $props();
|
const { event }: { event: Event } = $props();
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
|
|
||||||
let reservations = $state<Reservation[]>();
|
let reservations = $state<Reservation[]>();
|
||||||
let users = $state<Map<string, User>>();
|
let users = $state<Map<string, User>>();
|
||||||
|
|
||||||
@ -20,7 +22,7 @@
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
reservations = res.sort((a, b) => b.startDateTime.valueOf() - a.startDateTime.valueOf());
|
reservations = res.sort((a, b) => b.startDateTime.valueOf() - a.startDateTime.valueOf());
|
||||||
const u = new Set<string>(res.map((r) => r.user));
|
const u = new Set<string>(res.map((r) => r.user));
|
||||||
getAccessToken().then((token) => {
|
auth.getAccessToken().then((token) => {
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
apiFunctions
|
apiFunctions
|
||||||
.getManyUsers(token, Array.from(u))
|
.getManyUsers(token, Array.from(u))
|
||||||
|
@ -4,17 +4,22 @@
|
|||||||
import Loading from '$lib/components/loading.svelte';
|
import Loading from '$lib/components/loading.svelte';
|
||||||
import { Role } from '$lib/enums/role.enum';
|
import { Role } from '$lib/enums/role.enum';
|
||||||
import type { User } from '$lib/interfaces/user.interface';
|
import type { User } from '$lib/interfaces/user.interface';
|
||||||
import type { PageData } from './$types';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import { userStore } from '$lib/stores/user-store';
|
|
||||||
import DeleteModal from './delete-modal.svelte';
|
import DeleteModal from './delete-modal.svelte';
|
||||||
import ResetPasswordModal from './reset-password-modal.svelte';
|
import ResetPasswordModal from './reset-password-modal.svelte';
|
||||||
|
import { apiFunctions } from '$lib/api';
|
||||||
|
|
||||||
const { data }: { data: PageData } = $props();
|
const auth = getAuthContext();
|
||||||
|
|
||||||
let deleteUser = $state<User>();
|
let deleteUser = $state<User>();
|
||||||
let resetPassUser = $state<User>();
|
let resetPassUser = $state<User>();
|
||||||
|
|
||||||
let me = $derived($userStore);
|
let me = $derived(auth.user);
|
||||||
|
|
||||||
|
const usersP = auth.getAccessToken().then((token) => {
|
||||||
|
if (!token) return [];
|
||||||
|
return apiFunctions.getAllUsers(token);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@ -27,7 +32,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#await data.users}
|
{#await usersP}
|
||||||
<Loading />
|
<Loading />
|
||||||
{:then users}
|
{:then users}
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import { apiFunctions } from '$lib/api';
|
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
|
||||||
import type { PageLoad } from './$types';
|
|
||||||
|
|
||||||
export const load: PageLoad = () => {
|
|
||||||
return {
|
|
||||||
users: getAccessToken().then((token) => {
|
|
||||||
if (!token) return [];
|
|
||||||
return apiFunctions.getAllUsers(token);
|
|
||||||
})
|
|
||||||
};
|
|
||||||
};
|
|
@ -2,11 +2,13 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { apiFunctions } from '$lib/api';
|
import { apiFunctions } from '$lib/api';
|
||||||
import { uppercaseInput } from '$lib/input-helpers';
|
import { uppercaseInput } from '$lib/input-helpers';
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
|
import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
|
||||||
import Fa from 'svelte-fa';
|
import Fa from 'svelte-fa';
|
||||||
import PasswordField from '../password-field.svelte';
|
import PasswordField from '../password-field.svelte';
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
|
|
||||||
let username = $state('');
|
let username = $state('');
|
||||||
let password = $state('');
|
let password = $state('');
|
||||||
let name = $state('');
|
let name = $state('');
|
||||||
@ -16,7 +18,7 @@
|
|||||||
|
|
||||||
function submit() {
|
function submit() {
|
||||||
error = undefined;
|
error = undefined;
|
||||||
getAccessToken().then((token) => {
|
auth.getAccessToken().then((token) => {
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
apiFunctions
|
apiFunctions
|
||||||
.createUser(token, {
|
.createUser(token, {
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
import { invalidateAll } from '$app/navigation';
|
import { invalidateAll } from '$app/navigation';
|
||||||
import { apiFunctions } from '$lib/api';
|
import { apiFunctions } from '$lib/api';
|
||||||
import type { User } from '$lib/interfaces/user.interface';
|
import type { User } from '$lib/interfaces/user.interface';
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
|
|
||||||
let { deleteUser = $bindable() }: { deleteUser: User | undefined } = $props();
|
let { deleteUser = $bindable() }: { deleteUser: User | undefined } = $props();
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<dialog class="modal {deleteUser ? 'modal-open' : ''}">
|
<dialog class="modal {deleteUser ? 'modal-open' : ''}">
|
||||||
@ -19,7 +21,7 @@
|
|||||||
class="btn btn-error"
|
class="btn btn-error"
|
||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
deleteUser = undefined;
|
deleteUser = undefined;
|
||||||
const token = await getAccessToken();
|
const token = await auth.getAccessToken();
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
apiFunctions
|
apiFunctions
|
||||||
.deleteUser(token, deleteUser!._id)
|
.deleteUser(token, deleteUser!._id)
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { invalidateAll } from '$app/navigation';
|
|
||||||
import { apiFunctions } from '$lib/api';
|
import { apiFunctions } from '$lib/api';
|
||||||
import type { User } from '$lib/interfaces/user.interface';
|
import type { User } from '$lib/interfaces/user.interface';
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import { tick } from 'svelte';
|
|
||||||
import PasswordField from './password-field.svelte';
|
import PasswordField from './password-field.svelte';
|
||||||
|
|
||||||
let { user = $bindable() }: { user: User | undefined } = $props();
|
let { user = $bindable() }: { user: User | undefined } = $props();
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
|
|
||||||
let password = $state('');
|
let password = $state('');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -23,7 +23,7 @@
|
|||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
onclick={async () => {
|
onclick={async () => {
|
||||||
const token = await getAccessToken();
|
const token = await auth.getAccessToken();
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
apiFunctions
|
apiFunctions
|
||||||
.updateOtherPassword(token, user!._id, password)
|
.updateOtherPassword(token, user!._id, password)
|
||||||
|
@ -6,13 +6,15 @@
|
|||||||
import Fa from 'svelte-fa';
|
import Fa from 'svelte-fa';
|
||||||
import FreeDatesComponent from './free-dates-component.svelte';
|
import FreeDatesComponent from './free-dates-component.svelte';
|
||||||
import Time from './time.svelte';
|
import Time from './time.svelte';
|
||||||
import { userStore } from '$lib/stores/user-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import ReserveComponent from './reserve/reserve-component.svelte';
|
import ReserveComponent from './reserve/reserve-component.svelte';
|
||||||
import MyReservations from './reserve/my-reservations.svelte';
|
import MyReservations from './reserve/my-reservations.svelte';
|
||||||
import { Role } from '$lib/enums/role.enum';
|
import { Role } from '$lib/enums/role.enum';
|
||||||
import PrivateTag from '$lib/components/private-tag.svelte';
|
import PrivateTag from '$lib/components/private-tag.svelte';
|
||||||
|
|
||||||
const { event }: { event: Event } = $props();
|
const { event }: { event: Event } = $props();
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-8">
|
<div class="flex flex-col gap-8">
|
||||||
@ -28,7 +30,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $userStore?.roles.includes(Role.Admin)}
|
{#if auth.user?.roles.includes(Role.Admin)}
|
||||||
<a href="/admin/events/{event._id}" class="btn btn-warning btn-sm">Uredi</a>
|
<a href="/admin/events/{event._id}" class="btn btn-warning btn-sm">Uredi</a>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@ -49,7 +51,7 @@
|
|||||||
<FreeDatesComponent {event} />
|
<FreeDatesComponent {event} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if $userStore && event.access.includes($userStore._id)}
|
{#if auth.user && event.access.includes(auth.user._id)}
|
||||||
<div>
|
<div>
|
||||||
<h2 class="mb-4 text-2xl">Moje rezervacije</h2>
|
<h2 class="mb-4 text-2xl">Moje rezervacije</h2>
|
||||||
<MyReservations eventId={event._id} />
|
<MyReservations eventId={event._id} />
|
||||||
@ -63,7 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<ReserveComponent {event} />
|
<ReserveComponent {event} />
|
||||||
</div>
|
</div>
|
||||||
{:else if !$userStore}
|
{:else if !auth.user}
|
||||||
<div>
|
<div>
|
||||||
<a href="/login?redirect=/event/{event._id}" class="btn">Prijavi se za rezervacijo</a>
|
<a href="/login?redirect=/event/{event._id}" class="btn">Prijavi se za rezervacijo</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,11 +2,13 @@
|
|||||||
import { apiFunctions } from '$lib/api';
|
import { apiFunctions } from '$lib/api';
|
||||||
import Loading from '$lib/components/loading.svelte';
|
import Loading from '$lib/components/loading.svelte';
|
||||||
import ReservationsTable from '$lib/components/reservations-table.svelte';
|
import ReservationsTable from '$lib/components/reservations-table.svelte';
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
|
|
||||||
const { eventId }: { eventId: string } = $props();
|
const { eventId }: { eventId: string } = $props();
|
||||||
|
|
||||||
const reservations = getAccessToken().then((token) => {
|
const auth = getAuthContext();
|
||||||
|
|
||||||
|
const reservations = auth.getAccessToken().then((token) => {
|
||||||
if (!token) return [];
|
if (!token) return [];
|
||||||
return apiFunctions
|
return apiFunctions
|
||||||
.getReservationsForSelf(token, { event: eventId })
|
.getReservationsForSelf(token, { event: eventId })
|
||||||
|
@ -10,9 +10,11 @@
|
|||||||
import BandSelector from './band-selector.svelte';
|
import BandSelector from './band-selector.svelte';
|
||||||
import ModeSelector from './mode-selector.svelte';
|
import ModeSelector from './mode-selector.svelte';
|
||||||
import { invalidateAll } from '$app/navigation';
|
import { invalidateAll } from '$app/navigation';
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import { clampDate } from '..';
|
import { clampDate } from '..';
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
|
|
||||||
function floorHour(date: number) {
|
function floorHour(date: number) {
|
||||||
return new Date(Math.floor(date / hourInMs) * hourInMs);
|
return new Date(Math.floor(date / hourInMs) * hourInMs);
|
||||||
}
|
}
|
||||||
@ -92,7 +94,7 @@
|
|||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
error = undefined;
|
error = undefined;
|
||||||
const token = await getAccessToken();
|
const token = await auth.getAccessToken();
|
||||||
if (!token) {
|
if (!token) {
|
||||||
error = ['Napaka'];
|
error = ['Napaka'];
|
||||||
return;
|
return;
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { page } from '$app/stores';
|
import { page } from '$app/stores';
|
||||||
import { apiFunctions } from '$lib/api';
|
|
||||||
import { uppercaseInput } from '$lib/input-helpers';
|
import { uppercaseInput } from '$lib/input-helpers';
|
||||||
import { authStore, isAuthValid } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
|
|
||||||
const redirect = $page.url.searchParams.get('redirect');
|
const redirect = $page.url.searchParams.get('redirect');
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
isAuthValid().then((r) => {
|
if (auth.user) goto(redirect || '/');
|
||||||
if (r) goto(redirect || '/');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let username = $state('');
|
let username = $state('');
|
||||||
@ -21,16 +20,9 @@
|
|||||||
function login() {
|
function login() {
|
||||||
error = undefined;
|
error = undefined;
|
||||||
|
|
||||||
apiFunctions
|
auth.login(username, password).then((res) => {
|
||||||
.login(username, password)
|
if (!res) error = 'Napačno uporabniško ime ali geslo';
|
||||||
.then((res) => {
|
});
|
||||||
authStore.set(res);
|
|
||||||
goto(redirect || '/');
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.error(e);
|
|
||||||
error = 'Napačno uporabniško ime ali geslo';
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,14 +1,25 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { apiFunctions } from '$lib/api';
|
||||||
import Loading from '$lib/components/loading.svelte';
|
import Loading from '$lib/components/loading.svelte';
|
||||||
import ReservationsTable from '$lib/components/reservations-table.svelte';
|
import ReservationsTable from '$lib/components/reservations-table.svelte';
|
||||||
import ResetPasswordAlert from '$lib/components/reset-password-alert.svelte';
|
import ResetPasswordAlert from '$lib/components/reset-password-alert.svelte';
|
||||||
import type { PageData } from './$types';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
|
|
||||||
const { data }: { data: PageData } = $props();
|
const auth = getAuthContext();
|
||||||
|
|
||||||
|
const promise = auth.getAccessToken().then((token) => {
|
||||||
|
if (!token) throw Error('Unauthenticated');
|
||||||
|
return Promise.all([
|
||||||
|
apiFunctions.getMe(token),
|
||||||
|
apiFunctions.getReservationsForSelf(token).then((res) => {
|
||||||
|
return res.sort((a, b) => b.startDateTime.valueOf() - a.startDateTime.valueOf());
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container flex flex-col gap-8 py-10">
|
<div class="container flex flex-col gap-8 py-10">
|
||||||
{#await Promise.all([data.user, data.reservations])}
|
{#await promise}
|
||||||
<Loading />
|
<Loading />
|
||||||
{:then [user, reservations]}
|
{:then [user, reservations]}
|
||||||
<ResetPasswordAlert />
|
<ResetPasswordAlert />
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import { apiFunctions } from '$lib/api';
|
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
|
||||||
import type { PageLoad } from './$types';
|
|
||||||
|
|
||||||
export const load: PageLoad = async () => {
|
|
||||||
const tokenP = getAccessToken().then((token) => {
|
|
||||||
if (!token) throw Error('Unauthenticated');
|
|
||||||
return token;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
user: tokenP.then(apiFunctions.getMe),
|
|
||||||
reservations: tokenP.then(apiFunctions.getReservationsForSelf).then((res) => {
|
|
||||||
return res.sort((a, b) => b.startDateTime.valueOf() - a.startDateTime.valueOf());
|
|
||||||
})
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,11 +1,11 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { apiFunctions } from '$lib/api';
|
import { apiFunctions } from '$lib/api';
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import { refreshUser, userStore } from '$lib/stores/user-store';
|
|
||||||
import { faCircleCheck, faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
|
import { faCircleCheck, faTriangleExclamation } from '@fortawesome/free-solid-svg-icons';
|
||||||
import Fa from 'svelte-fa';
|
import Fa from 'svelte-fa';
|
||||||
|
|
||||||
const user = $derived($userStore);
|
const auth = getAuthContext();
|
||||||
|
const user = $derived(auth.user);
|
||||||
|
|
||||||
let name = $state('');
|
let name = $state('');
|
||||||
let email = $state('');
|
let email = $state('');
|
||||||
@ -28,7 +28,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = await getAccessToken();
|
const token = await auth.getAccessToken();
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
apiFunctions
|
apiFunctions
|
||||||
.updateSelf(token, {
|
.updateSelf(token, {
|
||||||
@ -38,7 +38,7 @@
|
|||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
saved = true;
|
saved = true;
|
||||||
refreshUser();
|
auth.refreshUser();
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@ -55,7 +55,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = await getAccessToken();
|
const token = await auth.getAccessToken();
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
apiFunctions
|
apiFunctions
|
||||||
.updateSelfPassword(token, oldPassword, newPassword)
|
.updateSelfPassword(token, oldPassword, newPassword)
|
||||||
|
@ -11,9 +11,8 @@
|
|||||||
import DownloadButton from './download-button.svelte';
|
import DownloadButton from './download-button.svelte';
|
||||||
import LogSummary from './log-summary.svelte';
|
import LogSummary from './log-summary.svelte';
|
||||||
import UploadLog from './upload-log.svelte';
|
import UploadLog from './upload-log.svelte';
|
||||||
import { userStore } from '$lib/stores/user-store';
|
|
||||||
import { Role } from '$lib/enums/role.enum';
|
import { Role } from '$lib/enums/role.enum';
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import { apiFunctions } from '$lib/api';
|
import { apiFunctions } from '$lib/api';
|
||||||
import ProgressBar from '$lib/components/progress-bar.svelte';
|
import ProgressBar from '$lib/components/progress-bar.svelte';
|
||||||
import { createTimeState } from '$lib/stores/time-state.svelte';
|
import { createTimeState } from '$lib/stores/time-state.svelte';
|
||||||
@ -21,11 +20,19 @@
|
|||||||
|
|
||||||
const { data }: { data: PageData } = $props();
|
const { data }: { data: PageData } = $props();
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
|
|
||||||
|
const promise = auth.getAccessToken().then((token) => {
|
||||||
|
if (!token) throw new Error('Unauthenticated');
|
||||||
|
const res = apiFunctions.getReservation(token, data.id);
|
||||||
|
return Promise.all([res, res.then((r) => apiFunctions.getEvent(r.event))]);
|
||||||
|
});
|
||||||
|
|
||||||
const now = createTimeState(10_000);
|
const now = createTimeState(10_000);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="container py-10">
|
<div class="container py-10">
|
||||||
{#await Promise.all([data.reservation, data.event])}
|
{#await promise}
|
||||||
<Loading />
|
<Loading />
|
||||||
{:then [reservation, event]}
|
{:then [reservation, event]}
|
||||||
<div class="flex flex-col gap-10">
|
<div class="flex flex-col gap-10">
|
||||||
@ -36,7 +43,7 @@
|
|||||||
<span>Nazaj na dogodek</span>
|
<span>Nazaj na dogodek</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{#if $userStore?.roles.includes(Role.Admin)}
|
{#if auth.user?.roles.includes(Role.Admin)}
|
||||||
<a href="/admin/events/{event._id}" class="btn btn-warning btn-sm btn-outline">
|
<a href="/admin/events/{event._id}" class="btn btn-warning btn-sm btn-outline">
|
||||||
Nazaj na dogodek
|
Nazaj na dogodek
|
||||||
</a>
|
</a>
|
||||||
@ -44,7 +51,7 @@
|
|||||||
<button
|
<button
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
if (!confirm('Ali ste prepričani, da želite izbrisati rezervacijo?')) return;
|
if (!confirm('Ali ste prepričani, da želite izbrisati rezervacijo?')) return;
|
||||||
getAccessToken().then((token) => {
|
auth.getAccessToken().then((token) => {
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
apiFunctions.deleteReservation(token, reservation._id).then(() => {
|
apiFunctions.deleteReservation(token, reservation._id).then(() => {
|
||||||
window.location.href = `/event/${event._id}`;
|
window.location.href = `/event/${event._id}`;
|
||||||
|
@ -1,17 +1,7 @@
|
|||||||
import { apiFunctions } from '$lib/api';
|
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
|
||||||
import type { PageLoad } from './$types';
|
import type { PageLoad } from './$types';
|
||||||
|
|
||||||
export const load: PageLoad = ({ params }) => {
|
export const load: PageLoad = ({ params }) => {
|
||||||
const tokenP = getAccessToken();
|
|
||||||
|
|
||||||
const res = tokenP.then((token) => {
|
|
||||||
if (!token) throw Error('Unauthenticated');
|
|
||||||
return apiFunctions.getReservation(token, params.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
reservation: res,
|
id: params.id
|
||||||
event: res.then((res) => apiFunctions.getEvent(res.event))
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -2,13 +2,15 @@
|
|||||||
import { apiFunctions } from '$lib/api';
|
import { apiFunctions } from '$lib/api';
|
||||||
import type { Event } from '$lib/interfaces/event.interface';
|
import type { Event } from '$lib/interfaces/event.interface';
|
||||||
import type { Reservation } from '$lib/interfaces/reservation.interface';
|
import type { Reservation } from '$lib/interfaces/reservation.interface';
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import { faDownload } from '@fortawesome/free-solid-svg-icons';
|
import { faDownload } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { tick } from 'svelte';
|
import { tick } from 'svelte';
|
||||||
import Fa from 'svelte-fa';
|
import Fa from 'svelte-fa';
|
||||||
|
|
||||||
const { event, reservation }: { event: Event; reservation: Reservation } = $props();
|
const { event, reservation }: { event: Event; reservation: Reservation } = $props();
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
|
|
||||||
let aRef: HTMLAnchorElement;
|
let aRef: HTMLAnchorElement;
|
||||||
let url = $state<string>();
|
let url = $state<string>();
|
||||||
|
|
||||||
@ -24,7 +26,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = await getAccessToken();
|
const token = await auth.getAccessToken();
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
apiFunctions
|
apiFunctions
|
||||||
.getLog(token, reservation._id)
|
.getLog(token, reservation._id)
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { apiFunctions } from '$lib/api';
|
import { apiFunctions } from '$lib/api';
|
||||||
import type { Reservation } from '$lib/interfaces/reservation.interface';
|
import type { Reservation } from '$lib/interfaces/reservation.interface';
|
||||||
import { getAccessToken } from '$lib/stores/auth-store';
|
import { getAuthContext } from '$lib/stores/auth-state.svelte';
|
||||||
import { userStore } from '$lib/stores/user-store';
|
|
||||||
|
|
||||||
const { reservation: res }: { reservation: Reservation } = $props();
|
const { reservation: res }: { reservation: Reservation } = $props();
|
||||||
|
|
||||||
|
const auth = getAuthContext();
|
||||||
|
|
||||||
let error = $state<string>();
|
let error = $state<string>();
|
||||||
let file = $state<File>();
|
let file = $state<File>();
|
||||||
|
|
||||||
@ -16,7 +17,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = await getAccessToken();
|
const token = await auth.getAccessToken();
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
apiFunctions
|
apiFunctions
|
||||||
.uploadLog(token, res._id, file)
|
.uploadLog(token, res._id, file)
|
||||||
@ -30,7 +31,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $userStore?._id === res.user}
|
{#if auth.user?._id === res.user}
|
||||||
<div class="form-control">
|
<div class="form-control">
|
||||||
<div class="label">
|
<div class="label">
|
||||||
{res.logSummary ? 'Ponovno oddaj' : 'Oddaj'} dnevnik
|
{res.logSummary ? 'Ponovno oddaj' : 'Oddaj'} dnevnik
|
||||||
|
Loading…
x
Reference in New Issue
Block a user