frontend: real world movement

This commit is contained in:
Alex
2025-01-16 14:23:54 +01:00
parent 66ce257198
commit 11c55a17ea
46 changed files with 1277 additions and 563 deletions

View File

@@ -7,6 +7,130 @@ import { formatError, userDatesFromRFC3339 } from "$lib/utils/helpers";
import { fail, redirect } from "@sveltejs/kit";
import { toRFC3339 } from "$lib/utils/helpers";
/**
* Converts FormData to a nested object structure
* @param {FormData} formData - The FormData object to convert
* @returns {{ user: Partial<App.Locals['user']> }} Nested object representation of the form data
*/
function formDataToObject(formData) {
/** @type { Partial<App.Locals['user']> } */
const object = {};
console.log("Form data entries:");
for (const [key, value] of formData.entries()) {
console.log("Key:", key, "Value:", value);
}
for (const [key, value] of formData.entries()) {
/** @type {string[]} */
const keys = key.match(/\[([^\]]+)\]/g)?.map((k) => k.slice(1, -1)) || [
key,
];
console.log("Processed keys:", keys);
/** @type {Record<string, any>} */
let current = object;
console.log("Current object state:", JSON.stringify(current));
for (let i = 0; i < keys.length - 1; i++) {
/**
* Create nested object if it doesn't exist
* @type {Record<string, any>}
* @description Ensures proper nesting structure for user data fields
* @example
* // For input name="user[membership][status]"
* // Creates: { user: { membership: { status: value } } }
*/
current[keys[i]] = current[keys[i]] || {};
/**
* Move to the next level of the object
* @type {Record<string, any>}
*/
current = current[keys[i]];
}
const lastKey = keys[keys.length - 1];
if (lastKey.endsWith("[]")) {
/**
* Handle array fields (licence categories)
*/
const arrayKey = lastKey.slice(0, -2);
current[arrayKey] = current[arrayKey] || [];
current[arrayKey].push(value);
} else {
current[lastKey] = value;
}
}
return { user: object };
}
/**
* Processes the raw form data into the expected user data structure
* @param {{ user: Partial<App.Locals['user']> } } rawData - The raw form data object
* @returns {{ user: Partial<App.Locals['user']> }} Processed user data
*/
function processFormData(rawData) {
/** @type {{ user: Partial<App.Locals['user']> }} */
const processedData = {
user: {
id: Number(rawData.user.id) || 0,
status: Number(rawData.user.status),
role_id: Number(rawData.user.role_id),
first_name: String(rawData.user.first_name),
last_name: String(rawData.user.last_name),
email: String(rawData.user.email),
phone: String(rawData.user.phone || ""),
company: String(rawData.user.company || ""),
date_of_birth: toRFC3339(rawData.user.date_of_birth),
address: String(rawData.user.address || ""),
zip_code: String(rawData.user.zip_code || ""),
city: String(rawData.user.city || ""),
notes: String(rawData.user.notes || ""),
profile_picture: String(rawData.user.profile_picture || ""),
membership: {
id: Number(rawData.user.membership?.id) || 0,
status: Number(rawData.user.membership?.status),
start_date: toRFC3339(rawData.user.membership?.start_date),
end_date: toRFC3339(rawData.user.membership?.end_date),
parent_member_id:
Number(rawData.user.membership?.parent_member_id) || 0,
subscription_model: {
id: Number(rawData.user.membership?.subscription_model?.id) || 0,
name: String(rawData.user.membership?.subscription_model?.name) || "",
},
},
licence: {
id: Number(rawData.user.licence?.id) || 0,
status: Number(rawData.user.licence?.status),
licence_number: String(rawData.user.licence?.licence_number || ""),
issued_date: toRFC3339(rawData.user.licence?.issued_date),
expiration_date: toRFC3339(rawData.user.licence?.expiration_date),
country: String(rawData.user.licence?.country || ""),
licence_categories: rawData.user.licence?.licence_categories || [],
},
bank_account: {
id: Number(rawData.user.bank_account?.id) || 0,
account_holder_name: String(
rawData.user.bank_account?.account_holder_name || ""
),
bank: String(rawData.user.bank_account?.bank || ""),
iban: String(rawData.user.bank_account?.iban || ""),
bic: String(rawData.user.bank_account?.bic || ""),
mandate_reference: String(
rawData.user.bank_account?.mandate_reference || ""
),
mandate_date_signed: toRFC3339(
rawData.user.bank_account?.mandate_date_signed
),
},
},
};
return processedData;
}
/** @type {import('./$types').PageServerLoad} */
export async function load({ locals, params }) {
// redirect user if not logged in
@@ -28,77 +152,24 @@ export const actions = {
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);
// Convert form data to nested object
const rawData = formDataToObject(formData);
const processedData = processFormData(rawData);
/** @type {Partial<App.Locals['user']>} */
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),
JSON.stringify(processedData),
(key, value) => (value !== null && value !== "" ? value : undefined)
);
console.dir(formData);
console.dir(cleanUpdateData);
const apiURL = `${BASE_API_URI}/backend/users/update/`;
console.dir(processedData.user.membership);
const isCreating = !processedData.user.id || processedData.user.id === 0;
console.log("Is creating: ", isCreating);
const apiURL = `${BASE_API_URI}/backend/users/update`;
/** @type {RequestInit} */
const requestUpdateOptions = {
method: "PATCH",
const requestOptions = {
method: isCreating ? "POST" : "PATCH",
credentials: "include",
headers: {
"Content-Type": "application/json",
@@ -106,7 +177,8 @@ export const actions = {
},
body: JSON.stringify(cleanUpdateData),
};
const res = await fetch(apiURL, requestUpdateOptions);
const res = await fetch(apiURL, requestOptions);
if (!res.ok) {
const response = await res.json();
@@ -115,6 +187,7 @@ export const actions = {
}
const response = await res.json();
console.log("Server success response:", response);
locals.user = response;
userDatesFromRFC3339(locals.user);
throw redirect(303, `/auth/about/${response.id}`);