diff --git a/frontend/src/routes/+layout.server.ts b/frontend/src/routes/+layout.server.ts index b3aecd87510c735f98e5cb872178617dbaeaccd9..52b690c683dbb07b89175a4081bfde972b2465ce 100644 --- a/frontend/src/routes/+layout.server.ts +++ b/frontend/src/routes/+layout.server.ts @@ -1,4 +1,5 @@ import { type ServerLoad, redirect } from '@sveltejs/kit'; +import { patchUserAPI } from '$lib/api/users'; const publicly_allowed = ['/login', '/register', '/tests', '/surveys', '/tutor/register']; @@ -11,10 +12,16 @@ const isPublic = (path: string) => { return false; }; -export const load: ServerLoad = async ({ locals, url }) => { +export const load: ServerLoad = async ({ locals, url, fetch }) => { if (locals.user == null || locals.user == undefined) { if (!isPublic(url.pathname)) { - redirect(303, `/login`); + throw redirect(303, `/login`); + } + } else { + try { + await patchUserAPI(fetch, locals.user.id, { is_active: true }); + } catch (error) { + console.error('Failed to update user status:', error); } } diff --git a/frontend/src/routes/Header.svelte b/frontend/src/routes/Header.svelte index 876faf2f741150f97c9aaa4e5e0bb02f82717092..99cf2da8761753302a65dc760cf8280801493147 100644 --- a/frontend/src/routes/Header.svelte +++ b/frontend/src/routes/Header.svelte @@ -14,6 +14,22 @@ displayMetadataWarning = true; } } + + function getUserStatus(user: User | null, isIn: boolean): string { + if (!user) { + return 'red'; + } + + if (!user.is_active) { + return 'red'; + } + + if (isIn) { + return 'green'; + } + + return 'orange'; + } </script> <header class="navbar shadow bg-gray-200"> @@ -54,14 +70,26 @@ {#if user} <li> <details> - <summary class="px-3"> - <img - src={`https://gravatar.com/avatar/${user.emailHash}?d=identicon`} - alt={''} - class="rounded-full border text-sm size-8 border-neutral-400" - /> - {user.nickname} + <summary class="px-3 flex items-center space-x-2"> + <div class="relative"> + <img + src={`https://gravatar.com/avatar/${user.emailHash}?d=identicon`} + alt={''} + class="rounded-full border text-sm size-8 border-neutral-400" + /> + <div + class={`absolute bottom-0 right-0 w-3 h-3 rounded-full border-2 border-white ${ + getUserStatus(user, true) === 'green' + ? 'bg-green-500' + : getUserStatus(user, false) === 'orange' + ? 'bg-orange-500' + : 'bg-red-500' + }`} + ></div> + </div> + <span>{user.nickname}</span> </summary> + <ul class="menu menu-sm dropdown-content absolute right-0"> <li> <a data-sveltekit-reload href="/logout" class="whitespace-nowrap"> diff --git a/frontend/src/routes/logout/+page.server.ts b/frontend/src/routes/logout/+page.server.ts index 7790fb9c026c13a8dfafae0d1e2b38ee1fb553f1..7cd9bda3942d17247394a1ff656bdc0b4da093e2 100644 --- a/frontend/src/routes/logout/+page.server.ts +++ b/frontend/src/routes/logout/+page.server.ts @@ -1,8 +1,22 @@ import { type ServerLoad, redirect } from '@sveltejs/kit'; +import { patchUserAPI } from '$lib/api/users'; + +export const load: ServerLoad = async ({ cookies, locals, fetch }) => { + if (locals.user) { + try { + const success = await patchUserAPI(fetch, locals.user.id, { is_active: false }); + if (!success) { + console.error('Failed to update user status.'); + } + } catch (error) { + console.error('Error updating user status:', error); + } + } -export const load: ServerLoad = async ({ cookies }) => { cookies.set('access_token_cookie', '', { maxAge: -1, path: '/' }); cookies.set('refresh_token_cookie', '', { maxAge: -1, path: '/' }); - redirect(303, '/login'); + locals.user = null; + + throw redirect(303, '/login'); }; diff --git a/frontend/src/routes/sessions/[id]/Message.svelte b/frontend/src/routes/sessions/[id]/Message.svelte index 943515d2dee818b3418757db16fb8127b2bc38e6..796beded212a57bfdb543cd7ae79ad1b8d3de82b 100644 --- a/frontend/src/routes/sessions/[id]/Message.svelte +++ b/frontend/src/routes/sessions/[id]/Message.svelte @@ -185,6 +185,22 @@ await message.deleteFeedback(feedback); } + + function getUserStatus(user: User | null, isInConversation: boolean): string { + if (!user) { + return 'red'; + } + + if (!user.is_active) { + return 'red'; + } + + if (isInConversation) { + return 'green'; + } + + return 'orange'; + } </script> <div @@ -193,12 +209,21 @@ class:chat-start={!isSender} class:chat-end={isSender} > - <div class="rounded-full mx-2 chat-image size-12" title={message.user.nickname}> + <div class="relative rounded-full mx-2 chat-image size-12" title={message.user.nickname}> <img src={`https://gravatar.com/avatar/${message.user.emailHash}?d=identicon`} alt={user.nickname} class="rounded-full border border-neutral-400 text-sm" /> + <div + class={`absolute bottom-0 right-0 w-3 h-3 rounded-full border-2 border-white ${ + getUserStatus(message.user, true) === 'green' + ? 'bg-green-500' + : getUserStatus(message.user, false) === 'orange' + ? 'bg-orange-500' + : 'bg-red-500' + }`} + ></div> </div> <div class="chat-bubble text-black" class:bg-blue-50={isSender} class:bg-gray-300={!isSender}>