From ab8d143aeb9cfa4e636ce9807710ce9dbbe1aeca Mon Sep 17 00:00:00 2001 From: Alex <$(pass /github/email)> Date: Sun, 13 Oct 2024 13:38:30 +0200 Subject: [PATCH] add usermamagement page --- .../src/routes/auth/admin/users/+layout.js | 8 ++ .../routes/auth/admin/users/+layout.server.js | 56 ++++++++ .../routes/auth/admin/users/+page.server.js | 122 ++++++++++++++++++ .../src/routes/auth/admin/users/+page.svelte | 92 +++++++++++++ 4 files changed, 278 insertions(+) create mode 100644 frontend/src/routes/auth/admin/users/+layout.js create mode 100644 frontend/src/routes/auth/admin/users/+layout.server.js create mode 100644 frontend/src/routes/auth/admin/users/+page.server.js create mode 100644 frontend/src/routes/auth/admin/users/+page.svelte diff --git a/frontend/src/routes/auth/admin/users/+layout.js b/frontend/src/routes/auth/admin/users/+layout.js new file mode 100644 index 0000000..58465b7 --- /dev/null +++ b/frontend/src/routes/auth/admin/users/+layout.js @@ -0,0 +1,8 @@ +/** @type {import('./$types').LayoutLoad} */ +export async function load({ fetch, url, data }) { + const { users } = data; + return { + users: data.users, + user: data.user, + }; +} diff --git a/frontend/src/routes/auth/admin/users/+layout.server.js b/frontend/src/routes/auth/admin/users/+layout.server.js new file mode 100644 index 0000000..348269b --- /dev/null +++ b/frontend/src/routes/auth/admin/users/+layout.server.js @@ -0,0 +1,56 @@ +import { BASE_API_URI } from "$lib/utils/constants"; +import { redirect } from "@sveltejs/kit"; +import { userDatesFromRFC3339, refreshCookie } from "$lib/utils/helpers"; + +/** @type {import('./$types').LayoutServerLoad} */ +export async function load({ cookies, fetch, locals }) { + // if (locals.users) { + // return { + // users: locals.users, + // user: locals.user, + // }; + // } + + const jwt = cookies.get("jwt"); + try { + // Fetch user data, subscriptions, and licence categories in parallel + const response = await fetch(`${BASE_API_URI}/backend/users/all`, { + credentials: "include", + headers: { + Cookie: `jwt=${jwt}`, + }, + }); + if (!response.ok) { + // Clear the invalid JWT cookie + cookies.delete("jwt", { path: "/" }); + throw redirect(302, "/auth/login?next=/"); + } + + const data = await response.json(); + + // Check if the server sent a new token + const newToken = response.headers.get("Set-Cookie"); + refreshCookie(newToken, null); + + /** @type {App.Locals['users']}*/ + const users = data.users; + + users.forEach((user) => { + userDatesFromRFC3339(user); + }); + + locals.users = users; + return { + subscriptions: locals.subscriptions, + licence_categories: locals.licence_categories, + users: locals.users, + user: locals.user, + }; + } catch (error) { + console.error("Error fetching data:", error); + // In case of any error, clear the JWT cookie + cookies.delete("jwt", { path: "/" }); + + throw redirect(302, "/auth/login?next=/"); + } +} diff --git a/frontend/src/routes/auth/admin/users/+page.server.js b/frontend/src/routes/auth/admin/users/+page.server.js new file mode 100644 index 0000000..92a20e7 --- /dev/null +++ b/frontend/src/routes/auth/admin/users/+page.server.js @@ -0,0 +1,122 @@ +// - Add authentication check to ensure only admins can access this route. +// - Implement a load function to fetch a list of all users. +// - Create actions for updating user information (similar to the about/[id] route). + +import { BASE_API_URI } from "$lib/utils/constants"; +import { formatError, userDatesFromRFC3339 } from "$lib/utils/helpers"; +import { fail, redirect } from "@sveltejs/kit"; +import { toRFC3339 } from "$lib/utils/helpers"; + +/** @type {import('./$types').PageServerLoad} */ +export async function load({ locals, params }) { + // redirect user if not logged in + if (!locals.user) { + throw redirect(302, `/auth/login?next=/auth/users`); + } +} + +/** @type {import('./$types').Actions} */ +export const actions = { + /** + * + * @param request - The request object + * @param fetch - Fetch object from sveltekit + * @param cookies - SvelteKit's cookie object + * @param locals - The local object, housing current user + * @returns Error data or redirects user to the home page or the previous page + */ + updateUser: async ({ request, fetch, cookies, locals }) => { + let formData = await request.formData(); + + const licenceCategories = formData + .getAll("licence_categories[]") + .filter((value) => typeof value === "string") + .map((value) => { + try { + return JSON.parse(value); + } catch (e) { + console.error("Failed to parse licence category:", value); + return null; + } + }) + .filter(Boolean); + + /** @type {Partial} */ + const updateData = { + id: Number(formData.get("id")), + first_name: String(formData.get("first_name")), + last_name: String(formData.get("last_name")), + email: String(formData.get("email")), + phone: String(formData.get("phone")), + notes: String(formData.get("notes")), + address: String(formData.get("address")), + zip_code: String(formData.get("zip_code")), + city: String(formData.get("city")), + date_of_birth: toRFC3339(formData.get("birth_date")), + company: String(formData.get("company")), + profile_picture: String(formData.get("profile_picture")), + membership: { + id: Number(formData.get("membership_id")), + start_date: toRFC3339(formData.get("membership_start_date")), + end_date: toRFC3339(formData.get("membership_end_date")), + status: Number(formData.get("membership_status")), + parent_member_id: Number(formData.get("parent_member_id")), + subscription_model: { + id: Number(formData.get("subscription_model_id")), + name: String(formData.get("subscription_model_name")), + }, + }, + bank_account: { + id: Number(formData.get("bank_account_id")), + mandate_date_signed: toRFC3339( + String(formData.get("mandate_date_signed")) + ), + bank: String(formData.get("bank")), + account_holder_name: String(formData.get("account_holder_name")), + iban: String(formData.get("iban")), + bic: String(formData.get("bic")), + mandate_reference: String(formData.get("mandate_reference")), + }, + licence: { + id: Number(formData.get("drivers_licence_id")), + status: Number(formData.get("licence_status")), + licence_number: String(formData.get("licence_number")), + issued_date: toRFC3339(formData.get("issued_date")), + expiration_date: toRFC3339(formData.get("expiration_date")), + country: String(formData.get("country")), + licence_categories: licenceCategories, + }, + }; + // Remove undefined or null properties + const cleanUpdateData = JSON.parse( + JSON.stringify(updateData), + (key, value) => (value !== null && value !== "" ? value : undefined) + ); + console.dir(formData); + console.dir(cleanUpdateData); + const apiURL = `${BASE_API_URI}/backend/users/update/`; + + /** @type {RequestInit} */ + const requestUpdateOptions = { + method: "PATCH", + credentials: "include", + headers: { + "Content-Type": "application/json", + Cookie: `jwt=${cookies.get("jwt")}`, + }, + body: JSON.stringify(cleanUpdateData), + }; + const res = await fetch(apiURL, requestUpdateOptions); + + if (!res.ok) { + const response = await res.json(); + const errors = formatError(response.errors); + return fail(400, { errors: errors }); + } + + const response = await res.json(); + locals.user = response; + userDatesFromRFC3339(locals.user); + throw redirect(303, `/auth/about/${response.id}`); + }, +}; diff --git a/frontend/src/routes/auth/admin/users/+page.svelte b/frontend/src/routes/auth/admin/users/+page.svelte new file mode 100644 index 0000000..48bea04 --- /dev/null +++ b/frontend/src/routes/auth/admin/users/+page.svelte @@ -0,0 +1,92 @@ + + + + +
+

{$t("user.management")}

+ +
+ + + + + + + + + + + + + {#each users as user} + + + + + + + {/each} + +
{$t("user.id")}{$t("name")}{$t("email")}{$t("status")}{$t("actions")}
{user.id}{user.first_name} {user.last_name}{user.email}{$t("userStatus." + user.status)} + + +
+ + + +