styling
This commit is contained in:
2
frontend/.gitignore
vendored
2
frontend/.gitignore
vendored
@@ -35,3 +35,5 @@ Thumbs.db
|
||||
!vite.config.js # Vite configuration
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
|
||||
!src/**
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<script>
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { t } from "svelte-i18n";
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
/** @type {string} */
|
||||
export let name;
|
||||
|
||||
/** @type {string} */
|
||||
export let type = "text";
|
||||
export let type = 'text';
|
||||
|
||||
/** @type {string|Number|null} */
|
||||
export let value;
|
||||
|
||||
/** @type {string} */
|
||||
export let placeholder = "";
|
||||
export let placeholder = '';
|
||||
|
||||
/** @type {Number} */
|
||||
export let rows = 4;
|
||||
@@ -24,10 +24,10 @@
|
||||
export let required = false;
|
||||
|
||||
/** @type {string} */
|
||||
export let label = "";
|
||||
export let label = '';
|
||||
|
||||
/** @type {string} */
|
||||
export let otherPasswordValue = "";
|
||||
export let otherPasswordValue = '';
|
||||
|
||||
/** @type {boolean} */
|
||||
export let toUpperCase = false;
|
||||
@@ -62,66 +62,57 @@
|
||||
* @returns {string|null} The error message or null if valid
|
||||
*/
|
||||
function validateField(name, value, required) {
|
||||
if (
|
||||
value === null ||
|
||||
(typeof value === "string" && !value.trim() && !required)
|
||||
)
|
||||
return null;
|
||||
if (value === null || (typeof value === 'string' && !value.trim() && !required)) return null;
|
||||
switch (name) {
|
||||
case "membership_start_date":
|
||||
return typeof value === "string" && value.trim()
|
||||
case 'membership_start_date':
|
||||
return typeof value === 'string' && value.trim() ? null : $t('validation.date');
|
||||
case 'email':
|
||||
return typeof value === 'string' && /^\S+@\S+\.\S+$/.test(value)
|
||||
? null
|
||||
: $t("validation.date");
|
||||
case "email":
|
||||
return typeof value === "string" && /^\S+@\S+\.\S+$/.test(value)
|
||||
? null
|
||||
: $t("validation.email");
|
||||
case "password":
|
||||
case "password2":
|
||||
if (typeof value === "string" && value.length < 8) {
|
||||
return $t("validation.password");
|
||||
: $t('validation.email');
|
||||
case 'password':
|
||||
case 'password2':
|
||||
if (typeof value === 'string' && value.length < 8) {
|
||||
return $t('validation.password');
|
||||
}
|
||||
if (otherPasswordValue && value !== otherPasswordValue) {
|
||||
return $t("validation.password_match");
|
||||
return $t('validation.password_match');
|
||||
}
|
||||
return null;
|
||||
case "phone":
|
||||
return typeof value === "string" && /^\+?[0-9\s()-]{7,}$/.test(value)
|
||||
case 'phone':
|
||||
return typeof value === 'string' && /^\+?[0-9\s()-]{7,}$/.test(value)
|
||||
? null
|
||||
: $t("validation.phone");
|
||||
case "zip_code":
|
||||
return typeof value === "string" && /^\d{5}$/.test(value)
|
||||
: $t('validation.phone');
|
||||
case 'zip_code':
|
||||
return typeof value === 'string' && /^\d{5}$/.test(value)
|
||||
? null
|
||||
: $t("validation.zip_code");
|
||||
case "iban":
|
||||
return typeof value === "string" &&
|
||||
/^[A-Z]{2}\d{2}[A-Z0-9]{1,30}$/.test(value)
|
||||
: $t('validation.zip_code');
|
||||
case 'iban':
|
||||
return typeof value === 'string' && /^[A-Z]{2}\d{2}[A-Z0-9]{1,30}$/.test(value)
|
||||
? null
|
||||
: $t("validation.iban");
|
||||
case "bic":
|
||||
return typeof value === "string" &&
|
||||
: $t('validation.iban');
|
||||
case 'bic':
|
||||
return typeof value === 'string' &&
|
||||
/^[A-Z]{6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3})?$/.test(value)
|
||||
? null
|
||||
: $t("validation.bic");
|
||||
case "licence_number":
|
||||
return typeof value === "string" && value.length == 11
|
||||
? null
|
||||
: $t("validation.licence");
|
||||
: $t('validation.bic');
|
||||
case 'licence_number':
|
||||
return typeof value === 'string' && value.length == 11 ? null : $t('validation.licence');
|
||||
|
||||
default:
|
||||
return typeof value === "string" && !value.trim() && required
|
||||
? $t("validation.required")
|
||||
return typeof value === 'string' && !value.trim() && required
|
||||
? $t('validation.required')
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
$: error = validateField(name, value, required);
|
||||
$: selectedOption = options.find((option) => option.value == value);
|
||||
$: selectedColor = selectedOption ? selectedOption.color : "";
|
||||
$: selectedColor = selectedOption ? selectedOption.color : '';
|
||||
</script>
|
||||
|
||||
<div class="input-box {type === 'checkbox' ? 'checkbox-container' : ''}">
|
||||
{#if type === "checkbox"}
|
||||
{#if type === 'checkbox'}
|
||||
<label class="form-control {readonly ? 'form-control--disabled' : ''}">
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -140,19 +131,19 @@
|
||||
{#if error}
|
||||
<span class="error-message">{error}</span>
|
||||
{/if}
|
||||
{#if type === "select"}
|
||||
{#if type === 'select'}
|
||||
<select
|
||||
{name}
|
||||
bind:value
|
||||
{required}
|
||||
class="input select"
|
||||
style={selectedColor ? `color: ${selectedColor};` : ""}
|
||||
style={selectedColor ? `color: ${selectedColor};` : ''}
|
||||
>
|
||||
{#each options as option}
|
||||
<option value={option.value}>{option.label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else if type === "textarea"}
|
||||
{:else if type === 'textarea'}
|
||||
<textarea
|
||||
{name}
|
||||
{placeholder}
|
||||
@@ -162,8 +153,8 @@
|
||||
{rows}
|
||||
class="input textarea {readonly ? 'readonly' : ''}"
|
||||
style="height:{rows * 1.5}em;"
|
||||
/>
|
||||
{:else if type != "checkbox"}
|
||||
></textarea>
|
||||
{:else if type != 'checkbox'}
|
||||
<input
|
||||
{name}
|
||||
{type}
|
||||
@@ -181,8 +172,8 @@
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--form-control-color: #6bff55;
|
||||
--form-control-disabled: #959495;
|
||||
--form-control-color: var(--green); /* Changed from #6bff55 */
|
||||
--form-control-disabled: var(--overlay0); /* Changed from #959495 */
|
||||
}
|
||||
|
||||
.form-control {
|
||||
@@ -194,6 +185,7 @@
|
||||
gap: 0.75em;
|
||||
align-items: center;
|
||||
opacity: 0.8;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.form-control--disabled {
|
||||
@@ -201,16 +193,16 @@
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
input[type='checkbox'] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background-color: var(--form-background);
|
||||
background-color: var(--surface0);
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
color: currentColor;
|
||||
color: var(--text);
|
||||
width: 1.75em;
|
||||
height: 1.75em;
|
||||
border: 0.15em solid currentColor;
|
||||
border: 0.15em solid var(--overlay0);
|
||||
border-radius: 0.5em;
|
||||
transform: translateY(-0.075em);
|
||||
display: grid;
|
||||
@@ -218,8 +210,8 @@
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
input[type="checkbox"]::before {
|
||||
content: "";
|
||||
input[type='checkbox']::before {
|
||||
content: '';
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
|
||||
@@ -227,39 +219,41 @@
|
||||
transform-origin: bottom left;
|
||||
transition: 120ms transform ease-in-out;
|
||||
box-shadow: inset 1em 1em var(--form-control-color);
|
||||
background-color: CanvasText;
|
||||
background-color: var(--crust);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked::before {
|
||||
input[type='checkbox']:checked::before {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:hover {
|
||||
outline: max(2px, 0.15em) solid currentColor;
|
||||
input[type='checkbox']:hover {
|
||||
outline: max(2px, 0.15em) solid var(--lavender);
|
||||
outline-offset: max(2px, 0.15em);
|
||||
transform: scale(1.3);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:disabled {
|
||||
input[type='checkbox']:disabled {
|
||||
--form-control-color: var(--form-control-disabled);
|
||||
color: var(--form-control-disabled);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.readonly {
|
||||
background-color: #ececec;
|
||||
background-color: var(--surface0);
|
||||
cursor: not-allowed;
|
||||
opacity: 0.7;
|
||||
color: #4f4f4f;
|
||||
color: var(--overlay1);
|
||||
}
|
||||
|
||||
.checkbox-container {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.checkbox-text {
|
||||
font-size: 16px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
@@ -270,16 +264,18 @@
|
||||
}
|
||||
.select {
|
||||
padding-right: 1.5em;
|
||||
background-color: var(--surface0);
|
||||
}
|
||||
.input-error-container {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
max-width: 444px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
color: #eb5424;
|
||||
color: var(--red); /* Changed from #eb5424 */
|
||||
font-size: 12px;
|
||||
margin-bottom: 5px;
|
||||
align-self: flex-start;
|
||||
@@ -287,18 +283,56 @@
|
||||
|
||||
.input {
|
||||
width: 100%;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
input,
|
||||
textarea,
|
||||
select {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
padding: 0.75rem 0;
|
||||
background-color: var(--surface0);
|
||||
border: 1px solid var(--overlay0);
|
||||
border-radius: 6px;
|
||||
color: var(--text);
|
||||
transition: border-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
input:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
outline: none;
|
||||
border-color: var(--lavender);
|
||||
}
|
||||
|
||||
input:hover:not(:disabled),
|
||||
textarea:hover:not(:disabled),
|
||||
select:hover:not(:disabled) {
|
||||
border-color: var(--overlay2);
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 100px;
|
||||
}
|
||||
/* Add consistent spacing between input boxes */
|
||||
.input-box {
|
||||
margin: 1rem 0;
|
||||
padding: 0.5rem;
|
||||
background-color: var(--surface0);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.input-box .label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--lavender);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Style select dropdown */
|
||||
select option {
|
||||
background-color: var(--base);
|
||||
color: var(--text);
|
||||
padding: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script>
|
||||
import { quintOut } from "svelte/easing";
|
||||
import { quintOut } from 'svelte/easing';
|
||||
|
||||
import { t } from "svelte-i18n";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { t } from 'svelte-i18n';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const modal = (/** @type {Element} */ node, { duration = 300 } = {}) => {
|
||||
const transform = getComputedStyle(node).transform;
|
||||
@@ -16,31 +16,26 @@
|
||||
scale(${t})
|
||||
translateY(${u * -100}%)
|
||||
`;
|
||||
},
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
function closeModal() {
|
||||
dispatch("close", {});
|
||||
dispatch('close', {});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="modal-background">
|
||||
<div
|
||||
transition:modal|global={{ duration: 1000 }}
|
||||
class="modal"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<div transition:modal|global={{ duration: 1000 }} class="modal" role="dialog" aria-modal="true">
|
||||
<!-- svelte-ignore a11y-missing-attribute -->
|
||||
<a
|
||||
title={$t("cancel")}
|
||||
title={$t('cancel')}
|
||||
class="modal-close"
|
||||
on:click={closeModal}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:keydown={(e) => e.key == "Enter" && closeModal()}
|
||||
on:keydown={(e) => e.key == 'Enter' && closeModal()}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -53,7 +48,7 @@
|
||||
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>
|
||||
<span class="sr-only">{$t('cancel')}</span>
|
||||
</a>
|
||||
<div class="container">
|
||||
<slot />
|
||||
@@ -69,7 +64,8 @@
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
background: rgba(30, 30, 46, 0.65); /* var(--base) with 0.75 opacity */
|
||||
backdrop-filter: blur(4px); /* Optional: adds a slight blur effect */
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
}
|
||||
@@ -79,7 +75,10 @@
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 70%;
|
||||
box-shadow: 0 0 10px hsl(0 0% 0% / 10%);
|
||||
background-color: var(--base);
|
||||
border: 1px solid var(--surface0);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 20px rgba(17, 17, 27, 0.5); /* var(--crust) with opacity */
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.sr-only {
|
||||
@@ -100,24 +99,52 @@
|
||||
}
|
||||
.modal-close {
|
||||
border: none;
|
||||
padding: 1rem;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.modal-close svg {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
fill: rgb(14 165 233 /1);
|
||||
transition: all 0.5s;
|
||||
transition: all 0.3s ease-in-out;
|
||||
fill: var(--blue); /* Using Catppuccin blue */
|
||||
}
|
||||
.modal-close:hover svg {
|
||||
fill: rgb(225 29 72);
|
||||
transform: scale(1.5);
|
||||
fill: var(--red); /* Using Catppuccin red */
|
||||
transform: scale(1.2);
|
||||
}
|
||||
.modal .container {
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
align-items: center;
|
||||
padding: 2rem;
|
||||
background-color: var(--base);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* Scrollbar styling */
|
||||
.modal .container::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.modal .container::-webkit-scrollbar-track {
|
||||
background: var(--surface0);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.modal .container::-webkit-scrollbar-thumb {
|
||||
background: var(--surface2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.modal .container::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--surface1);
|
||||
}
|
||||
|
||||
@media (min-width: 680px) {
|
||||
.modal .container {
|
||||
flex-direction: column;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
</script>
|
||||
|
||||
<div class="loading">
|
||||
<p class="simple-loader" style={width ? `width: ${width}px` : ""} />
|
||||
<p class="simple-loader" style={width ? `width: ${width}px` : ''}></p>
|
||||
{#if message}
|
||||
<p>{message}</p>
|
||||
{/if}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script>
|
||||
import InputField from "$lib/components/InputField.svelte";
|
||||
import SmallLoader from "$lib/components/SmallLoader.svelte";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { applyAction, enhance } from "$app/forms";
|
||||
import { receive, send } from "$lib/utils/helpers";
|
||||
import { t } from "svelte-i18n";
|
||||
import InputField from '$lib/components/InputField.svelte';
|
||||
import SmallLoader from '$lib/components/SmallLoader.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { applyAction, enhance } from '$app/forms';
|
||||
import { receive, send } from '$lib/utils/helpers';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
/** @type {import('../../routes/auth/about/[id]/$types').ActionData} */
|
||||
export let form;
|
||||
@@ -15,49 +15,49 @@
|
||||
/** @type {App.Locals['user']} */
|
||||
const blankUser = {
|
||||
id: 0,
|
||||
email: "",
|
||||
first_name: "",
|
||||
last_name: "",
|
||||
phone: "",
|
||||
address: "",
|
||||
zip_code: "",
|
||||
city: "",
|
||||
company: "",
|
||||
date_of_birth: "",
|
||||
notes: "",
|
||||
profile_picture: "",
|
||||
email: '',
|
||||
first_name: '',
|
||||
last_name: '',
|
||||
phone: '',
|
||||
address: '',
|
||||
zip_code: '',
|
||||
city: '',
|
||||
company: '',
|
||||
date_of_birth: '',
|
||||
notes: '',
|
||||
profile_picture: '',
|
||||
payment_status: 0,
|
||||
status: 1,
|
||||
role_id: 0,
|
||||
membership: {
|
||||
id: 0,
|
||||
start_date: "",
|
||||
end_date: "",
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
status: 3,
|
||||
parent_member_id: 0,
|
||||
subscription_model: {
|
||||
id: 0,
|
||||
name: "",
|
||||
},
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
licence: {
|
||||
id: 0,
|
||||
status: 1,
|
||||
licence_number: "",
|
||||
issued_date: "",
|
||||
expiration_date: "",
|
||||
country: "",
|
||||
licence_categories: [],
|
||||
licence_number: '',
|
||||
issued_date: '',
|
||||
expiration_date: '',
|
||||
country: '',
|
||||
licence_categories: []
|
||||
},
|
||||
bank_account: {
|
||||
id: 0,
|
||||
mandate_date_signed: "",
|
||||
bank: "",
|
||||
account_holder_name: "",
|
||||
iban: "",
|
||||
bic: "",
|
||||
mandate_reference: "",
|
||||
},
|
||||
mandate_date_signed: '',
|
||||
bank: '',
|
||||
account_holder_name: '',
|
||||
iban: '',
|
||||
bic: '',
|
||||
mandate_reference: ''
|
||||
}
|
||||
};
|
||||
|
||||
/** @type {App.Locals['user'] | null} */
|
||||
@@ -75,7 +75,7 @@
|
||||
...user,
|
||||
licence: user.licence || blankUser.licence,
|
||||
membership: user.membership || blankUser.membership,
|
||||
bank_account: user.bank_account || blankUser.bank_account,
|
||||
bank_account: user.bank_account || blankUser.bank_account
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -84,60 +84,58 @@
|
||||
$: isLoading = user === undefined;
|
||||
|
||||
$: {
|
||||
console.log("incomingUser:", user);
|
||||
console.log('incomingUser:', user);
|
||||
}
|
||||
|
||||
// Add debug logging for user
|
||||
$: {
|
||||
console.log("processed user:", user);
|
||||
console.log('processed user:', user);
|
||||
}
|
||||
/** @type {App.Locals['licence_categories']} */
|
||||
export let licence_categories;
|
||||
|
||||
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"
|
||||
{ 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"
|
||||
{ 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"
|
||||
{ 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: 1, label: $t("userStatus.1"), color: "#b1b1b1" }, // Grey for "Nicht 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"
|
||||
{ value: 1, label: $t('userStatus.1'), color: '#b1b1b1' }, // Grey for "Nicht 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 dispatch = createEventDispatcher();
|
||||
const TABS = ["profile", "licence", "membership", "bankaccount"];
|
||||
const TABS = ['profile', 'licence', 'membership', 'bankaccount'];
|
||||
let activeTab = TABS[0];
|
||||
|
||||
let isUpdating = false,
|
||||
password = "",
|
||||
password2 = "";
|
||||
password = '',
|
||||
password2 = '';
|
||||
|
||||
/** @type {Object.<string, App.Locals['licence_categories']>} */
|
||||
$: groupedCategories = groupCategories(licence_categories);
|
||||
$: subscriptionModelOptions = subscriptions.map((sub) => ({
|
||||
value: sub?.name ?? "",
|
||||
label: sub?.name ?? "",
|
||||
value: sub?.name ?? '',
|
||||
label: sub?.name ?? ''
|
||||
}));
|
||||
$: selectedSubscriptionModel =
|
||||
subscriptions.find(
|
||||
(sub) => sub?.id === localUser.membership?.subscription_model.id
|
||||
) || null;
|
||||
subscriptions.find((sub) => sub?.id === localUser.membership?.subscription_model.id) || null;
|
||||
|
||||
/**
|
||||
* creates groups of categories depending on the first letter
|
||||
@@ -148,10 +146,7 @@
|
||||
return Object.entries(categories)
|
||||
.sort((a, b) => a[1].category.localeCompare(b[1].category))
|
||||
.reduce(
|
||||
(
|
||||
/** @type {Object.<string, App.Locals['licence_categories']>} */ acc,
|
||||
[_, category]
|
||||
) => {
|
||||
(/** @type {Object.<string, App.Locals['licence_categories']>} */ acc, [_, category]) => {
|
||||
const firstLetter = category.category[0];
|
||||
if (!acc[firstLetter]) {
|
||||
acc[firstLetter] = [];
|
||||
@@ -176,12 +171,10 @@
|
||||
isUpdating = true;
|
||||
return async ({ result }) => {
|
||||
isUpdating = false;
|
||||
if (result.type === "success" || result.type === "redirect") {
|
||||
if (result.type === 'success' || result.type === 'redirect') {
|
||||
close();
|
||||
} else {
|
||||
document
|
||||
.querySelector(".modal .container")
|
||||
?.scrollTo({ top: 0, behavior: "smooth" });
|
||||
document.querySelector('.modal .container')?.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
await applyAction(result);
|
||||
};
|
||||
@@ -189,24 +182,19 @@
|
||||
</script>
|
||||
|
||||
{#if isLoading}
|
||||
<SmallLoader width={30} message={$t("loading.user_data")} />
|
||||
<SmallLoader width={30} message={$t('loading.user_data')} />
|
||||
{:else if localUser}
|
||||
<form
|
||||
class="content"
|
||||
action="?/updateUser"
|
||||
method="POST"
|
||||
use:enhance={handleUpdate}
|
||||
>
|
||||
<form class="content" action="?/updateUser" method="POST" use:enhance={handleUpdate}>
|
||||
<input name="user[id]" type="hidden" bind:value={localUser.id} />
|
||||
<h1 class="step-title" style="text-align: center;">{$t("user.edit")}</h1>
|
||||
<h1 class="step-title" style="text-align: center;">{$t('user.edit')}</h1>
|
||||
{#if form?.success}
|
||||
<h4
|
||||
class="step-subtitle warning"
|
||||
in:receive|global={{ key: Math.floor(Math.random() * 100) }}
|
||||
out:send|global={{ key: Math.floor(Math.random() * 100) }}
|
||||
>
|
||||
Um einen fehlerhaften upload Ihres Bildes zu vermeiden, clicke bitte auf
|
||||
den "Update" Button unten.
|
||||
Um einen fehlerhaften upload Ihres Bildes zu vermeiden, clicke bitte auf den "Update" Button
|
||||
unten.
|
||||
</h4>
|
||||
{/if}
|
||||
{#if form?.errors}
|
||||
@@ -216,7 +204,7 @@
|
||||
in:receive|global={{ key: error.id }}
|
||||
out:send|global={{ key: error.id }}
|
||||
>
|
||||
{$t(error.field) + ": " + $t(error.key)}
|
||||
{$t(error.field) + ': ' + $t(error.key)}
|
||||
</h4>
|
||||
{/each}
|
||||
{/if}
|
||||
@@ -240,14 +228,11 @@
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
<div
|
||||
class="tab-content"
|
||||
style="display: {activeTab === 'profile' ? 'block' : 'none'}"
|
||||
>
|
||||
<div class="tab-content" style="display: {activeTab === 'profile' ? 'block' : 'none'}">
|
||||
<InputField
|
||||
name="user[status]"
|
||||
type="select"
|
||||
label={$t("status")}
|
||||
label={$t('status')}
|
||||
bind:value={localUser.status}
|
||||
options={userStatusOptions}
|
||||
/>
|
||||
@@ -255,7 +240,7 @@
|
||||
<InputField
|
||||
name="user[role_id]"
|
||||
type="select"
|
||||
label={$t("user.role")}
|
||||
label={$t('user.role')}
|
||||
bind:value={localUser.role_id}
|
||||
options={userRoleOptions}
|
||||
/>
|
||||
@@ -263,135 +248,132 @@
|
||||
<InputField
|
||||
name="user[password]"
|
||||
type="password"
|
||||
label={$t("password")}
|
||||
placeholder={$t("placeholder.password")}
|
||||
label={$t('password')}
|
||||
placeholder={$t('placeholder.password')}
|
||||
bind:value={password}
|
||||
otherPasswordValue={password2}
|
||||
/>
|
||||
<InputField
|
||||
name="password2"
|
||||
type="password"
|
||||
label={$t("password_repeat")}
|
||||
placeholder={$t("placeholder.password")}
|
||||
label={$t('password_repeat')}
|
||||
placeholder={$t('placeholder.password')}
|
||||
bind:value={password2}
|
||||
otherPasswordValue={password}
|
||||
/>
|
||||
<InputField
|
||||
name="user[first_name]"
|
||||
label={$t("first_name")}
|
||||
label={$t('first_name')}
|
||||
bind:value={localUser.first_name}
|
||||
placeholder={$t("placeholder.first_name")}
|
||||
placeholder={$t('placeholder.first_name')}
|
||||
required={true}
|
||||
/>
|
||||
<InputField
|
||||
name="user[last_name]"
|
||||
label={$t("last_name")}
|
||||
label={$t('last_name')}
|
||||
bind:value={localUser.last_name}
|
||||
placeholder={$t("placeholder.last_name")}
|
||||
placeholder={$t('placeholder.last_name')}
|
||||
required={true}
|
||||
/>
|
||||
<InputField
|
||||
name="user[company]"
|
||||
label={$t("company")}
|
||||
label={$t('company')}
|
||||
bind:value={localUser.company}
|
||||
placeholder={$t("placeholder.company")}
|
||||
placeholder={$t('placeholder.company')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[email]"
|
||||
type="email"
|
||||
label={$t("email")}
|
||||
label={$t('email')}
|
||||
bind:value={localUser.email}
|
||||
placeholder={$t("placeholder.email")}
|
||||
placeholder={$t('placeholder.email')}
|
||||
required={true}
|
||||
/>
|
||||
<InputField
|
||||
name="user[phone]"
|
||||
type="tel"
|
||||
label={$t("phone")}
|
||||
label={$t('phone')}
|
||||
bind:value={localUser.phone}
|
||||
placeholder={$t("placeholder.phone")}
|
||||
placeholder={$t('placeholder.phone')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[date_of_birth]"
|
||||
type="date"
|
||||
label={$t("date_of_birth")}
|
||||
label={$t('date_of_birth')}
|
||||
bind:value={localUser.date_of_birth}
|
||||
placeholder={$t("placeholder.date_of_birth")}
|
||||
placeholder={$t('placeholder.date_of_birth')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[address]"
|
||||
label={$t("address")}
|
||||
label={$t('address')}
|
||||
bind:value={localUser.address}
|
||||
placeholder={$t("placeholder.address")}
|
||||
placeholder={$t('placeholder.address')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[zip_code]"
|
||||
label={$t("zip_code")}
|
||||
label={$t('zip_code')}
|
||||
bind:value={localUser.zip_code}
|
||||
placeholder={$t("placeholder.zip_code")}
|
||||
placeholder={$t('placeholder.zip_code')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[city]"
|
||||
label={$t("city")}
|
||||
label={$t('city')}
|
||||
bind:value={localUser.city}
|
||||
placeholder={$t("placeholder.city")}
|
||||
placeholder={$t('placeholder.city')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[notes]"
|
||||
type="textarea"
|
||||
label={$t("notes")}
|
||||
label={$t('notes')}
|
||||
bind:value={localUser.notes}
|
||||
placeholder={$t("placeholder.notes", {
|
||||
values: { name: localUser.first_name || "" },
|
||||
placeholder={$t('placeholder.notes', {
|
||||
values: { name: localUser.first_name || '' }
|
||||
})}
|
||||
rows={10}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tab-content"
|
||||
style="display: {activeTab === 'licence' ? 'block' : 'none'}"
|
||||
>
|
||||
<div class="tab-content" style="display: {activeTab === 'licence' ? 'block' : 'none'}">
|
||||
<InputField
|
||||
name="user[licence][status]"
|
||||
type="select"
|
||||
label={$t("status")}
|
||||
label={$t('status')}
|
||||
bind:value={localUser.licence.status}
|
||||
options={licenceStatusOptions}
|
||||
/>
|
||||
<InputField
|
||||
name="user[licence][number]"
|
||||
type="text"
|
||||
label={$t("licence_number")}
|
||||
label={$t('licence_number')}
|
||||
bind:value={localUser.licence.licence_number}
|
||||
placeholder={$t("placeholder.licence_number")}
|
||||
placeholder={$t('placeholder.licence_number')}
|
||||
toUpperCase={true}
|
||||
/>
|
||||
<InputField
|
||||
name="user[licence][issued_date]"
|
||||
type="date"
|
||||
label={$t("issued_date")}
|
||||
label={$t('issued_date')}
|
||||
bind:value={localUser.licence.issued_date}
|
||||
placeholder={$t("placeholder.issued_date")}
|
||||
placeholder={$t('placeholder.issued_date')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[licence][expiration_date]"
|
||||
type="date"
|
||||
label={$t("expiration_date")}
|
||||
label={$t('expiration_date')}
|
||||
bind:value={localUser.licence.expiration_date}
|
||||
placeholder={$t("placeholder.expiration_date")}
|
||||
placeholder={$t('placeholder.expiration_date')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[licence][country]"
|
||||
label={$t("country")}
|
||||
label={$t('country')}
|
||||
bind:value={localUser.licence.country}
|
||||
placeholder={$t("placeholder.issuing_country")}
|
||||
placeholder={$t('placeholder.issuing_country')}
|
||||
/>
|
||||
<div class="licence-categories">
|
||||
<h3>{$t("licence_categories")}</h3>
|
||||
<h3>{$t('licence_categories')}</h3>
|
||||
<div class="checkbox-grid">
|
||||
{#each Object.entries(groupedCategories) as [group, categories], groupIndex}
|
||||
{#if groupIndex > 0}
|
||||
<div class="category-break" />
|
||||
<div class="category-break"></div>
|
||||
{/if}
|
||||
{#each categories as category}
|
||||
<div class="checkbox-item">
|
||||
@@ -416,55 +398,52 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="tab-content"
|
||||
style="display: {activeTab === 'membership' ? 'block' : 'none'}"
|
||||
>
|
||||
<div class="tab-content" style="display: {activeTab === 'membership' ? 'block' : 'none'}">
|
||||
<InputField
|
||||
name="user[membership][status]"
|
||||
type="select"
|
||||
label={$t("status")}
|
||||
label={$t('status')}
|
||||
bind:value={localUser.membership.status}
|
||||
options={membershipStatusOptions}
|
||||
/>
|
||||
<InputField
|
||||
name="user[membership][subscription_model][name]"
|
||||
type="select"
|
||||
label={$t("subscription_model")}
|
||||
label={$t('subscription_model')}
|
||||
bind:value={localUser.membership.subscription_model.name}
|
||||
options={subscriptionModelOptions}
|
||||
/>
|
||||
<div class="subscription-info">
|
||||
<div class="subscription-column">
|
||||
<p>
|
||||
<strong>{$t("monthly_fee")}:</strong>
|
||||
{selectedSubscriptionModel?.monthly_fee || "-"}
|
||||
<strong>{$t('monthly_fee')}:</strong>
|
||||
{selectedSubscriptionModel?.monthly_fee || '-'}
|
||||
</p>
|
||||
<p>
|
||||
<strong>{$t("hourly_rate")}:</strong>
|
||||
{selectedSubscriptionModel?.hourly_rate || "-"}
|
||||
<strong>{$t('hourly_rate')}:</strong>
|
||||
{selectedSubscriptionModel?.hourly_rate || '-'}
|
||||
</p>
|
||||
{#if selectedSubscriptionModel?.included_hours_per_year}
|
||||
<p>
|
||||
<strong>{$t("included_hours_per_year")}:</strong>
|
||||
<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>
|
||||
<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 || "-"}
|
||||
<strong>{$t('details')}:</strong>
|
||||
{selectedSubscriptionModel?.details || '-'}
|
||||
</p>
|
||||
{#if selectedSubscriptionModel?.conditions}
|
||||
<p>
|
||||
<strong>{$t("conditions")}:</strong>
|
||||
<strong>{$t('conditions')}:</strong>
|
||||
{selectedSubscriptionModel?.conditions}
|
||||
</p>
|
||||
{/if}
|
||||
@@ -473,64 +452,61 @@
|
||||
<InputField
|
||||
name="user[membership][start_date]"
|
||||
type="date"
|
||||
label={$t("start")}
|
||||
label={$t('start')}
|
||||
bind:value={localUser.membership.start_date}
|
||||
placeholder={$t("placeholder.start_date")}
|
||||
placeholder={$t('placeholder.start_date')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[membership][end_date]"
|
||||
type="date"
|
||||
label={$t("end")}
|
||||
label={$t('end')}
|
||||
bind:value={localUser.membership.end_date}
|
||||
placeholder={$t("placeholder.end_date")}
|
||||
placeholder={$t('placeholder.end_date')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[membership][parent_member_id]"
|
||||
type="number"
|
||||
label={$t("parent_member_id")}
|
||||
label={$t('parent_member_id')}
|
||||
bind:value={localUser.membership.parent_member_id}
|
||||
placeholder={$t("placeholder.parent_member_id")}
|
||||
placeholder={$t('placeholder.parent_member_id')}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="tab-content"
|
||||
style="display: {activeTab === 'bankaccount' ? 'block' : 'none'}"
|
||||
>
|
||||
<div class="tab-content" style="display: {activeTab === 'bankaccount' ? 'block' : 'none'}">
|
||||
<InputField
|
||||
name="user[bank_account][account_holder_name]"
|
||||
label={$t("bank_account_holder")}
|
||||
label={$t('bank_account_holder')}
|
||||
bind:value={localUser.bank_account.account_holder_name}
|
||||
placeholder={$t("placeholder.bank_account_holder")}
|
||||
placeholder={$t('placeholder.bank_account_holder')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[bank_account][bank_name]"
|
||||
label={$t("bank_name")}
|
||||
label={$t('bank_name')}
|
||||
bind:value={localUser.bank_account.bank}
|
||||
placeholder={$t("placeholder.bank_name")}
|
||||
placeholder={$t('placeholder.bank_name')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[bank_account][iban]"
|
||||
label={$t("iban")}
|
||||
label={$t('iban')}
|
||||
bind:value={localUser.bank_account.iban}
|
||||
placeholder={$t("placeholder.iban")}
|
||||
placeholder={$t('placeholder.iban')}
|
||||
toUpperCase={true}
|
||||
/>
|
||||
<InputField
|
||||
name="user[bank_account][bic]"
|
||||
label={$t("bic")}
|
||||
label={$t('bic')}
|
||||
bind:value={localUser.bank_account.bic}
|
||||
placeholder={$t("placeholder.bic")}
|
||||
placeholder={$t('placeholder.bic')}
|
||||
toUpperCase={true}
|
||||
/>
|
||||
<InputField
|
||||
name="user[bank_account][mandate_reference]"
|
||||
label={$t("mandate_reference")}
|
||||
label={$t('mandate_reference')}
|
||||
bind:value={localUser.bank_account.mandate_reference}
|
||||
placeholder={$t("placeholder.mandate_reference")}
|
||||
placeholder={$t('placeholder.mandate_reference')}
|
||||
/>
|
||||
<InputField
|
||||
name="user[bank_account][mandate_date_signed]"
|
||||
label={$t("mandate_date_signed")}
|
||||
label={$t('mandate_date_signed')}
|
||||
type="date"
|
||||
bind:value={localUser.bank_account.mandate_date_signed}
|
||||
readonly={true}
|
||||
@@ -538,16 +514,12 @@
|
||||
</div>
|
||||
<div class="button-container">
|
||||
{#if isUpdating}
|
||||
<SmallLoader width={30} message={"Aktualisiere..."} />
|
||||
<SmallLoader width={30} message={'Aktualisiere...'} />
|
||||
{:else}
|
||||
<button
|
||||
type="button"
|
||||
class="button-dark"
|
||||
on:click={() => dispatch("cancel")}
|
||||
<button type="button" class="button-dark" on:click={() => dispatch('cancel')}>
|
||||
{$t('cancel')}</button
|
||||
>
|
||||
{$t("cancel")}</button
|
||||
>
|
||||
<button type="submit" class="button-dark">{$t("confirm")}</button>
|
||||
<button type="submit" class="button-dark">{$t('confirm')}</button>
|
||||
{/if}
|
||||
</div>
|
||||
</form>
|
||||
@@ -557,7 +529,7 @@
|
||||
.category-break {
|
||||
grid-column: 1 / -1;
|
||||
height: 1px;
|
||||
background-color: #ccc;
|
||||
background-color: var(--surface0);
|
||||
margin-top: 10px;
|
||||
margin-left: 20%;
|
||||
width: 60%;
|
||||
@@ -589,7 +561,7 @@
|
||||
.checkbox-description {
|
||||
flex: 1;
|
||||
font-size: 15px;
|
||||
color: #9b9b9b;
|
||||
color: var(--subtext0);
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@@ -618,11 +590,16 @@
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
font-size: 0.9rem;
|
||||
background-color: var(--surface0);
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--surface1);
|
||||
}
|
||||
|
||||
.subscription-column {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.subscription-column p {
|
||||
@@ -632,10 +609,14 @@
|
||||
.subscription-column strong {
|
||||
display: inline-block;
|
||||
min-width: 100px;
|
||||
color: var(--lavender);
|
||||
}
|
||||
.tab-content {
|
||||
padding: 1rem;
|
||||
border-radius: 0 0 3px 3px;
|
||||
background-color: var(--surface0);
|
||||
border: 1px solid var(--surface1);
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.button-container {
|
||||
display: flex;
|
||||
@@ -649,8 +630,24 @@
|
||||
.button-container button {
|
||||
flex: 1 1 0;
|
||||
min-width: 120px;
|
||||
max-width: calc(50%-5px);
|
||||
max-width: calc(50% - 5px);
|
||||
background-color: var(--surface1);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--overlay0);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.button-container button:hover {
|
||||
background-color: var(--surface2);
|
||||
border-color: var(--lavender);
|
||||
}
|
||||
|
||||
.button-container button.active {
|
||||
background-color: var(--mauve);
|
||||
border-color: var(--mauve);
|
||||
color: var(--base);
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.button-container button {
|
||||
flex-basis: 100%;
|
||||
|
||||
155
frontend/src/lib/css/styles.min.css
vendored
155
frontend/src/lib/css/styles.min.css
vendored
@@ -1,23 +1,52 @@
|
||||
:root {
|
||||
--rosewater: #f5e0dc;
|
||||
--flamingo: #f2cdcd;
|
||||
--pink: #f5c2e7;
|
||||
--mauve: #cba6f7;
|
||||
--red: #f38ba8;
|
||||
--maroon: #eba0ac;
|
||||
--peach: #fab387;
|
||||
--yellow: #f9e2af;
|
||||
--green: #a6e3a1;
|
||||
--teal: #94e2d5;
|
||||
--sky: #89dceb;
|
||||
--sapphire: #74c7ec;
|
||||
--blue: #89b4fa;
|
||||
--lavender: #b4befe;
|
||||
--text: #cdd6f4;
|
||||
--subtext1: #bac2de;
|
||||
--subtext0: #a6adc8;
|
||||
--overlay2: #9399b2;
|
||||
--overlay1: #7f849c;
|
||||
--overlay0: #6c7086;
|
||||
--surface2: #585b70;
|
||||
--surface1: #45475a;
|
||||
--surface0: #313244;
|
||||
--base: #1e1e2e;
|
||||
--mantle: #181825;
|
||||
--crust: #11111b;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Roboto Mono";
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v22/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_gPq_ROW9.ttf)
|
||||
format("truetype");
|
||||
format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Roboto Mono";
|
||||
font-family: 'Roboto Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/robotomono/v22/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_3vq_ROW9.ttf)
|
||||
format("truetype");
|
||||
format('truetype');
|
||||
}
|
||||
|
||||
html {
|
||||
padding: 0 30px;
|
||||
background-color: black;
|
||||
color: #9b9b9b;
|
||||
font-family: "Quicksand", sans-serif;
|
||||
background-color: var(--base);
|
||||
color: var(--text);
|
||||
font-family: 'Quicksand', sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
}
|
||||
@@ -28,13 +57,13 @@ body {
|
||||
pre,
|
||||
code {
|
||||
display: inline;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
input {
|
||||
font-family: "Roboto Mono", monospace;
|
||||
color: white;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
color: var(--text);
|
||||
border-style: none;
|
||||
height: 21px;
|
||||
font-size: 16px;
|
||||
@@ -53,12 +82,12 @@ h6 {
|
||||
}
|
||||
h2 {
|
||||
margin: 0 0 45px 0;
|
||||
color: #fff;
|
||||
color: var(--lavender);
|
||||
font-size: 36px;
|
||||
}
|
||||
h3 {
|
||||
margin: 0 0 2rem 0;
|
||||
color: #fff;
|
||||
color: var(--lavender);
|
||||
font-size: 32px;
|
||||
}
|
||||
p {
|
||||
@@ -72,16 +101,16 @@ a {
|
||||
transition: border 0.2s ease-in-out;
|
||||
border-bottom: 1px solid transparent;
|
||||
text-decoration: none;
|
||||
color: #00b7ef;
|
||||
color: var(--blue);
|
||||
}
|
||||
a:hover {
|
||||
border-bottom-color: #00b7ef;
|
||||
border-bottom-color: var(--blue);
|
||||
}
|
||||
li {
|
||||
line-height: 1.8;
|
||||
}
|
||||
li strong {
|
||||
color: #fff;
|
||||
color: var(--text);
|
||||
}
|
||||
.image {
|
||||
width: 100%;
|
||||
@@ -117,7 +146,7 @@ li strong {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 3em 0 0;
|
||||
background: black;
|
||||
background: var(--base);
|
||||
}
|
||||
.header.top-banner-open {
|
||||
margin-top: 5px;
|
||||
@@ -131,6 +160,7 @@ li strong {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
background-color: var(--base);
|
||||
}
|
||||
.header .header-container .header-left {
|
||||
display: flex;
|
||||
@@ -142,7 +172,7 @@ li strong {
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container a {
|
||||
display: flex;
|
||||
color: #9b9b9b;
|
||||
color: var(--subtext0);
|
||||
border: none;
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container a img {
|
||||
@@ -154,7 +184,7 @@ li strong {
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container .auth0 {
|
||||
margin-left: 1ch;
|
||||
color: #fff;
|
||||
color: var(--text);
|
||||
font-weight: bold;
|
||||
}
|
||||
.header .header-container .header-right {
|
||||
@@ -174,7 +204,7 @@ li strong {
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item.active a,
|
||||
.header .header-container .header-right .header-nav-item.active button {
|
||||
color: #fff;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.header .header-container .header-right a img {
|
||||
@@ -188,11 +218,12 @@ li strong {
|
||||
display: block;
|
||||
padding: 20px 0;
|
||||
border: none;
|
||||
color: #9b9b9b;
|
||||
color: var(--subtext0);
|
||||
}
|
||||
|
||||
.header .header-container .header-right .header-nav-item:hover a,
|
||||
.header .header-container .header-right .header-nav-item:hover button {
|
||||
color: #fdfff5;
|
||||
color: var(--lavender);
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
.header {
|
||||
@@ -212,7 +243,9 @@ li strong {
|
||||
}
|
||||
}
|
||||
.button-dark {
|
||||
transition: border-color 0.3s ease-in-out, background-color 0.3s ease-in-out;
|
||||
transition:
|
||||
border-color 0.3s ease-in-out,
|
||||
background-color 0.3s ease-in-out;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
@@ -220,52 +253,58 @@ li strong {
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 1px solid #595b5c;
|
||||
border: 1px solid var(--surface1);
|
||||
margin: 2px;
|
||||
}
|
||||
.button-dark:hover {
|
||||
border-color: #fff;
|
||||
border-color: var(--text);
|
||||
}
|
||||
.button-colorful {
|
||||
transition: border-color 0.3s ease-in-out, background-color 0.3s ease-in-out;
|
||||
transition:
|
||||
border-color 0.3s ease-in-out,
|
||||
background-color 0.3s ease-in-out;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
padding: 18px 28px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
background-color: #d43aff;
|
||||
border: 1px solid #d43aff;
|
||||
background-color: var(--mauve);
|
||||
border: 1px solid var(--mauve);
|
||||
}
|
||||
.button-colorful:hover {
|
||||
background-color: #c907ff;
|
||||
border-color: #c907ff;
|
||||
}
|
||||
.button-orange {
|
||||
transition: border-color 0.3s ease-in-out, background-color 0.3s ease-in-out;
|
||||
transition:
|
||||
border-color 0.3s ease-in-out,
|
||||
background-color 0.3s ease-in-out;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
padding: 18px 28px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
background-color: #eb5424;
|
||||
border: 1px solid #eb5424;
|
||||
background-color: var(--peach);
|
||||
border: 1px solid var(--peach);
|
||||
}
|
||||
.button-orange:hover {
|
||||
background-color: #ca3f12;
|
||||
border-color: #ca3f12;
|
||||
}
|
||||
.button-colorful:disabled {
|
||||
transition: border-color 0.3s ease-in-out, background-color 0.3s ease-in-out;
|
||||
transition:
|
||||
border-color 0.3s ease-in-out,
|
||||
background-color 0.3s ease-in-out;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
padding: 18px 28px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
background-color: #9a9a9a;
|
||||
border: 1px solid #9a9a9a;
|
||||
background-color: var(--overlay0);
|
||||
border: 1px solid var(--overlay0);
|
||||
}
|
||||
.hero-container {
|
||||
max-width: 795px;
|
||||
@@ -351,9 +390,9 @@ li strong {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
box-sizing: border-box;
|
||||
background-color: #2f2f2f;
|
||||
background-color: var(--surface0);
|
||||
border-radius: 3px;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
.input-box .label {
|
||||
@@ -361,10 +400,10 @@ li strong {
|
||||
font-size: 16px;
|
||||
}
|
||||
.input-box .input {
|
||||
background-color: #494848;
|
||||
background-color: var(--surface1);
|
||||
border: 3px solid var(--surface1);
|
||||
border-radius: 6px;
|
||||
outline: none;
|
||||
border: 3px solid #494848;
|
||||
width: 100%;
|
||||
max-width: 444px;
|
||||
font-size: 13px;
|
||||
@@ -394,7 +433,7 @@ li strong {
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
letter-spacing: 1px;
|
||||
padding: 18px 28px;
|
||||
border: 1px solid;
|
||||
@@ -402,44 +441,46 @@ li strong {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
transition: border-color 0.3s ease-in-out, background-color 0.3s ease-in-out;
|
||||
transition:
|
||||
border-color 0.3s ease-in-out,
|
||||
background-color 0.3s ease-in-out;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.btn.primary {
|
||||
background-color: #4361ee;
|
||||
border-color: #4361ee;
|
||||
background-color: var(--blue);
|
||||
border-color: var(--blue);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn.primary:hover {
|
||||
background-color: #3651d4;
|
||||
border-color: #3651d4;
|
||||
background-color: var(--sapphire);
|
||||
border-color: var(--sapphire);
|
||||
}
|
||||
|
||||
.btn.danger {
|
||||
background-color: #dc2626;
|
||||
border-color: #dc2626;
|
||||
background-color: var(--red);
|
||||
border-color: var(--red);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn.danger:hover {
|
||||
background-color: #c51f1f;
|
||||
border-color: #c51f1f;
|
||||
background-color: var(--maroon);
|
||||
border-color: var(--maroon);
|
||||
}
|
||||
.warning {
|
||||
margin: 20px 0;
|
||||
padding: 1rem;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: rgb(255 228 230);
|
||||
border: 1px solid rgb(225 29 72);
|
||||
background-color: var(--surface0);
|
||||
border: 1px solid var(--red);
|
||||
color: var(--red);
|
||||
border-radius: 6px;
|
||||
color: rgb(225 29 72);
|
||||
font-size: 16px;
|
||||
}
|
||||
.warning a {
|
||||
color: rgb(225 29 72);
|
||||
color: var(--red);
|
||||
text-decoration: underline;
|
||||
}
|
||||
.warning.hidden {
|
||||
@@ -449,8 +490,8 @@ li strong {
|
||||
.error {
|
||||
margin-top: 10rem;
|
||||
padding: 30px 40px;
|
||||
background: #2f3132;
|
||||
color: #fff;
|
||||
background: var(--surface0);
|
||||
color: var(--text);
|
||||
}
|
||||
.error p {
|
||||
margin: 0 0 1rem;
|
||||
@@ -523,7 +564,7 @@ li strong {
|
||||
--b: 20px; /* border thickness */
|
||||
--n: 15; /* number of dashes*/
|
||||
--g: 7deg; /* gap between dashes*/
|
||||
--c: #d43aff; /* the color */
|
||||
--c: var(--mauve); /* Changed loader color to match theme */
|
||||
|
||||
width: 40px; /* size */
|
||||
aspect-ratio: 1;
|
||||
@@ -535,11 +576,7 @@ li strong {
|
||||
#000 1deg calc(360deg / var(--n) - var(--g) - 1deg),
|
||||
#0000 calc(360deg / var(--n) - var(--g)) calc(360deg / var(--n))
|
||||
),
|
||||
radial-gradient(
|
||||
farthest-side,
|
||||
#0000 calc(98% - var(--b)),
|
||||
#000 calc(100% - var(--b))
|
||||
);
|
||||
radial-gradient(farthest-side, #0000 calc(98% - var(--b)), #000 calc(100% - var(--b)));
|
||||
-webkit-mask: var(--_m);
|
||||
mask: var(--_m);
|
||||
-webkit-mask-composite: destination-in;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script>
|
||||
import Modal from "$lib/components/Modal.svelte";
|
||||
import UserEditForm from "$lib/components/UserEditForm.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { page } from "$app/stores";
|
||||
import { t } from "svelte-i18n";
|
||||
import Modal from '$lib/components/Modal.svelte';
|
||||
import UserEditForm from '$lib/components/UserEditForm.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { t } from 'svelte-i18n';
|
||||
|
||||
/** @type {import('./$types').ActionData} */
|
||||
export let form;
|
||||
@@ -33,12 +33,10 @@
|
||||
<span class="value block-value">
|
||||
<span
|
||||
>{$t(`userStatus.${user.status}`, {
|
||||
default: "unknown status",
|
||||
default: 'unknown status'
|
||||
})}</span
|
||||
>
|
||||
<span
|
||||
>{$t(`userRole.${user.role_id}`, { default: "unknown role" })}</span
|
||||
>
|
||||
<span>{$t(`userRole.${user.role_id}`, { default: 'unknown role' })}</span>
|
||||
</span>
|
||||
</h3>
|
||||
{/if}
|
||||
@@ -75,7 +73,7 @@
|
||||
{/if}
|
||||
{#if user.notes}
|
||||
<h3 class="hero-subtitle subtitle info-row">
|
||||
<span class="label">{$t("notes")}:</span>
|
||||
<span class="label">{$t('notes')}:</span>
|
||||
<span class="value">{user.notes}</span>
|
||||
</h3>
|
||||
{/if}
|
||||
@@ -88,13 +86,7 @@
|
||||
|
||||
{#if showModal}
|
||||
<Modal on:close={close}>
|
||||
<UserEditForm
|
||||
{form}
|
||||
{user}
|
||||
{subscriptions}
|
||||
{licence_categories}
|
||||
on:cancel={close}
|
||||
/>
|
||||
<UserEditForm {form} {user} {subscriptions} {licence_categories} on:cancel={close} />
|
||||
</Modal>
|
||||
{/if}
|
||||
|
||||
@@ -116,6 +108,11 @@
|
||||
align-items: start;
|
||||
text-align: left;
|
||||
margin-top: 1rem;
|
||||
color: var(--text);
|
||||
background-color: var(--surface0);
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--surface1);
|
||||
}
|
||||
|
||||
.info-row {
|
||||
@@ -127,18 +124,21 @@
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
padding-right: 1rem;
|
||||
color: var(--lavender);
|
||||
}
|
||||
|
||||
.value {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
text-align: left;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.block-value {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
color: var(--subtext0);
|
||||
}
|
||||
|
||||
.hero-buttons-container {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script>
|
||||
import Modal from "$lib/components/Modal.svelte";
|
||||
import UserEditForm from "$lib/components/UserEditForm.svelte";
|
||||
import { t } from "svelte-i18n";
|
||||
import { page } from "$app/stores";
|
||||
import Modal from '$lib/components/Modal.svelte';
|
||||
import UserEditForm from '$lib/components/UserEditForm.svelte';
|
||||
import { t } from 'svelte-i18n';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
/** @type {import('./$types').ActionData} */
|
||||
export let form;
|
||||
@@ -12,10 +12,10 @@
|
||||
users = [],
|
||||
licence_categories = [],
|
||||
subscriptions = [],
|
||||
payments = [],
|
||||
payments = []
|
||||
} = $page.data);
|
||||
|
||||
let activeSection = "users";
|
||||
let activeSection = 'users';
|
||||
/** @type{App.Locals['user'] | null} */
|
||||
let selectedUser = null;
|
||||
let showModal = false;
|
||||
@@ -57,7 +57,7 @@
|
||||
class="nav-link {activeSection === 'users' ? 'active' : ''}"
|
||||
on:click={() => setActiveSection('users')}
|
||||
>
|
||||
<i class="fas fa-users" />
|
||||
<i class="fas fa-users"></i>
|
||||
<span class="nav-badge">{users.length}</span>
|
||||
{$t('users')}
|
||||
</button>
|
||||
@@ -67,7 +67,7 @@
|
||||
class="nav-link {activeSection === 'subscriptions' ? 'active' : ''}"
|
||||
on:click={() => setActiveSection('subscriptions')}
|
||||
>
|
||||
<i class="fas fa-clipboard-list" />
|
||||
<i class="fas fa-clipboard-list"></i>
|
||||
<span class="nav-badge">{subscriptions.length}</span>
|
||||
{$t('subscriptions')}
|
||||
</button>
|
||||
@@ -77,7 +77,7 @@
|
||||
class="nav-link {activeSection === 'payments' ? 'active' : ''}"
|
||||
on:click={() => setActiveSection('payments')}
|
||||
>
|
||||
<i class="fas fa-credit-card" />
|
||||
<i class="fas fa-credit-card"></i>
|
||||
{$t('payments')}
|
||||
</button>
|
||||
</li>
|
||||
@@ -90,7 +90,7 @@
|
||||
<div class="section-header">
|
||||
<h2>{$t('users')}</h2>
|
||||
<button class="btn primary" on:click={() => openEditModal(null)}>
|
||||
<i class="fas fa-plus" />
|
||||
<i class="fas fa-plus"></i>
|
||||
{$t('add_new')}
|
||||
</button>
|
||||
</div>
|
||||
@@ -124,11 +124,11 @@
|
||||
</table>
|
||||
<div class="button-group">
|
||||
<button class="btn primary" on:click={() => openEditModal(user)}>
|
||||
<i class="fas fa-edit" />
|
||||
<i class="fas fa-edit"></i>
|
||||
{$t('edit')}
|
||||
</button>
|
||||
<button class="btn danger">
|
||||
<i class="fas fa-trash" />
|
||||
<i class="fas fa-trash"></i>
|
||||
{$t('delete')}
|
||||
</button>
|
||||
</div>
|
||||
@@ -140,7 +140,7 @@
|
||||
<div class="section-header">
|
||||
<h2>{$t('subscriptions')}</h2>
|
||||
<button class="btn primary" on:click={() => openEditModal(null)}>
|
||||
<i class="fas fa-plus" />
|
||||
<i class="fas fa-plus"></i>
|
||||
{$t('add_new')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user