styling, DateOfBirth corrected
This commit is contained in:
126
frontend/src/app.d.ts
vendored
126
frontend/src/app.d.ts
vendored
@@ -1,87 +1,87 @@
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
|
||||
interface Subscription {
|
||||
id: number | -1;
|
||||
name: string | "";
|
||||
details?: string | "";
|
||||
conditions?: string | "";
|
||||
monthly_fee?: number | -1;
|
||||
hourly_rate?: number | -1;
|
||||
included_hours_per_year?: number | 0;
|
||||
included_hours_per_month?: number | 0;
|
||||
id: number | -1;
|
||||
name: string | '';
|
||||
details?: string | '';
|
||||
conditions?: string | '';
|
||||
monthly_fee?: number | -1;
|
||||
hourly_rate?: number | -1;
|
||||
included_hours_per_year?: number | 0;
|
||||
included_hours_per_month?: number | 0;
|
||||
}
|
||||
|
||||
interface Membership {
|
||||
id: number | -1;
|
||||
status: number | -1;
|
||||
start_date: string | "";
|
||||
end_date: string | "";
|
||||
parent_member_id: number | -1;
|
||||
subscription_model: Subscription;
|
||||
id: number | -1;
|
||||
status: number | -1;
|
||||
start_date: string | '';
|
||||
end_date: string | '';
|
||||
parent_member_id: number | -1;
|
||||
subscription_model: Subscription;
|
||||
}
|
||||
|
||||
interface BankAccount {
|
||||
id: number | -1;
|
||||
mandate_date_signed: string | "";
|
||||
bank: string | "";
|
||||
account_holder_name: string | "";
|
||||
iban: string | "";
|
||||
bic: string | "";
|
||||
mandate_reference: string | "";
|
||||
id: number | -1;
|
||||
mandate_date_signed: string | '';
|
||||
bank: string | '';
|
||||
account_holder_name: string | '';
|
||||
iban: string | '';
|
||||
bic: string | '';
|
||||
mandate_reference: string | '';
|
||||
}
|
||||
|
||||
interface Licence {
|
||||
id: number | -1;
|
||||
status: number | -1;
|
||||
licence_number: string | "";
|
||||
issued_date: string | "";
|
||||
expiration_date: string | "";
|
||||
country: string | "";
|
||||
licence_categories: LicenceCategory[];
|
||||
id: number | -1;
|
||||
status: number | -1;
|
||||
licence_number: string | '';
|
||||
issued_date: string | '';
|
||||
expiration_date: string | '';
|
||||
country: string | '';
|
||||
licence_categories: LicenceCategory[];
|
||||
}
|
||||
|
||||
interface LicenceCategory {
|
||||
id: number | -1;
|
||||
category: string | "";
|
||||
id: number | -1;
|
||||
category: string | '';
|
||||
}
|
||||
|
||||
interface User {
|
||||
email: string | "";
|
||||
first_name: string | "";
|
||||
last_name: string | "";
|
||||
phone: string | "";
|
||||
notes: string | "";
|
||||
address: string | "";
|
||||
zip_code: string | "";
|
||||
city: string | "";
|
||||
status: number | -1;
|
||||
id: number | -1;
|
||||
role_id: number | -1;
|
||||
date_of_birth: string | "";
|
||||
company: string | "";
|
||||
profile_picture: string | "";
|
||||
payment_status: number | -1;
|
||||
membership: Membership;
|
||||
bank_account: BankAccount;
|
||||
licence: Licence;
|
||||
notes: string | "";
|
||||
email: string | '';
|
||||
first_name: string | '';
|
||||
last_name: string | '';
|
||||
phone: string | '';
|
||||
notes: string | '';
|
||||
address: string | '';
|
||||
zip_code: string | '';
|
||||
city: string | '';
|
||||
status: number | -1;
|
||||
id: number | -1;
|
||||
role_id: number | -1;
|
||||
dateofbirth: string | '';
|
||||
company: string | '';
|
||||
profile_picture: string | '';
|
||||
payment_status: number | -1;
|
||||
membership: Membership;
|
||||
bank_account: BankAccount;
|
||||
licence: Licence;
|
||||
notes: string | '';
|
||||
}
|
||||
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
interface Locals {
|
||||
user: User;
|
||||
users: User[];
|
||||
subscriptions: Subscription[];
|
||||
licence_categories: LicenceCategory[];
|
||||
}
|
||||
interface Types {
|
||||
licenceCategory: LicenceCategory;
|
||||
}
|
||||
// interface PageData {}
|
||||
// interface Platform {}
|
||||
}
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
interface Locals {
|
||||
user: User;
|
||||
users: User[];
|
||||
subscriptions: Subscription[];
|
||||
licence_categories: LicenceCategory[];
|
||||
}
|
||||
interface Types {
|
||||
licenceCategory: LicenceCategory;
|
||||
}
|
||||
// interface PageData {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
|
||||
@@ -283,7 +283,6 @@
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
input,
|
||||
textarea,
|
||||
@@ -316,7 +315,6 @@
|
||||
}
|
||||
/* Add consistent spacing between input boxes */
|
||||
.input-box {
|
||||
margin: 1rem 0;
|
||||
padding: 0.5rem;
|
||||
background-color: var(--surface0);
|
||||
border-radius: 6px;
|
||||
|
||||
@@ -28,28 +28,6 @@
|
||||
|
||||
<div class="modal-background">
|
||||
<div transition:modal|global={{ duration: 1000 }} class="modal" role="dialog" aria-modal="true">
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<a
|
||||
title={$t('cancel')}
|
||||
class="modal-close"
|
||||
on:click={closeModal}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:keydown={(e) => e.key == 'Enter' && closeModal()}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 384 512"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"
|
||||
/>
|
||||
</svg>
|
||||
<span class="sr-only">{$t('cancel')}</span>
|
||||
</a>
|
||||
<div class="container">
|
||||
<slot />
|
||||
</div>
|
||||
@@ -64,7 +42,7 @@
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(30, 30, 46, 0.65); /* var(--base) with 0.75 opacity */
|
||||
background: var(--modal-backdrop); /* var(--base) with 0.75 opacity */
|
||||
backdrop-filter: blur(4px); /* Optional: adds a slight blur effect */
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
@@ -149,7 +127,6 @@
|
||||
.modal .container {
|
||||
flex-direction: column;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
zip_code: '',
|
||||
city: '',
|
||||
company: '',
|
||||
date_of_birth: '',
|
||||
dateofbirth: '',
|
||||
notes: '',
|
||||
profile_picture: '',
|
||||
payment_status: 0,
|
||||
@@ -297,11 +297,11 @@
|
||||
placeholder={$t('placeholder.phone')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[date_of_birth]"
|
||||
name="user[dateofbirth]"
|
||||
type="date"
|
||||
label={$t('date_of_birth')}
|
||||
bind:value={localUser.date_of_birth}
|
||||
placeholder={$t('placeholder.date_of_birth')}
|
||||
label={$t('dateofbirth')}
|
||||
bind:value={localUser.dateofbirth}
|
||||
placeholder={$t('placeholder.dateofbirth')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[address]"
|
||||
@@ -529,7 +529,7 @@
|
||||
.category-break {
|
||||
grid-column: 1 / -1;
|
||||
height: 1px;
|
||||
background-color: var(--surface0);
|
||||
background-color: var(--overlay0);
|
||||
margin-top: 10px;
|
||||
margin-left: 20%;
|
||||
width: 60%;
|
||||
|
||||
3
frontend/src/lib/css/styles.min.css
vendored
3
frontend/src/lib/css/styles.min.css
vendored
@@ -25,6 +25,7 @@
|
||||
--base: #1e1e2e;
|
||||
--mantle: #181825;
|
||||
--crust: #11111b;
|
||||
--modal-backdrop: rgba(49, 50, 68, 0.45); /* For Mocha theme */
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -385,8 +386,6 @@ li strong {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
@@ -1,179 +1,176 @@
|
||||
export default {
|
||||
userStatus: {
|
||||
1: "Nicht verifiziert",
|
||||
2: "Verifiziert",
|
||||
3: "Aktiv",
|
||||
4: "Passiv",
|
||||
5: "Deaktiviert",
|
||||
},
|
||||
userRole: {
|
||||
0: "Mitglied",
|
||||
1: "Betrachter",
|
||||
4: "Bearbeiter",
|
||||
8: "Administrator",
|
||||
},
|
||||
placeholder: {
|
||||
password: "Passwort eingeben...",
|
||||
email: "Emailadresse eingeben...",
|
||||
company: "Firmennamen eingeben...",
|
||||
first_name: "Vornamen eingeben...",
|
||||
last_name: "Nachnamen eingeben...",
|
||||
phone: "Telefonnummer eingeben...",
|
||||
address: "Straße und Hausnummer eingeben...",
|
||||
zip_code: "Postleitzahl eingeben...",
|
||||
city: "Wohnort eingeben...",
|
||||
bank_name: "Namen der Bank eingeben...",
|
||||
parent_member_id: "Mitgliedsnr des Hauptmitglieds eingeben...",
|
||||
bank_account_holder: "Namen eingeben...",
|
||||
iban: "IBAN eingeben..",
|
||||
bic: "BIC eingeben(Bei nicht deutschen Konten)...",
|
||||
mandate_reference: "SEPA Mandatsreferenz eingeben..",
|
||||
notes: "Deine Notizen zu {name}...",
|
||||
licence_number: "Auf dem Führerschein unter Feld 5",
|
||||
issued_date: "Ausgabedatum unter Feld 4a",
|
||||
expiration_date: "Ablaufdatum unter Feld 4b",
|
||||
issuing_country: "Ausstellendes Land",
|
||||
},
|
||||
validation: {
|
||||
required: "Eingabe benötigt",
|
||||
password: "Password zu kurz, mindestens 8 Zeichen",
|
||||
password_match: "Passwörter stimmen nicht überein!",
|
||||
phone: "Ungültiges Format(+491738762387 oder 0173850698)",
|
||||
zip_code: "Ungültige Postleitzahl(Nur deutsche Wohnorte sind zulässig)",
|
||||
bic: "Ungültige BIC",
|
||||
iban: "Ungültige IBAN",
|
||||
date: "Bitte geben Sie ein Datum ein",
|
||||
email: "Ungültige Emailadresse",
|
||||
licence: "Nummer zu kurz(11 Zeichen)",
|
||||
},
|
||||
server: {
|
||||
error: {
|
||||
invalid_json: "JSON Daten sind ungültig",
|
||||
no_auth_token: "Nicht authorisiert, fehlender oder ungültiger Auth-Token",
|
||||
jwt_parsing_error:
|
||||
"Nicht authorisiert, Auth-Token konnte nicht gelesen werden",
|
||||
unauthorized: "Sie sind nicht befugt diese Handlung durchzuführen",
|
||||
internal_server_error:
|
||||
"Verdammt, Fehler auf unserer Seite, probieren Sie es nochmal, danach rufen Sie jemanden vom Verein an.",
|
||||
},
|
||||
validation: {
|
||||
invalid_user_id: "Nutzer ID ungültig",
|
||||
invalid_subscription_model: "Model nicht gefunden",
|
||||
user_not_found: "{field} konnte nicht gefunden werden",
|
||||
invalid_user_data: "Nutzerdaten ungültig",
|
||||
user_not_found_or_wrong_password:
|
||||
"Existiert nicht oder falsches Passwort",
|
||||
email_already_registered:
|
||||
"Ein Mitglied wurde schon mit dieser Emailadresse erstellt.",
|
||||
alphanumunicode: "beinhaltet nicht erlaubte Zeichen",
|
||||
safe_content: "I see what you did there! Do not cross this line!",
|
||||
iban: "Ungültig. Format: DE07123412341234123412",
|
||||
bic: "Ungültig. Format: BELADEBEXXX",
|
||||
email: "Format ungültig",
|
||||
number: "Ist keine Nummer",
|
||||
euDriversLicence: "Ist kein europäischer Führerschein",
|
||||
lte: "Ist zu groß/neu",
|
||||
gt: "Ist zu klein/alt",
|
||||
required: "Feld wird benötigt",
|
||||
image: "Dies ist kein Bild",
|
||||
alphanum: "beinhaltet ungültige Zeichen",
|
||||
alphaunicode: "darf nur aus Buchstaben bestehen",
|
||||
},
|
||||
},
|
||||
licenceCategory: {
|
||||
AM: "Mopeds und leichte vierrädrige Kraftfahrzeuge (50ccm, max 45km/h)",
|
||||
A1: "Leichte Motorräder (125ccm)",
|
||||
A2: "Motorräder mit mittlerer Leistung (max 35kW)",
|
||||
A: "Motorräder",
|
||||
B: "Kraftfahrzeuge ≤ 3500 kg, ≤ 8 Sitzplätze",
|
||||
C1: "Mittelschwere Fahrzeuge -7500 kg",
|
||||
C: "Schwere Nutzfahrzeuge > 3500 kg",
|
||||
D1: "Kleinbusse 9-16 Sitzplätze",
|
||||
D: "Busse > 8 Sitzplätze",
|
||||
BE: "Fahrzeugklasse B mit Anhänger",
|
||||
C1E: "Fahrzeugklasse C1 mit Anhänger",
|
||||
CE: "Fahrzeugklasse C mit Anhänger",
|
||||
D1E: "Fahrzeugklasse D1 mit Anhänger",
|
||||
DE: "Fahrzeugklasse D mit Anhänger",
|
||||
L: "Land-, Forstwirtschaftsfahrzeuge, Stapler max 40km/h",
|
||||
T: "Land-, Forstwirtschaftsfahrzeuge, Stapler max 60km/h",
|
||||
},
|
||||
users: "Mitglieder",
|
||||
user: {
|
||||
login: "Nutzer Anmeldung",
|
||||
edit: "Nutzer bearbeiten",
|
||||
user: "Nutzer",
|
||||
management: "Mitgliederverwaltung",
|
||||
id: "Mitgliedsnr",
|
||||
name: "Name",
|
||||
email: "Email",
|
||||
status: "Status",
|
||||
role: "Nutzerrolle",
|
||||
},
|
||||
cancel: "Abbrechen",
|
||||
confirm: "Bestätigen",
|
||||
actions: "Aktionen",
|
||||
edit: "Bearbeiten",
|
||||
delete: "Löschen",
|
||||
mandate_date_signed: "Mandatserteilungsdatum",
|
||||
licence_categories: "Führerscheinklassen",
|
||||
subscription_model: "Mitgliedschatfsmodell",
|
||||
licence: "Führerschein",
|
||||
licence_number: "Führerscheinnummer",
|
||||
issued_date: "Ausgabedatum",
|
||||
expiration_date: "Ablaufdatum",
|
||||
country: "Land",
|
||||
monthly_fee: "Monatliche Gebühr",
|
||||
hourly_rate: "Stundensatz",
|
||||
details: "Details",
|
||||
conditions: "Bedingungen",
|
||||
unknown: "Unbekannt",
|
||||
notes: "Notizen",
|
||||
address: "Straße & Hausnummer",
|
||||
city: "Wohnort",
|
||||
zip_code: "PLZ",
|
||||
forgot_password: "Passwort vergessen?",
|
||||
password: "Passwort",
|
||||
password_repeat: "Passwort wiederholen",
|
||||
email: "Email",
|
||||
company: "Firma",
|
||||
login: "Anmeldung",
|
||||
profile: "Profil",
|
||||
membership: "Mitgliedschaft",
|
||||
bankaccount: "Kontodaten",
|
||||
first_name: "Vorname",
|
||||
last_name: "Nachname",
|
||||
name: "Name",
|
||||
phone: "Telefonnummer",
|
||||
date_of_birth: "Geburtstag",
|
||||
status: "Status",
|
||||
start: "Beginn",
|
||||
end: "Ende",
|
||||
parent_member_id: "Hauptmitgliedsnr.",
|
||||
bank_account_holder: "Kontoinhaber",
|
||||
bank_name: "Bank",
|
||||
iban: "IBAN",
|
||||
bic: "BIC",
|
||||
mandate_reference: "SEPA Mandat",
|
||||
subscriptions: "Tarifmodelle",
|
||||
payments: "Zahlungen",
|
||||
add_new: "Neu",
|
||||
included_hours_per_year: "Inkludierte Stunden pro Jahr",
|
||||
included_hours_per_month: "Inkludierte Stunden pro Monat",
|
||||
userStatus: {
|
||||
1: 'Nicht verifiziert',
|
||||
2: 'Verifiziert',
|
||||
3: 'Aktiv',
|
||||
4: 'Passiv',
|
||||
5: 'Deaktiviert'
|
||||
},
|
||||
userRole: {
|
||||
0: 'Mitglied',
|
||||
1: 'Betrachter',
|
||||
4: 'Bearbeiter',
|
||||
8: 'Administrator'
|
||||
},
|
||||
placeholder: {
|
||||
password: 'Passwort eingeben...',
|
||||
email: 'Emailadresse eingeben...',
|
||||
company: 'Firmennamen eingeben...',
|
||||
first_name: 'Vornamen eingeben...',
|
||||
last_name: 'Nachnamen eingeben...',
|
||||
phone: 'Telefonnummer eingeben...',
|
||||
address: 'Straße und Hausnummer eingeben...',
|
||||
zip_code: 'Postleitzahl eingeben...',
|
||||
city: 'Wohnort eingeben...',
|
||||
bank_name: 'Namen der Bank eingeben...',
|
||||
parent_member_id: 'Mitgliedsnr des Hauptmitglieds eingeben...',
|
||||
bank_account_holder: 'Namen eingeben...',
|
||||
iban: 'IBAN eingeben..',
|
||||
bic: 'BIC eingeben(Bei nicht deutschen Konten)...',
|
||||
mandate_reference: 'SEPA Mandatsreferenz eingeben..',
|
||||
notes: 'Deine Notizen zu {name}...',
|
||||
licence_number: 'Auf dem Führerschein unter Feld 5',
|
||||
issued_date: 'Ausgabedatum unter Feld 4a',
|
||||
expiration_date: 'Ablaufdatum unter Feld 4b',
|
||||
issuing_country: 'Ausstellendes Land'
|
||||
},
|
||||
validation: {
|
||||
required: 'Eingabe benötigt',
|
||||
password: 'Password zu kurz, mindestens 8 Zeichen',
|
||||
password_match: 'Passwörter stimmen nicht überein!',
|
||||
phone: 'Ungültiges Format(+491738762387 oder 0173850698)',
|
||||
zip_code: 'Ungültige Postleitzahl(Nur deutsche Wohnorte sind zulässig)',
|
||||
bic: 'Ungültige BIC',
|
||||
iban: 'Ungültige IBAN',
|
||||
date: 'Bitte geben Sie ein Datum ein',
|
||||
email: 'Ungültige Emailadresse',
|
||||
licence: 'Nummer zu kurz(11 Zeichen)'
|
||||
},
|
||||
server: {
|
||||
error: {
|
||||
invalid_json: 'JSON Daten sind ungültig',
|
||||
no_auth_token: 'Nicht authorisiert, fehlender oder ungültiger Auth-Token',
|
||||
jwt_parsing_error: 'Nicht authorisiert, Auth-Token konnte nicht gelesen werden',
|
||||
unauthorized: 'Sie sind nicht befugt diese Handlung durchzuführen',
|
||||
internal_server_error:
|
||||
'Verdammt, Fehler auf unserer Seite, probieren Sie es nochmal, danach rufen Sie jemanden vom Verein an.'
|
||||
},
|
||||
validation: {
|
||||
invalid_user_id: 'Nutzer ID ungültig',
|
||||
invalid_subscription_model: 'Model nicht gefunden',
|
||||
user_not_found: '{field} konnte nicht gefunden werden',
|
||||
invalid_user_data: 'Nutzerdaten ungültig',
|
||||
user_not_found_or_wrong_password: 'Existiert nicht oder falsches Passwort',
|
||||
email_already_registered: 'Ein Mitglied wurde schon mit dieser Emailadresse erstellt.',
|
||||
alphanumunicode: 'beinhaltet nicht erlaubte Zeichen',
|
||||
safe_content: 'I see what you did there! Do not cross this line!',
|
||||
iban: 'Ungültig. Format: DE07123412341234123412',
|
||||
bic: 'Ungültig. Format: BELADEBEXXX',
|
||||
email: 'Format ungültig',
|
||||
number: 'Ist keine Nummer',
|
||||
euDriversLicence: 'Ist kein europäischer Führerschein',
|
||||
lte: 'Ist zu groß/neu',
|
||||
gt: 'Ist zu klein/alt',
|
||||
required: 'Feld wird benötigt',
|
||||
image: 'Dies ist kein Bild',
|
||||
alphanum: 'beinhaltet ungültige Zeichen',
|
||||
alphaunicode: 'darf nur aus Buchstaben bestehen'
|
||||
}
|
||||
},
|
||||
licenceCategory: {
|
||||
AM: 'Mopeds und leichte vierrädrige Kraftfahrzeuge (50ccm, max 45km/h)',
|
||||
A1: 'Leichte Motorräder (125ccm)',
|
||||
A2: 'Motorräder mit mittlerer Leistung (max 35kW)',
|
||||
A: 'Motorräder',
|
||||
B: 'Kraftfahrzeuge ≤ 3500 kg, ≤ 8 Sitzplätze',
|
||||
C1: 'Mittelschwere Fahrzeuge -7500 kg',
|
||||
C: 'Schwere Nutzfahrzeuge > 3500 kg',
|
||||
D1: 'Kleinbusse 9-16 Sitzplätze',
|
||||
D: 'Busse > 8 Sitzplätze',
|
||||
BE: 'Fahrzeugklasse B mit Anhänger',
|
||||
C1E: 'Fahrzeugklasse C1 mit Anhänger',
|
||||
CE: 'Fahrzeugklasse C mit Anhänger',
|
||||
D1E: 'Fahrzeugklasse D1 mit Anhänger',
|
||||
DE: 'Fahrzeugklasse D mit Anhänger',
|
||||
L: 'Land-, Forstwirtschaftsfahrzeuge, Stapler max 40km/h',
|
||||
T: 'Land-, Forstwirtschaftsfahrzeuge, Stapler max 60km/h'
|
||||
},
|
||||
users: 'Mitglieder',
|
||||
user: {
|
||||
login: 'Nutzer Anmeldung',
|
||||
edit: 'Nutzer bearbeiten',
|
||||
user: 'Nutzer',
|
||||
management: 'Mitgliederverwaltung',
|
||||
id: 'Mitgliedsnr',
|
||||
name: 'Name',
|
||||
email: 'Email',
|
||||
status: 'Status',
|
||||
role: 'Nutzerrolle'
|
||||
},
|
||||
cancel: 'Abbrechen',
|
||||
confirm: 'Bestätigen',
|
||||
actions: 'Aktionen',
|
||||
edit: 'Bearbeiten',
|
||||
delete: 'Löschen',
|
||||
mandate_date_signed: 'Mandatserteilungsdatum',
|
||||
licence_categories: 'Führerscheinklassen',
|
||||
subscription_model: 'Mitgliedschatfsmodell',
|
||||
licence: 'Führerschein',
|
||||
licence_number: 'Führerscheinnummer',
|
||||
issued_date: 'Ausgabedatum',
|
||||
expiration_date: 'Ablaufdatum',
|
||||
country: 'Land',
|
||||
monthly_fee: 'Monatliche Gebühr',
|
||||
hourly_rate: 'Stundensatz',
|
||||
details: 'Details',
|
||||
conditions: 'Bedingungen',
|
||||
unknown: 'Unbekannt',
|
||||
notes: 'Notizen',
|
||||
address: 'Straße & Hausnummer',
|
||||
city: 'Wohnort',
|
||||
zip_code: 'PLZ',
|
||||
forgot_password: 'Passwort vergessen?',
|
||||
password: 'Passwort',
|
||||
password_repeat: 'Passwort wiederholen',
|
||||
email: 'Email',
|
||||
company: 'Firma',
|
||||
login: 'Anmeldung',
|
||||
profile: 'Profil',
|
||||
membership: 'Mitgliedschaft',
|
||||
bankaccount: 'Kontodaten',
|
||||
first_name: 'Vorname',
|
||||
last_name: 'Nachname',
|
||||
name: 'Name',
|
||||
phone: 'Telefonnummer',
|
||||
dateofbirth: 'Geburtstag',
|
||||
status: 'Status',
|
||||
start: 'Beginn',
|
||||
end: 'Ende',
|
||||
parent_member_id: 'Hauptmitgliedsnr.',
|
||||
bank_account_holder: 'Kontoinhaber',
|
||||
bank_name: 'Bank',
|
||||
iban: 'IBAN',
|
||||
bic: 'BIC',
|
||||
mandate_reference: 'SEPA Mandat',
|
||||
subscriptions: 'Tarifmodelle',
|
||||
payments: 'Zahlungen',
|
||||
add_new: 'Neu',
|
||||
included_hours_per_year: 'Inkludierte Stunden pro Jahr',
|
||||
included_hours_per_month: 'Inkludierte Stunden pro Monat',
|
||||
|
||||
// For payments section
|
||||
payment: {
|
||||
id: "Zahlungs-Nr",
|
||||
amount: "Betrag",
|
||||
date: "Datum",
|
||||
status: "Status",
|
||||
},
|
||||
// For payments section
|
||||
payment: {
|
||||
id: 'Zahlungs-Nr',
|
||||
amount: 'Betrag',
|
||||
date: 'Datum',
|
||||
status: 'Status'
|
||||
},
|
||||
|
||||
// For subscription statuses
|
||||
subscriptionStatus: {
|
||||
pending: "Ausstehend",
|
||||
completed: "Abgeschlossen",
|
||||
failed: "Fehlgeschlagen",
|
||||
cancelled: "Storniert",
|
||||
},
|
||||
// For subscription statuses
|
||||
subscriptionStatus: {
|
||||
pending: 'Ausstehend',
|
||||
completed: 'Abgeschlossen',
|
||||
failed: 'Fehlgeschlagen',
|
||||
cancelled: 'Storniert'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
// @ts-nocheck
|
||||
import { quintOut } from "svelte/easing";
|
||||
import { crossfade } from "svelte/transition";
|
||||
import { quintOut } from 'svelte/easing';
|
||||
import { crossfade } from 'svelte/transition';
|
||||
|
||||
export const [send, receive] = crossfade({
|
||||
duration: (d) => Math.sqrt(d * 200),
|
||||
duration: (d) => Math.sqrt(d * 200),
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
fallback(node, params) {
|
||||
const style = getComputedStyle(node);
|
||||
const transform = style.transform === "none" ? "" : style.transform;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
fallback(node, params) {
|
||||
const style = getComputedStyle(node);
|
||||
const transform = style.transform === 'none' ? '' : style.transform;
|
||||
|
||||
return {
|
||||
duration: 600,
|
||||
easing: quintOut,
|
||||
css: (t) => `
|
||||
return {
|
||||
duration: 600,
|
||||
easing: quintOut,
|
||||
css: (t) => `
|
||||
transform: ${transform} scale(${t});
|
||||
opacity: ${t}
|
||||
`,
|
||||
};
|
||||
},
|
||||
`
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -27,9 +27,9 @@ export const [send, receive] = crossfade({
|
||||
* @param {string} email - The email to validate
|
||||
*/
|
||||
export const isValidEmail = (email) => {
|
||||
const EMAIL_REGEX =
|
||||
/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
|
||||
return EMAIL_REGEX.test(email.trim());
|
||||
const EMAIL_REGEX =
|
||||
/[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
|
||||
return EMAIL_REGEX.test(email.trim());
|
||||
};
|
||||
/**
|
||||
* Validates a strong password field
|
||||
@@ -37,11 +37,9 @@ export const isValidEmail = (email) => {
|
||||
* @param {string} password - The password to validate
|
||||
*/
|
||||
export const isValidPasswordStrong = (password) => {
|
||||
const strongRegex = new RegExp(
|
||||
"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})"
|
||||
);
|
||||
const strongRegex = new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})');
|
||||
|
||||
return strongRegex.test(password.trim());
|
||||
return strongRegex.test(password.trim());
|
||||
};
|
||||
/**
|
||||
* Validates a medium password field
|
||||
@@ -49,11 +47,11 @@ export const isValidPasswordStrong = (password) => {
|
||||
* @param {string} password - The password to validate
|
||||
*/
|
||||
export const isValidPasswordMedium = (password) => {
|
||||
const mediumRegex = new RegExp(
|
||||
"^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})"
|
||||
);
|
||||
const mediumRegex = new RegExp(
|
||||
'^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})'
|
||||
);
|
||||
|
||||
return mediumRegex.test(password.trim());
|
||||
return mediumRegex.test(password.trim());
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -63,22 +61,22 @@ export const isValidPasswordMedium = (password) => {
|
||||
*/
|
||||
|
||||
export function isEmpty(obj) {
|
||||
for (const _i in obj) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
for (const _i in obj) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function toRFC3339(dateString) {
|
||||
if (!dateString) dateString = "0001-01-01T00:00:00.000Z";
|
||||
const date = new Date(dateString);
|
||||
return date.toISOString();
|
||||
if (!dateString) dateString = '0001-01-01T00:00:00.000Z';
|
||||
const date = new Date(dateString);
|
||||
return date.toISOString();
|
||||
}
|
||||
|
||||
export function fromRFC3339(dateString) {
|
||||
if (!dateString) dateString = "0001-01-01T00:00:00.000Z";
|
||||
const date = new Date(dateString);
|
||||
return date.toISOString().split("T")[0];
|
||||
if (!dateString) dateString = '0001-01-01T00:00:00.000Z';
|
||||
const date = new Date(dateString);
|
||||
return date.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,28 +84,26 @@ export function fromRFC3339(dateString) {
|
||||
* @param {App.Locals.User} user - The user object to format
|
||||
*/
|
||||
export function userDatesFromRFC3339(user) {
|
||||
if (user.date_of_birth) {
|
||||
user.date_of_birth = fromRFC3339(user.date_of_birth);
|
||||
}
|
||||
if (user.membership) {
|
||||
if (user.membership.start_date) {
|
||||
user.membership.start_date = fromRFC3339(user.membership.start_date);
|
||||
}
|
||||
if (user.membership.end_date) {
|
||||
user.membership.end_date = fromRFC3339(user.membership.end_date);
|
||||
}
|
||||
}
|
||||
if (user.licence?.issued_date) {
|
||||
user.licence.issued_date = fromRFC3339(user.licence.issued_date);
|
||||
}
|
||||
if (user.licence?.expiration_date) {
|
||||
user.licence.expiration_date = fromRFC3339(user.licence.expiration_date);
|
||||
}
|
||||
if (user.bank_account && user.bank_account.mandate_date_signed) {
|
||||
user.bank_account.mandate_date_signed = fromRFC3339(
|
||||
user.bank_account.mandate_date_signed
|
||||
);
|
||||
}
|
||||
if (user.dateofbirth) {
|
||||
user.dateofbirth = fromRFC3339(user.dateofbirth);
|
||||
}
|
||||
if (user.membership) {
|
||||
if (user.membership.start_date) {
|
||||
user.membership.start_date = fromRFC3339(user.membership.start_date);
|
||||
}
|
||||
if (user.membership.end_date) {
|
||||
user.membership.end_date = fromRFC3339(user.membership.end_date);
|
||||
}
|
||||
}
|
||||
if (user.licence?.issued_date) {
|
||||
user.licence.issued_date = fromRFC3339(user.licence.issued_date);
|
||||
}
|
||||
if (user.licence?.expiration_date) {
|
||||
user.licence.expiration_date = fromRFC3339(user.licence.expiration_date);
|
||||
}
|
||||
if (user.bank_account && user.bank_account.mandate_date_signed) {
|
||||
user.bank_account.mandate_date_signed = fromRFC3339(user.bank_account.mandate_date_signed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,28 +111,26 @@ export function userDatesFromRFC3339(user) {
|
||||
* @param {App.Locals.User} user - The user object to format
|
||||
*/
|
||||
export function userDatesToRFC3339(user) {
|
||||
if (user.date_of_birth) {
|
||||
user.date_of_birth = toRFC3339(user.date_of_birth);
|
||||
}
|
||||
if (user.membership) {
|
||||
if (user.membership.start_date) {
|
||||
user.membership.start_date = toRFC3339(user.membership.start_date);
|
||||
}
|
||||
if (user.membership.end_date) {
|
||||
user.membership.end_date = toRFC3339(user.membership.end_date);
|
||||
}
|
||||
}
|
||||
if (user.licence?.issued_date) {
|
||||
user.licence.issued_date = toRFC3339(user.licence.issued_date);
|
||||
}
|
||||
if (user.licence?.expiration_date) {
|
||||
user.licence.expiration_date = toRFC3339(user.licence.expiration_date);
|
||||
}
|
||||
if (user.bank_account && user.bank_account.mandate_date_signed) {
|
||||
user.bank_account.mandate_date_signed = toRFC3339(
|
||||
user.bank_account.mandate_date_signed
|
||||
);
|
||||
}
|
||||
if (user.dateofbirth) {
|
||||
user.dateofbirth = toRFC3339(user.dateofbirth);
|
||||
}
|
||||
if (user.membership) {
|
||||
if (user.membership.start_date) {
|
||||
user.membership.start_date = toRFC3339(user.membership.start_date);
|
||||
}
|
||||
if (user.membership.end_date) {
|
||||
user.membership.end_date = toRFC3339(user.membership.end_date);
|
||||
}
|
||||
}
|
||||
if (user.licence?.issued_date) {
|
||||
user.licence.issued_date = toRFC3339(user.licence.issued_date);
|
||||
}
|
||||
if (user.licence?.expiration_date) {
|
||||
user.licence.expiration_date = toRFC3339(user.licence.expiration_date);
|
||||
}
|
||||
if (user.bank_account && user.bank_account.mandate_date_signed) {
|
||||
user.bank_account.mandate_date_signed = toRFC3339(user.bank_account.mandate_date_signed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,33 +139,33 @@ export function userDatesToRFC3339(user) {
|
||||
* @returns {array} The formatted error object
|
||||
*/
|
||||
export function formatError(obj) {
|
||||
const errors = [];
|
||||
if (typeof obj === "object" && obj !== null) {
|
||||
if (Array.isArray(obj)) {
|
||||
obj.forEach((error) => {
|
||||
errors.push({
|
||||
field: error.field,
|
||||
key: error.key,
|
||||
id: Math.random() * 1000,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Object.keys(obj).forEach((field) => {
|
||||
errors.push({
|
||||
field: field,
|
||||
key: obj[field].key,
|
||||
id: Math.random() * 1000,
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
errors.push({
|
||||
field: "general",
|
||||
key: obj,
|
||||
id: 0,
|
||||
});
|
||||
}
|
||||
return errors;
|
||||
const errors = [];
|
||||
if (typeof obj === 'object' && obj !== null) {
|
||||
if (Array.isArray(obj)) {
|
||||
obj.forEach((error) => {
|
||||
errors.push({
|
||||
field: error.field,
|
||||
key: error.key,
|
||||
id: Math.random() * 1000
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Object.keys(obj).forEach((field) => {
|
||||
errors.push({
|
||||
field: field,
|
||||
key: obj[field].key,
|
||||
id: Math.random() * 1000
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
errors.push({
|
||||
field: 'general',
|
||||
key: obj,
|
||||
id: 0
|
||||
});
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,26 +174,26 @@ export function formatError(obj) {
|
||||
* @param {import('RequestEvent<Partial<Record<string, string>>, string | null>')} event - The event object
|
||||
*/
|
||||
export function refreshCookie(newToken, event) {
|
||||
if (newToken) {
|
||||
const match = newToken.match(/jwt=([^;]+)/);
|
||||
if (match) {
|
||||
if (event) {
|
||||
event.cookies.set("jwt", match[1], {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production", // Secure in production
|
||||
sameSite: "lax",
|
||||
maxAge: 5 * 24 * 60 * 60, // 5 days in seconds
|
||||
});
|
||||
} else {
|
||||
cookies.set("jwt", match[1], {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production", // Secure in production
|
||||
sameSite: "lax",
|
||||
maxAge: 5 * 24 * 60 * 60, // 5 days in seconds
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (newToken) {
|
||||
const match = newToken.match(/jwt=([^;]+)/);
|
||||
if (match) {
|
||||
if (event) {
|
||||
event.cookies.set('jwt', match[1], {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production', // Secure in production
|
||||
sameSite: 'lax',
|
||||
maxAge: 5 * 24 * 60 * 60 // 5 days in seconds
|
||||
});
|
||||
} else {
|
||||
cookies.set('jwt', match[1], {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === 'production', // Secure in production
|
||||
sameSite: 'lax',
|
||||
maxAge: 5 * 24 * 60 * 60 // 5 days in seconds
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
121
frontend/src/lib/utils/processing.js
Normal file
121
frontend/src/lib/utils/processing.js
Normal file
@@ -0,0 +1,121 @@
|
||||
import { toRFC3339 } from './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
|
||||
*/
|
||||
export 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
|
||||
*/
|
||||
export 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 || ''),
|
||||
dateofbirth: toRFC3339(rawData.user.dateofbirth),
|
||||
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)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Remove undefined or null properties
|
||||
const cleanUpdateData = JSON.parse(JSON.stringify(processedData), (key, value) =>
|
||||
value !== null && value !== '' ? value : undefined
|
||||
);
|
||||
console.dir(cleanUpdateData);
|
||||
return cleanUpdateData;
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
import { BASE_API_URI } from "$lib/utils/constants";
|
||||
import {
|
||||
formatError,
|
||||
userDatesFromRFC3339,
|
||||
userDatesToRFC3339,
|
||||
} from "$lib/utils/helpers";
|
||||
import { fail, redirect } from "@sveltejs/kit";
|
||||
import { toRFC3339 } from "$lib/utils/helpers";
|
||||
import { BASE_API_URI } from '$lib/utils/constants';
|
||||
import { formatError, userDatesFromRFC3339 } from '$lib/utils/helpers';
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { formDataToObject, processFormData } from '$lib/utils/processing';
|
||||
|
||||
/**
|
||||
* @typedef {Object} UpdateData
|
||||
@@ -14,187 +10,125 @@ 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/about/${params.id}`);
|
||||
}
|
||||
// redirect user if not logged in
|
||||
if (!locals.user) {
|
||||
throw redirect(302, `/auth/login?next=/auth/about/${params.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
/** @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();
|
||||
/**
|
||||
*
|
||||
* @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();
|
||||
|
||||
/** @type {App.Types['licenceCategory'][]} */
|
||||
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;
|
||||
}
|
||||
});
|
||||
const rawData = formDataToObject(formData);
|
||||
const processedData = processFormData(rawData);
|
||||
|
||||
/** @type {Partial<App.Locals['user']>} */
|
||||
const userData = {
|
||||
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("date_of_birth")),
|
||||
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(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,
|
||||
},
|
||||
};
|
||||
console.dir(processedData.user.membership);
|
||||
const isCreating = !processedData.user.id || processedData.user.id === 0;
|
||||
console.log('Is updating: ', isCreating);
|
||||
console.dir(formData);
|
||||
const apiURL = `${BASE_API_URI}/backend/users/update/`;
|
||||
|
||||
// userDatesToRFC3339(userData);
|
||||
/** @type {RequestInit} */
|
||||
const requestUpdateOptions = {
|
||||
method: 'PATCH',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Cookie: `jwt=${cookies.get('jwt')}`
|
||||
},
|
||||
body: JSON.stringify(processedData)
|
||||
};
|
||||
const res = await fetch(apiURL, requestUpdateOptions);
|
||||
|
||||
/** @type {UpdateData} */
|
||||
const updateData = { user: userData };
|
||||
// Remove undefined or null properties
|
||||
const cleanUpdateData = JSON.parse(
|
||||
JSON.stringify(updateData),
|
||||
(key, value) => (value !== null && value !== "" ? value : undefined)
|
||||
);
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
const errors = formatError(response.errors);
|
||||
return fail(400, { errors: errors });
|
||||
}
|
||||
|
||||
console.dir(formData);
|
||||
console.dir(cleanUpdateData);
|
||||
const apiURL = `${BASE_API_URI}/backend/users/update/`;
|
||||
const response = await res.json();
|
||||
locals.user = response;
|
||||
userDatesFromRFC3339(locals.user);
|
||||
throw redirect(303, `/auth/about/${response.id}`);
|
||||
},
|
||||
|
||||
/** @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);
|
||||
/**
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
uploadImage: async ({ request, fetch, cookies }) => {
|
||||
const formData = await request.formData();
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
const errors = formatError(response.errors);
|
||||
return fail(400, { errors: errors });
|
||||
}
|
||||
/** @type {RequestInit} */
|
||||
const requestInitOptions = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Cookie: `jwt=${cookies.get('jwt')}`
|
||||
},
|
||||
body: formData
|
||||
};
|
||||
|
||||
const response = await res.json();
|
||||
locals.user = response;
|
||||
userDatesFromRFC3339(locals.user);
|
||||
throw redirect(303, `/auth/about/${response.id}`);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
uploadImage: async ({ request, fetch, cookies }) => {
|
||||
const formData = await request.formData();
|
||||
const res = await fetch(`${BASE_API_URI}/file/upload/`, requestInitOptions);
|
||||
|
||||
/** @type {RequestInit} */
|
||||
const requestInitOptions = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Cookie: `jwt=${cookies.get("jwt")}`,
|
||||
},
|
||||
body: formData,
|
||||
};
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
const errors = formatError(response.errors);
|
||||
return fail(400, { errors: errors });
|
||||
}
|
||||
|
||||
const res = await fetch(`${BASE_API_URI}/file/upload/`, requestInitOptions);
|
||||
const response = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
const errors = formatError(response.errors);
|
||||
return fail(400, { errors: errors });
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
profile_picture: response['']
|
||||
};
|
||||
},
|
||||
|
||||
const response = await res.json();
|
||||
/**
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
deleteImage: async ({ request, fetch, cookies }) => {
|
||||
const formData = await request.formData();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
profile_picture: response[""],
|
||||
};
|
||||
},
|
||||
/** @type {RequestInit} */
|
||||
const requestInitOptions = {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
Cookie: `jwt=${cookies.get('jwt')}`
|
||||
},
|
||||
body: formData
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
deleteImage: async ({ request, fetch, cookies }) => {
|
||||
const formData = await request.formData();
|
||||
const res = await fetch(`${BASE_API_URI}/file/delete/`, requestInitOptions);
|
||||
|
||||
/** @type {RequestInit} */
|
||||
const requestInitOptions = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
Cookie: `jwt=${cookies.get("jwt")}`,
|
||||
},
|
||||
body: formData,
|
||||
};
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
const errors = formatError(response.errors);
|
||||
return fail(400, { errors: errors });
|
||||
}
|
||||
|
||||
const res = await fetch(`${BASE_API_URI}/file/delete/`, requestInitOptions);
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
const errors = formatError(response.errors);
|
||||
return fail(400, { errors: errors });
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
profile_picture: "",
|
||||
};
|
||||
},
|
||||
return {
|
||||
success: true,
|
||||
profile_picture: ''
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -65,10 +65,10 @@
|
||||
<span class="value">{user.phone}</span>
|
||||
</h3>
|
||||
{/if}
|
||||
{#if user.date_of_birth}
|
||||
{#if user.dateofbirth}
|
||||
<h3 class="hero-subtitle subtitle info-row">
|
||||
<span class="label">Geburtstag:</span>
|
||||
<span class="value">{user.date_of_birth}</span>
|
||||
<span class="value">{user.dateofbirth}</span>
|
||||
</h3>
|
||||
{/if}
|
||||
{#if user.notes}
|
||||
|
||||
@@ -2,194 +2,63 @@
|
||||
// - 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";
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
import { BASE_API_URI } from '$lib/utils/constants';
|
||||
import { formatError, userDatesFromRFC3339 } from '$lib/utils/helpers';
|
||||
import { fail, redirect } from '@sveltejs/kit';
|
||||
import { formDataToObject, processFormData } from '$lib/utils/processing';
|
||||
|
||||
/** @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`);
|
||||
}
|
||||
// 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();
|
||||
/**
|
||||
*
|
||||
* @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();
|
||||
|
||||
// Convert form data to nested object
|
||||
const rawData = formDataToObject(formData);
|
||||
const rawData = formDataToObject(formData);
|
||||
const processedData = processFormData(rawData);
|
||||
|
||||
const processedData = processFormData(rawData);
|
||||
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`;
|
||||
|
||||
// Remove undefined or null properties
|
||||
const cleanUpdateData = JSON.parse(
|
||||
JSON.stringify(processedData),
|
||||
(key, value) => (value !== null && value !== "" ? value : undefined)
|
||||
);
|
||||
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 requestOptions = {
|
||||
method: isCreating ? 'POST' : 'PATCH',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Cookie: `jwt=${cookies.get('jwt')}`
|
||||
},
|
||||
body: JSON.stringify(processedData)
|
||||
};
|
||||
|
||||
/** @type {RequestInit} */
|
||||
const requestOptions = {
|
||||
method: isCreating ? "POST" : "PATCH",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Cookie: `jwt=${cookies.get("jwt")}`,
|
||||
},
|
||||
body: JSON.stringify(cleanUpdateData),
|
||||
};
|
||||
const res = await fetch(apiURL, requestOptions);
|
||||
|
||||
const res = await fetch(apiURL, requestOptions);
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
const errors = formatError(response.errors);
|
||||
return fail(400, { errors: errors });
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
const errors = formatError(response.errors);
|
||||
return fail(400, { errors: errors });
|
||||
}
|
||||
|
||||
const response = await res.json();
|
||||
console.log("Server success response:", response);
|
||||
locals.user = response;
|
||||
userDatesFromRFC3339(locals.user);
|
||||
throw redirect(303, `/auth/about/${response.id}`);
|
||||
},
|
||||
const response = await res.json();
|
||||
console.log('Server success response:', response);
|
||||
locals.user = response;
|
||||
userDatesFromRFC3339(locals.user);
|
||||
throw redirect(303, `/auth/about/${response.id}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
on:click={() => setActiveSection('users')}
|
||||
>
|
||||
<i class="fas fa-users"></i>
|
||||
<span class="nav-badge">{users.length}</span>
|
||||
{$t('users')}
|
||||
<span class="nav-badge">{users.length}</span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
@@ -68,8 +68,8 @@
|
||||
on:click={() => setActiveSection('subscriptions')}
|
||||
>
|
||||
<i class="fas fa-clipboard-list"></i>
|
||||
<span class="nav-badge">{subscriptions.length}</span>
|
||||
{$t('subscriptions')}
|
||||
<span class="nav-badge">{subscriptions.length}</span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
@@ -238,7 +238,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 1rem;
|
||||
color: white;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.layout {
|
||||
@@ -252,8 +252,8 @@
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
min-height: 600px;
|
||||
background: #2f2f2f;
|
||||
border-right: 1px solid #494848;
|
||||
background: var(--surface0);
|
||||
border-right: 1px solid var(--surface1);
|
||||
}
|
||||
|
||||
.nav-list {
|
||||
@@ -272,7 +272,7 @@
|
||||
background: none;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
color: #9b9b9b;
|
||||
color: var(--subtext0);
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
letter-spacing: 1px;
|
||||
@@ -280,12 +280,14 @@
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background: #fdfff5;
|
||||
background: var(--surface1);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
background: #494848;
|
||||
color: white;
|
||||
background: var(--surface2);
|
||||
color: var(--lavender);
|
||||
border-left: 3px solid var(--mauve);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
@@ -295,20 +297,29 @@
|
||||
|
||||
.accordion-item {
|
||||
border: none;
|
||||
background: #2f2f2f;
|
||||
background: var(--surface0);
|
||||
margin-bottom: 0.5rem;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.accordion-header {
|
||||
padding: 1rem;
|
||||
cursor: pointer;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
color: white;
|
||||
color: var(--text);
|
||||
background: var(--surface1);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.accordion-header:hover {
|
||||
background: var(--surface2);
|
||||
}
|
||||
|
||||
.accordion-content {
|
||||
padding: 1rem;
|
||||
background: #494848;
|
||||
background: var(--surface0);
|
||||
border-top: 1px solid var(--surface1);
|
||||
}
|
||||
|
||||
.table {
|
||||
@@ -325,11 +336,11 @@
|
||||
}
|
||||
|
||||
.table th {
|
||||
color: #9b9b9b;
|
||||
color: var(--subtext1);
|
||||
}
|
||||
|
||||
.table td {
|
||||
color: white;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
@@ -358,5 +369,42 @@
|
||||
|
||||
.section-header h2 {
|
||||
margin: 0;
|
||||
color: var(--lavender);
|
||||
}
|
||||
/* Additional styles for better visual hierarchy */
|
||||
details[open] .accordion-header {
|
||||
background: var(--surface2);
|
||||
color: var(--lavender);
|
||||
}
|
||||
|
||||
.button-group {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Style for the nav badge */
|
||||
.nav-badge {
|
||||
background: var(--surface2);
|
||||
color: var(--text);
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* Improved focus states */
|
||||
.nav-link:focus,
|
||||
.accordion-header:focus {
|
||||
outline: 2px solid var(--mauve);
|
||||
outline-offset: -2px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* Add subtle transitions */
|
||||
.accordion-item,
|
||||
.accordion-header,
|
||||
.nav-link {
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user