about page changes
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { BASE_API_URI } from "$lib/utils/constants";
|
||||
import { formatError } from "$lib/utils/helpers";
|
||||
import { fail, redirect } from "@sveltejs/kit";
|
||||
import { toRFC3339 } from "$lib/utils/utils";
|
||||
|
||||
/** @type {import('./$types').PageServerLoad} */
|
||||
export async function load({ locals, params }) {
|
||||
@@ -21,19 +22,50 @@ export const actions = {
|
||||
* @returns Error data or redirects user to the home page or the previous page
|
||||
*/
|
||||
updateUser: async ({ request, fetch, cookies, locals }) => {
|
||||
const formData = await request.formData();
|
||||
/** @type {Record<string, string>} */
|
||||
const updateData = {};
|
||||
let formData = await request.formData();
|
||||
|
||||
// Convert FormData to a plain object
|
||||
formData.forEach((value, key) => {
|
||||
if (typeof value === "string" && value !== "") {
|
||||
updateData[key] = value;
|
||||
}
|
||||
});
|
||||
/** @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: 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")),
|
||||
},
|
||||
};
|
||||
|
||||
// Remove undefined or null properties
|
||||
const cleanUpdateData = Object.fromEntries(
|
||||
Object.entries(updateData).filter(([_, v]) => v != null)
|
||||
);
|
||||
console.dir(cleanUpdateData);
|
||||
const apiURL = `${BASE_API_URI}/backend/users/update/`;
|
||||
|
||||
const res = await fetch(apiURL, {
|
||||
method: "PATCH",
|
||||
credentials: "include",
|
||||
@@ -41,7 +73,7 @@ export const actions = {
|
||||
"Content-Type": "application/json",
|
||||
Cookie: `jwt=${cookies.get("jwt")}`,
|
||||
},
|
||||
body: JSON.stringify(updateData),
|
||||
body: JSON.stringify(cleanUpdateData),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
@@ -51,11 +83,11 @@ export const actions = {
|
||||
}
|
||||
|
||||
const response = await res.json();
|
||||
|
||||
locals.user = response;
|
||||
|
||||
// Format dates
|
||||
if (locals.user.date_of_birth) {
|
||||
locals.user.date_of_birth = response["date_of_birth"].split("T")[0];
|
||||
locals.user.date_of_birth = response.date_of_birth.split("T")[0];
|
||||
}
|
||||
if (locals.user.membership?.start_date) {
|
||||
locals.user.membership.start_date =
|
||||
@@ -65,6 +97,7 @@ export const actions = {
|
||||
locals.user.membership.end_date =
|
||||
locals.user.membership.end_date.split("T")[0];
|
||||
}
|
||||
|
||||
throw redirect(303, `/auth/about/${response.id}`);
|
||||
},
|
||||
/**
|
||||
@@ -102,6 +135,7 @@ export const actions = {
|
||||
profile_picture: response[""],
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param request - The request object
|
||||
|
||||
@@ -11,7 +11,14 @@
|
||||
import { t } from "svelte-i18n";
|
||||
import { fly } from "svelte/transition";
|
||||
|
||||
$: ({ user } = $page.data);
|
||||
/** @type {import('./$types').ActionData} */
|
||||
export let form;
|
||||
|
||||
/** @type {App.Locals['subscriptions']}*/
|
||||
$: subscriptions = $page.data.subscriptions;
|
||||
|
||||
/** @type {App.Locals['user']}*/
|
||||
$: user = $page.data.user;
|
||||
|
||||
/** @typedef {{name: string, src: string}} Avatar */
|
||||
const avatarFiles = import.meta.glob("$lib/img/Avatar-*.jpeg", {
|
||||
@@ -20,11 +27,38 @@
|
||||
/** @type{Avatar[]} */
|
||||
let avatars = [];
|
||||
|
||||
const TABS = ["profile", "membership", "bankaccount"];
|
||||
const TABS = ["profile", "licence", "membership", "bankaccount"];
|
||||
let activeTab = TABS[0];
|
||||
|
||||
/** @type{string[]} */
|
||||
let errorMessages = [];
|
||||
$: subscriptionModelOptions = subscriptions.map((sub) => ({
|
||||
value: sub?.name ?? "",
|
||||
label: sub?.name ?? "",
|
||||
}));
|
||||
|
||||
const userStatusOptions = [
|
||||
{ value: 1, label: $t("userStatus.1"), color: "#b1b1b1" }, // Grey for "Nicht verifiziert"
|
||||
{ value: 2, label: $t("userStatus.2"), color: "#90EE90" }, // Light green for "Verifiziert"
|
||||
{ value: 3, label: $t("userStatus.3"), color: "#00bc00" }, // Green for "Aktiv"
|
||||
{ value: 4, label: $t("userStatus.4"), color: "#FFC0CB" }, // Pink for "Passiv"
|
||||
{ value: 5, label: $t("userStatus.5"), color: "#FF4646" }, // Red for "Deaktiviert"
|
||||
];
|
||||
|
||||
const userRoleOptions = [
|
||||
{ value: 0, label: $t("userRole.0"), color: "#b1b1b1" }, // Grey for "Mitglied"
|
||||
{ value: 1, label: $t("userRole.1"), color: "#00bc00" }, // Green for "Betrachter"
|
||||
{ value: 4, label: $t("userRole.4"), color: "#FFC0CB" }, // Pink for "Bearbeiter"
|
||||
{ value: 8, label: $t("userRole.8"), color: "#FF4646" }, // Red for "Admin"
|
||||
];
|
||||
const membershipStatusOptions = [
|
||||
{ value: 3, label: $t("userStatus.3"), color: "#00bc00" }, // Green for "Aktiv"
|
||||
{ value: 4, label: $t("userStatus.4"), color: "#FFC0CB" }, // Pink for "Passiv"
|
||||
{ value: 5, label: $t("userStatus.5"), color: "#FF4646" }, // Red for "Deaktiviert"
|
||||
];
|
||||
const licenceStatusOptions = [
|
||||
{ value: 3, label: $t("userStatus.3"), color: "#00bc00" }, // Green for "Aktiv"
|
||||
{ value: 4, label: $t("userStatus.4"), color: "#FFC0CB" }, // Pink for "Passiv"
|
||||
{ value: 5, label: $t("userStatus.5"), color: "#FF4646" }, // Red for "Deaktiviert"
|
||||
];
|
||||
|
||||
let showModal = false,
|
||||
isUploading = false,
|
||||
@@ -37,7 +71,13 @@
|
||||
const close = () => (showModal = false);
|
||||
const toggleAvatars = () => (showAvatars = !showAvatars);
|
||||
|
||||
$: selectedSubscriptionModel =
|
||||
subscriptions.find(
|
||||
(sub) => sub?.id === user.membership?.subscription_model.id
|
||||
) || null;
|
||||
|
||||
onMount(() => {
|
||||
console.dir(user);
|
||||
avatars = Object.entries(avatarFiles).map(([path, module]) => {
|
||||
if (typeof path !== "string") {
|
||||
throw new Error("Unexpected non-string path");
|
||||
@@ -60,9 +100,6 @@
|
||||
});
|
||||
});
|
||||
|
||||
/** @type {import('./$types').ActionData} */
|
||||
export let form;
|
||||
|
||||
/**
|
||||
* Sets the active tab
|
||||
* @param {string} tab - The tab to set as active
|
||||
@@ -73,28 +110,11 @@
|
||||
|
||||
/** @type {import('./$types').SubmitFunction} */
|
||||
const handleUpdate = async ({ form, formData, action, cancel }) => {
|
||||
errorMessages = [];
|
||||
const errorElements = form.querySelectorAll(".error-message");
|
||||
|
||||
errorElements.forEach((element) => {
|
||||
if (element.textContent) {
|
||||
errorMessages.push(element.textContent);
|
||||
}
|
||||
});
|
||||
|
||||
if (errorMessages.length > 0) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
errorMessages = [];
|
||||
isUpdating = true;
|
||||
return async ({ result }) => {
|
||||
isUpdating = false;
|
||||
if (result.type === "success" || result.type === "redirect") {
|
||||
close();
|
||||
} else if (result.type == "failure" && result.data?.errors) {
|
||||
errorMessages = result.data.errors.map((error) => error.error);
|
||||
}
|
||||
await applyAction(result);
|
||||
};
|
||||
@@ -164,10 +184,10 @@
|
||||
<span class="value">{user.phone}</span>
|
||||
</h3>
|
||||
{/if}
|
||||
{#if user.birth_date}
|
||||
{#if user.date_of_birth}
|
||||
<h3 class="hero-subtitle subtitle info-row">
|
||||
<span class="label">Geburtstag:</span>
|
||||
<span class="value">{user.birth_date}</span>
|
||||
<span class="value">{user.date_of_birth}</span>
|
||||
</h3>
|
||||
{/if}
|
||||
{#if user.notes}
|
||||
@@ -256,6 +276,7 @@
|
||||
method="POST"
|
||||
use:enhance={handleUpdate}
|
||||
>
|
||||
<input name="id" type="number" hidden bind:value={user.id} />
|
||||
<h1 class="step-title" style="text-align: center;">{$t("user_edit")}</h1>
|
||||
{#if form?.success}
|
||||
<h4
|
||||
@@ -298,13 +319,31 @@
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{#if activeTab == "profile"}
|
||||
<div class="tab-content">
|
||||
<div
|
||||
class="tab-content"
|
||||
style="display: {activeTab === 'profile' ? 'block' : 'none'}"
|
||||
>
|
||||
<InputField
|
||||
name="status"
|
||||
type="select"
|
||||
label={$t("status")}
|
||||
bind:value={user.status}
|
||||
options={userStatusOptions}
|
||||
/>
|
||||
{#if user.role_id === 8}
|
||||
<InputField
|
||||
name="role_id"
|
||||
type="select"
|
||||
label={$t("user_role")}
|
||||
bind:value={user.role_id}
|
||||
options={userRoleOptions}
|
||||
/>
|
||||
{/if}
|
||||
<InputField
|
||||
name="password"
|
||||
type="password"
|
||||
label={$t("password")}
|
||||
placeholder={$t("placeholder_password")}
|
||||
placeholder={$t("placeholder.password")}
|
||||
bind:value={password}
|
||||
otherPasswordValue={password2}
|
||||
/>
|
||||
@@ -312,136 +351,206 @@
|
||||
name="password2"
|
||||
type="password"
|
||||
label={$t("password_repeat")}
|
||||
placeholder={$t("placeholder_password")}
|
||||
placeholder={$t("placeholder.password")}
|
||||
bind:value={password2}
|
||||
otherPasswordValue={password}
|
||||
/>
|
||||
<InputField
|
||||
name="first_name"
|
||||
label={$t("first_name")}
|
||||
value={user.first_name}
|
||||
placeholder={$t("placeholder_first_name")}
|
||||
bind:value={user.first_name}
|
||||
placeholder={$t("placeholder.first_name")}
|
||||
required={true}
|
||||
/>
|
||||
<InputField
|
||||
name="last_name"
|
||||
label={$t("last_name")}
|
||||
value={user.last_name}
|
||||
placeholder={$t("placeholder_last_name")}
|
||||
bind:value={user.last_name}
|
||||
placeholder={$t("placeholder.last_name")}
|
||||
required={true}
|
||||
/>
|
||||
<InputField
|
||||
name="company"
|
||||
label={$t("company")}
|
||||
bind:value={user.company}
|
||||
placeholder={$t("placeholder.company")}
|
||||
/>
|
||||
<InputField
|
||||
name="email"
|
||||
type="email"
|
||||
label={$t("email")}
|
||||
value={user.email}
|
||||
placeholder={$t("placeholder_email")}
|
||||
bind:value={user.email}
|
||||
placeholder={$t("placeholder.email")}
|
||||
required={true}
|
||||
/>
|
||||
<InputField
|
||||
name="phone"
|
||||
type="tel"
|
||||
label={$t("phone")}
|
||||
value={user.phone || ""}
|
||||
placeholder={$t("placeholder_phone")}
|
||||
bind:value={user.phone}
|
||||
placeholder={$t("placeholder.phone")}
|
||||
/>
|
||||
<InputField
|
||||
name="birth_date"
|
||||
type="date"
|
||||
label={$t("birth_date")}
|
||||
value={user.birth_date || ""}
|
||||
placeholder={$t("placeholder_birth_date")}
|
||||
bind:value={user.date_of_birth}
|
||||
placeholder={$t("placeholder.birth_date")}
|
||||
/>
|
||||
<InputField
|
||||
name="address"
|
||||
label={$t("address")}
|
||||
value={user.address || ""}
|
||||
placeholder={$t("placeholder_address")}
|
||||
bind:value={user.address}
|
||||
placeholder={$t("placeholder.address")}
|
||||
/>
|
||||
<InputField
|
||||
name="zip_code"
|
||||
label={$t("zip_code")}
|
||||
value={user.zip_code || ""}
|
||||
placeholder={$t("placeholder_zip_code")}
|
||||
bind:value={user.zip_code}
|
||||
placeholder={$t("placeholder.zip_code")}
|
||||
/>
|
||||
<InputField
|
||||
name="city"
|
||||
label={$t("city")}
|
||||
value={user.city || ""}
|
||||
placeholder={$t("placeholder_city")}
|
||||
bind:value={user.city}
|
||||
placeholder={$t("placeholder.city")}
|
||||
/>
|
||||
<InputField
|
||||
name="notes"
|
||||
type="textarea"
|
||||
label={$t("notes")}
|
||||
bind:value={user.notes}
|
||||
placeholder={$t("placeholder.notes", {
|
||||
values: { name: user.first_name || "" },
|
||||
})}
|
||||
rows={10}
|
||||
/>
|
||||
</div>
|
||||
{:else if activeTab == "membership"}
|
||||
<div class="tab-content">
|
||||
<div
|
||||
class="tab-content"
|
||||
style="display: {activeTab === 'licence' ? 'block' : 'none'}"
|
||||
>
|
||||
<InputField
|
||||
name="licence_status"
|
||||
type="select"
|
||||
label={$t("status")}
|
||||
bind:value={user.drivers_licence.status}
|
||||
options={licenceStatusOptions}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tab-content"
|
||||
style="display: {activeTab === 'membership' ? 'block' : 'none'}"
|
||||
>
|
||||
<InputField
|
||||
name="membership_status"
|
||||
type="select"
|
||||
label={$t("status")}
|
||||
value={user.membership?.status || ""}
|
||||
bind:value={user.membership.status}
|
||||
options={membershipStatusOptions}
|
||||
/>
|
||||
<InputField
|
||||
name="subscription_model_name"
|
||||
type="select"
|
||||
label={$t("subscription_model")}
|
||||
bind:value={user.membership.subscription_model.name}
|
||||
options={subscriptionModelOptions}
|
||||
/>
|
||||
<div class="subscription-info">
|
||||
<div class="subscription-column">
|
||||
<p>
|
||||
<strong>{$t("monthly_fee")}:</strong>
|
||||
{selectedSubscriptionModel?.monthly_fee || "-"}
|
||||
</p>
|
||||
<p>
|
||||
<strong>{$t("hourly_rate")}:</strong>
|
||||
{selectedSubscriptionModel?.hourly_rate || "-"}
|
||||
</p>
|
||||
{#if selectedSubscriptionModel?.included_hours_per_year}
|
||||
<p>
|
||||
<strong>{$t("included_hours_per_year")}:</strong>
|
||||
{selectedSubscriptionModel?.included_hours_per_year}
|
||||
</p>
|
||||
{/if}
|
||||
{#if selectedSubscriptionModel?.included_hours_per_month}
|
||||
<p>
|
||||
<strong>{$t("included_hours_per_month")}:</strong>
|
||||
{selectedSubscriptionModel?.included_hours_per_month}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="subscription-column">
|
||||
<p>
|
||||
<strong>{$t("details")}:</strong>
|
||||
{selectedSubscriptionModel?.details || "-"}
|
||||
</p>
|
||||
{#if selectedSubscriptionModel?.conditions}
|
||||
<p>
|
||||
<strong>{$t("conditions")}:</strong>
|
||||
{selectedSubscriptionModel?.conditions}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<InputField
|
||||
name="membership_start_date"
|
||||
type="date"
|
||||
label={$t("start")}
|
||||
value={user.membership?.start_date || ""}
|
||||
placeholder={$t("placeholder_start_date")}
|
||||
bind:value={user.membership.start_date}
|
||||
placeholder={$t("placeholder.start_date")}
|
||||
/>
|
||||
<InputField
|
||||
name="membership_end_date"
|
||||
type="date"
|
||||
label={$t("end")}
|
||||
value={user.membership?.end_date || ""}
|
||||
placeholder={$t("placeholder_end_date")}
|
||||
bind:value={user.membership.end_date}
|
||||
placeholder={$t("placeholder.end_date")}
|
||||
/>
|
||||
<InputField
|
||||
name="parent_member_id"
|
||||
type="number"
|
||||
label={$t("parent_member_id")}
|
||||
value={user.membership?.parent_member_id || ""}
|
||||
placeholder={$t("placeholder_parent_member_id")}
|
||||
bind:value={user.membership.parent_member_id}
|
||||
placeholder={$t("placeholder.parent_member_id")}
|
||||
/>
|
||||
</div>
|
||||
{:else if activeTab == "bankaccount"}
|
||||
<div class="tab-content">
|
||||
<div
|
||||
class="tab-content"
|
||||
style="display: {activeTab === 'bankaccount' ? 'block' : 'none'}"
|
||||
>
|
||||
<InputField
|
||||
name="account_holder_name"
|
||||
label={$t("bank_account_holder")}
|
||||
value={user.bank_account?.account_holder_name || ""}
|
||||
placeholder={$t("placeholder_bank_account_holder")}
|
||||
bind:value={user.bank_account.account_holder_name}
|
||||
placeholder={$t("placeholder.bank_account_holder")}
|
||||
/>
|
||||
<InputField
|
||||
name="bank"
|
||||
label={$t("bank_name")}
|
||||
value={user.bank_account?.bank || ""}
|
||||
placeholder={$t("placeholder_bank_name")}
|
||||
bind:value={user.bank_account.bank}
|
||||
placeholder={$t("placeholder.bank_name")}
|
||||
/>
|
||||
<InputField
|
||||
name="iban"
|
||||
label={$t("iban")}
|
||||
value={user.bank_account?.iban || ""}
|
||||
placeholder={$t("placeholder_iban")}
|
||||
bind:value={user.bank_account.iban}
|
||||
placeholder={$t("placeholder.iban")}
|
||||
toUpperCase={true}
|
||||
/>
|
||||
<InputField
|
||||
name="bic"
|
||||
label={$t("bic")}
|
||||
value={user.bank_account?.bic || ""}
|
||||
placeholder={$t("placeholder_bic")}
|
||||
bind:value={user.bank_account.bic}
|
||||
placeholder={$t("placeholder.bic")}
|
||||
toUpperCase={true}
|
||||
/>
|
||||
<InputField
|
||||
name="mandate_reference"
|
||||
label={$t("mandate_reference")}
|
||||
value={user.bank_account?.mandate_reference || ""}
|
||||
placeholder={$t("placeholder_mandate_reference")}
|
||||
bind:value={user.bank_account.mandate_reference}
|
||||
placeholder={$t("placeholder.mandate_reference")}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if errorMessages.length > 0}
|
||||
<div class="error-container">
|
||||
{#each errorMessages as message, i (i)}
|
||||
<p class="error-message" transition:fly={{ y: -20, duration: 300 }}>
|
||||
{message}
|
||||
</p>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="button-container">
|
||||
{#if isUpdating}
|
||||
<SmallLoader width={30} message={"Aktualisiere..."} />
|
||||
@@ -457,18 +566,26 @@
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.error-container {
|
||||
background-color: #fee2e2;
|
||||
border: 1px solid #f87171;
|
||||
border-radius: 4px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
.subscription-info {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #dc2626;
|
||||
.subscription-column {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.subscription-column p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.subscription-column strong {
|
||||
display: inline-block;
|
||||
min-width: 100px;
|
||||
}
|
||||
.tab-content {
|
||||
padding: 1rem;
|
||||
|
||||
Reference in New Issue
Block a user