summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorLinnnus <[email protected]>2025-02-18 18:01:09 +0100
committerLinnnus <[email protected]>2025-02-18 18:01:09 +0100
commitd60f89487ab3bc2390054efcd5d986a4aadd4291 (patch)
tree4fe13a768dab5fd80ad3189d237bce4b2068000a /app
parent2b309097ca145651618234476160fb30405eabe7 (diff)
Tilføj opgaver (assignments)
Diffstat (limited to 'app')
-rw-r--r--app/src/lib/server/assignments.ts84
-rw-r--r--app/src/routes/assignments/+page.server.ts16
-rw-r--r--app/src/routes/assignments/+page.svelte21
-rw-r--r--app/src/routes/assignments/[assignmentId]/+page.server.ts27
-rw-r--r--app/src/routes/assignments/[assignmentId]/+page.svelte25
-rw-r--r--app/src/routes/profile/+page.svelte7
6 files changed, 178 insertions, 2 deletions
diff --git a/app/src/lib/server/assignments.ts b/app/src/lib/server/assignments.ts
new file mode 100644
index 0000000..7622408
--- /dev/null
+++ b/app/src/lib/server/assignments.ts
@@ -0,0 +1,84 @@
+import type pg from "pg";
+
+/** A row from the `cemetary_plot` table. */
+export interface CemetaryPlot {
+ id: number;
+ address: string;
+ ownerId: number;
+ //assignmentInterval: Date;
+}
+
+/** A row from the `assignments` table. */
+export interface Assignment {
+ id: number;
+ gardenerId: number;
+ cemetaryPlotId: number;
+ date: Date;
+}
+
+/**
+ * Retrieves all assignments for the given user.
+ *
+ * @param dbConn Connection to database.
+ * @param userId ID used to identify user.
+ */
+export async function getAssignments(dbConn: pg.ClientBase, userId: number): Promise<Assignment[]> {
+ const result = await dbConn.query(
+ "SELECT * FROM assignments WHERE gardener_id = $1 ORDER BY date",
+ [userId],
+ );
+ return result.rows.map(
+ (r) =>
+ ({
+ id: r.id,
+ gardenerId: r.gardener_id,
+ cemetaryPlotId: r.cemetary_plot_id,
+ date: r.date,
+ }) satisfies Assignment,
+ );
+}
+
+type GetAssignmentResult =
+ | { assignment: Assignment; cemetaryPlot: CemetaryPlot }
+ | { assignment: null; cemetaryPlot: null };
+
+/**
+ * Retrieves a specfic assignment, along with relevant cemetary plot.
+ */
+export async function getAssignmentAndCemetaryById(
+ dbConn: pg.ClientBase,
+ assignmentId: number,
+): Promise<GetAssignmentResult> {
+ const queryText = `SELECT
+ a.id,
+ a.gardener_id,
+ a.cemetary_plot_id,
+ a.date,
+ c.id,
+ c.address,
+ c.owner_id,
+ c.assignment_interval
+ FROM
+ assignments AS a
+ INNER JOIN
+ cemetary_plots AS C ON c.id = a.cemetary_plot_id
+ WHERE c.id = $1`;
+ const result = await dbConn.query({ rowMode: "array", text: queryText }, [assignmentId]);
+ if (result.rowCount == 0) {
+ return { assignment: null, cemetaryPlot: null };
+ }
+
+ const assignment: Assignment = {
+ id: result.rows[0][0],
+ gardenerId: result.rows[0][1],
+ cemetaryPlotId: result.rows[0][2],
+ date: result.rows[0][3],
+ };
+ const cemetaryPlot: CemetaryPlot = {
+ id: result.rows[0][4],
+ address: result.rows[0][5],
+ ownerId: result.rows[0][6],
+ //assignmentInterval: result.rows[0][7],
+ };
+ return { assignment, cemetaryPlot };
+}
diff --git a/app/src/routes/assignments/+page.server.ts b/app/src/routes/assignments/+page.server.ts
new file mode 100644
index 0000000..0b829cd
--- /dev/null
+++ b/app/src/routes/assignments/+page.server.ts
@@ -0,0 +1,16 @@
+import type { PageServerLoad } from "./$types";
+import { getAssignments } from "$lib/server/assignments";
+import { redirect } from "@sveltejs/kit";
+
+export const load = (async ({ url, locals }) => {
+ if (!locals.user) {
+ redirect(303, `/login?redirectTo=${encodeURIComponent(url.toString())}`);
+ }
+
+ const assignments = await getAssignments(locals.dbConn, locals.user.id);
+
+ return {
+ user: locals.user,
+ assignments,
+ };
+}) satisfies PageServerLoad;
diff --git a/app/src/routes/assignments/+page.svelte b/app/src/routes/assignments/+page.svelte
new file mode 100644
index 0000000..f43767d
--- /dev/null
+++ b/app/src/routes/assignments/+page.svelte
@@ -0,0 +1,21 @@
+<!-- The /assignments index page gives an overview of assignments -->
+
+<script lang="ts">
+ import type { PageProps } from "./$types";
+
+ const { data }: PageProps = $props();
+</script>
+
+<h1>Kommende opgaver for {data.user.firstName}</h1>
+
+<ol>
+ {#each data.assignments as assignment}
+ <li>
+ <span>{assignment.date}</span>
+ <a href={`/assignments/${assignment.id}`}>Mere info</a>
+ </li>
+ {/each}
+</ol>
+
+<style>
+</style>
diff --git a/app/src/routes/assignments/[assignmentId]/+page.server.ts b/app/src/routes/assignments/[assignmentId]/+page.server.ts
new file mode 100644
index 0000000..566dcd9
--- /dev/null
+++ b/app/src/routes/assignments/[assignmentId]/+page.server.ts
@@ -0,0 +1,27 @@
+import { getAssignmentAndCemetaryById } from "$lib/server/assignments";
+import type { PageServerLoad } from "./$types";
+import { error, redirect } from "@sveltejs/kit";
+
+export const load = (async ({ params, url, locals }) => {
+ if (!locals.user) {
+ redirect(303, `/login?redirectTo=${encodeURIComponent(url.toString())}`);
+ }
+
+ const { assignment, cemetaryPlot } = await getAssignmentAndCemetaryById(
+ locals.dbConn,
+ +params.assignmentId,
+ );
+ if (!assignment) {
+ return error(404, `Cemetary plot with id ${params.assignmentId} not found`);
+ }
+ console.debug("Found assignment: ", assignment);
+ if (assignment.gardenerId !== locals.user.id) {
+ return error(403, "This assignment isn't for you!");
+ }
+
+ return {
+ user: locals.user,
+ assignment,
+ cemetaryPlot,
+ };
+}) satisfies PageServerLoad;
diff --git a/app/src/routes/assignments/[assignmentId]/+page.svelte b/app/src/routes/assignments/[assignmentId]/+page.svelte
new file mode 100644
index 0000000..0b36a19
--- /dev/null
+++ b/app/src/routes/assignments/[assignmentId]/+page.svelte
@@ -0,0 +1,25 @@
+<script lang="ts">
+ import type { PageProps } from "./$types";
+
+ let { data }: PageProps = $props();
+
+ const assignmentDate = $derived(new Intl.DateTimeFormat().format(data.assignment.date));
+</script>
+
+<svelte:head>
+ <title>Opgave på {data.cemetaryPlot.address}</title>
+</svelte:head>
+
+<h1>Assignment #{data.assignment.id}</h1>
+<p>
+ Gravstedet, der skal vedligeholdes er ved <span class="address">{data.cemetaryPlot.address}</span
+ >.
+</p>
+<p>Vedligeholdelsen skal finde sted <time>{assignmentDate}<time>.</time></time></p>
+
+<style>
+ .address,
+ time {
+ font-weight: bold;
+ }
+</style>
diff --git a/app/src/routes/profile/+page.svelte b/app/src/routes/profile/+page.svelte
index 0ee18f0..62078cc 100644
--- a/app/src/routes/profile/+page.svelte
+++ b/app/src/routes/profile/+page.svelte
@@ -8,5 +8,8 @@
<img src="/profile_picture_standin.jpeg" width="255" height="255" alt="Dummy profile picture" />
<p>Hej, {data.user.firstName} {data.user.lastName}!</p>
-<style>
-</style>
+{#if data.user.role === "gardener"}
+ <p>Tjek dine opgaver her: <a href="/assignments">Opgaver</a></p>
+{/if}
+
+<!-- TODO: Burde også vise noget for owners -->