From 992dd9cb10a0bcddc59db399daf5e1f57d6a2dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksa=20Siri=C5=A1ki?= <31509435+aleksasiriski@users.noreply.github.com> Date: Wed, 25 Dec 2024 16:19:04 +0100 Subject: [PATCH 1/5] fix(docker-compose): add folder as volume for dev db --- docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index d79b2b7..86d9a16 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,3 +9,6 @@ services: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} POSTGRES_DB: ${POSTGRES_DB} + volumes: + - './database:/var/lib/postgresql/data:z' + restart: never From 8ab3f23f309052a4620639d20f284e4096de5c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksa=20Siri=C5=A1ki?= <31509435+aleksasiriski@users.noreply.github.com> Date: Wed, 25 Dec 2024 16:19:18 +0100 Subject: [PATCH 2/5] fix(docker-compose): add exposed ports for ssh tunnel --- docker-compose.prod.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index b828ec8..9a468a0 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -73,6 +73,8 @@ services: build: context: . dockerfile: docker/postgres.Dockerfile + ports: + - 5432:5432 environment: POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} From 6a9d20925d4ea524f0dd2c2a17bfd0616540e7bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksa=20Siri=C5=A1ki?= <31509435+aleksasiriski@users.noreply.github.com> Date: Wed, 25 Dec 2024 16:19:47 +0100 Subject: [PATCH 3/5] fix(search): switch to POST for search and show toast on toggle state --- src/routes/(dashboard)/+page.server.ts | 59 +++++++++++++++++++++++--- src/routes/(dashboard)/+page.svelte | 55 ++++++++++++++++-------- 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/src/routes/(dashboard)/+page.server.ts b/src/routes/(dashboard)/+page.server.ts index 331bd75..4b06bdd 100644 --- a/src/routes/(dashboard)/+page.server.ts +++ b/src/routes/(dashboard)/+page.server.ts @@ -6,11 +6,10 @@ import { Employee, Student, type Person } from '$lib/types/person'; import { invalidateSession } from '$lib/server/db/session'; import { deleteSessionTokenCookie } from '$lib/server/session'; -export const load: PageServerLoad = async ({ url }) => { +export const load: PageServerLoad = async () => { try { - const searchQuery = url.searchParams.get('q') ?? undefined; - const studentsP = getStudents(1000, 0, searchQuery); - const employeesP = getEmployees(1000, 0, searchQuery); + const studentsP = getStudents(1000, 0, undefined); + const employeesP = getEmployees(1000, 0, undefined); const students = await studentsP; const employees = await employeesP; @@ -38,18 +37,62 @@ export const load: PageServerLoad = async ({ url }) => { ]; return { - searchQuery, persons }; } catch (err: unknown) { console.debug(`Failed to get students and employees: ${(err as Error).message}`); return fail(500, { + persons: [] as Person[], message: 'Failed to get students and employees' }); } }; export const actions: Actions = { + search: async ({ request }) => { + try { + const formData = await request.formData(); + const searchQuery = formData.get('q')?.toString() ?? undefined; + + const studentsP = getStudents(1000, 0, searchQuery); + const employeesP = getEmployees(1000, 0, searchQuery); + const students = await studentsP; + const employees = await employeesP; + + const persons: Person[] = [ + ...students.map((s) => ({ + id: s.id, + type: Student, + identifier: s.index, + fname: s.fname, + lname: s.lname, + department: s.department, + building: s.building, + state: s.state + })), + ...employees.map((e) => ({ + id: e.id, + type: Employee, + identifier: e.email, + fname: e.fname, + lname: e.lname, + department: e.department, + building: e.building, + state: e.state + })) + ]; + + return { + searchQuery, + persons + }; + } catch (err: unknown) { + console.debug(`Failed to search: ${(err as Error).message}`); + return fail(500, { + message: 'Failed to search' + }); + } + }, togglestate: async ({ request, locals }) => { try { const formData = await request.formData(); @@ -86,10 +129,14 @@ export const actions: Actions = { } else if (type === Employee) { await toggleEmployeeState(id, building, creator); } else { - return fail(400, { + return fail(500, { message: 'Invalid type (neither student nor employee)' }); } + + return { + message: 'Successfully toggled state' + }; } catch (err: unknown) { console.debug(`Failed to toggle state: ${(err as Error).message}`); return fail(400, { diff --git a/src/routes/(dashboard)/+page.svelte b/src/routes/(dashboard)/+page.svelte index 729056c..9bc35d6 100644 --- a/src/routes/(dashboard)/+page.svelte +++ b/src/routes/(dashboard)/+page.svelte @@ -5,33 +5,54 @@ import Search from 'lucide-svelte/icons/search'; import Reset from 'lucide-svelte/icons/list-restart'; import DataTable from './data-table.svelte'; + import { toast } from 'svelte-sonner'; + import { page } from '$app/stores'; import { columns } from './columns'; + import { enhance } from '$app/forms'; + import { goto } from '$app/navigation'; let { data, form: actionData } = $props(); - const searchQuery = data.searchQuery; - let searchBox: HTMLFormElement | null; + $effect(() => { + const msg = actionData?.message; + if (msg !== undefined) { + if ($page.status === 200) { + toast.success(msg); + } else { + toast.error(msg); + } + } + }); + + let searchQuery = $state(''); + const persons = $derived(actionData?.persons ?? data.persons ?? []);
{ - searchBox?.submit(); + method="POST" + action="?/search" + class="flex gap-2 px-4 py-2" + use:enhance={() => { + return async ({ update }) => { + await update({ reset: false }); + }; }} > -
- - - -

{actionData?.message}

-
+ + +
- +
From 1aa76feef097c954b1912c4ed4debed7cafa5d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksa=20Siri=C5=A1ki?= <31509435+aleksasiriski@users.noreply.github.com> Date: Wed, 25 Dec 2024 16:25:00 +0100 Subject: [PATCH 4/5] fix(search): cleanup --- src/routes/(dashboard)/+page.server.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/routes/(dashboard)/+page.server.ts b/src/routes/(dashboard)/+page.server.ts index 4b06bdd..3a1a681 100644 --- a/src/routes/(dashboard)/+page.server.ts +++ b/src/routes/(dashboard)/+page.server.ts @@ -8,8 +8,8 @@ import { deleteSessionTokenCookie } from '$lib/server/session'; export const load: PageServerLoad = async () => { try { - const studentsP = getStudents(1000, 0, undefined); - const employeesP = getEmployees(1000, 0, undefined); + const studentsP = getStudents(1000, 0); + const employeesP = getEmployees(1000, 0); const students = await studentsP; const employees = await employeesP; @@ -42,7 +42,6 @@ export const load: PageServerLoad = async () => { } catch (err: unknown) { console.debug(`Failed to get students and employees: ${(err as Error).message}`); return fail(500, { - persons: [] as Person[], message: 'Failed to get students and employees' }); } From b8ac77c3f49db2d88be74e85a701e8073ab460b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksa=20Siri=C5=A1ki?= <31509435+aleksasiriski@users.noreply.github.com> Date: Wed, 25 Dec 2024 16:46:32 +0100 Subject: [PATCH 5/5] fix: linting problems long overdue --- src/app.d.ts | 2 -- src/hooks.server.ts | 1 + src/lib/server/db/connect.ts | 2 +- src/routes/(dashboard)/data-table.svelte | 11 ++++++----- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app.d.ts b/src/app.d.ts index 5de50c2..61fd649 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,11 +1,9 @@ -import type { Database } from '$lib/server/db/connect'; import type { User } from '$lib/server/db/schema/user'; import type { Session } from '$lib/server/db/schema/session'; declare global { namespace App { interface Locals { - database: Database; user: User | null; session: Session | null; } diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 760f480..9468998 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -35,6 +35,7 @@ export const handle: Handle = async ({ event, resolve }) => { } else { // If session is invalid, delete the session cookie deleteSessionTokenCookie(event); + // Redirect to the login page after session expiry if (event.url.pathname !== '/login') { return redirect(302, '/login'); diff --git a/src/lib/server/db/connect.ts b/src/lib/server/db/connect.ts index d50f7ea..9e09ae0 100644 --- a/src/lib/server/db/connect.ts +++ b/src/lib/server/db/connect.ts @@ -4,7 +4,7 @@ import { env } from '$env/dynamic/private'; import { connectDatabaseWithURL } from './connect_generic'; export type Database = PostgresJsDatabase>; -export type Client = postgres.Sql<{}>; +export type Client = postgres.Sql<{}>; // eslint-disable-line @typescript-eslint/no-empty-object-type // Connects to the database using the DATABASE_URL environment variable export async function connectDatabase(): Promise<{ diff --git a/src/routes/(dashboard)/data-table.svelte b/src/routes/(dashboard)/data-table.svelte index b5e97c0..86e460a 100644 --- a/src/routes/(dashboard)/data-table.svelte +++ b/src/routes/(dashboard)/data-table.svelte @@ -1,4 +1,4 @@ -