add: frontend profile data etc

This commit is contained in:
$(pass /github/name)
2024-09-10 18:52:32 +02:00
parent f0b2409963
commit b34a85e9d6
21 changed files with 1927 additions and 693 deletions

View File

@@ -3,7 +3,7 @@
import { applyAction, enhance } from "$app/forms";
import { page } from "$app/stores";
// import Developer from "$lib/img/hero-image.png";
import Avatar from "$lib/img/teamavatar.png";
import Avatar from "$lib/img/TeamAvatar.jpeg";
onMount(() => {
console.log("Page data in Header:", $page);
});

View File

@@ -0,0 +1,117 @@
<script>
// @ts-nocheck
export let avatar;
export let fieldName;
export let title;
let newAvatar;
const onFileSelected = (e) => {
const target = e.target;
if (target && target.files) {
let reader = new FileReader();
reader.readAsDataURL(target.files[0]);
reader.onload = (e) => {
newAvatar = e.target?.result;
};
}
};
</script>
<div id="app">
{#if avatar}
<img class="avatar" src={avatar} alt="d" />
{:else}
<img
class="avatar"
src={newAvatar
? newAvatar
: "https://cdn4.iconfinder.com/data/icons/small-n-flat/24/user-alt-512.png"}
alt=""
/>
<input
type="file"
id="file"
name={fieldName}
required
on:change={(e) => onFileSelected(e)}
/>
<label for="file" class="btn-3">
{#if newAvatar}
<span>Bild ausgewählt, clicke Hochladen.</span>
{:else}
<span>{title}</span>
{/if}
</label>
{/if}
</div>
<style>
#app {
margin-top: 1rem;
display: flex;
align-items: center;
justify-content: center;
flex-flow: column;
color: rgb(148 163 184);
}
.avatar {
display: flex;
width: 8rem;
}
[type="file"] {
height: 0;
overflow: hidden;
width: 0;
}
[type="file"] + label {
background: #9b9b9b;
border: none;
border-radius: 5px;
color: #fff;
cursor: pointer;
display: inline-block;
font-weight: 500;
margin-bottom: 1rem;
outline: none;
padding: 1rem 50px;
position: relative;
transition: all 0.3s;
vertical-align: middle;
}
[type="file"] + label:hover {
background-color: #9b9b9b;
}
[type="file"] + label.btn-3 {
background-color: #d43aff;
border-radius: 0;
overflow: hidden;
}
[type="file"] + label.btn-3 span {
display: inline-block;
height: 100%;
transition: all 0.3s;
width: 100%;
}
[type="file"] + label.btn-3::before {
color: #fff;
content: "\01F4F7";
font-size: 200%;
height: 100%;
left: 45%;
position: absolute;
top: -180%;
transition: all 0.3s;
width: 100%;
}
[type="file"] + label.btn-3:hover {
background-color: rgba(14, 166, 236, 0.5);
}
[type="file"] + label.btn-3:hover span {
transform: translateY(300%);
}
[type="file"] + label.btn-3:hover::before {
top: 0;
}
</style>

View File

@@ -0,0 +1,100 @@
<script>
import { createEventDispatcher } from "svelte";
import { t } from "svelte-i18n";
/** @type {string} */
export let name;
/** @type {string} */
export let type = "text";
/** @type {string} */
export let value = "";
/** @type {string} */
export let placeholder = "";
/** @type {string} */
export let label = "";
const dispatch = createEventDispatcher();
/**
* @param {Event} event - The input event
*/
function handleInput(event) {
const target = event.target;
if (target instanceof HTMLInputElement) {
const inputValue = target.value;
value = inputValue;
// dispatch("input", { name, value: inputValue });
}
}
/**
* Validates the field
* @param {string} name - The name of the field
* @param {string} value - The value of the field
* @returns {string|null} The error message or null if valid
*/
function validateField(name, value) {
switch (name) {
case "first_name":
case "last_name":
return value.trim() ? null : $t("required");
case "email":
return /^\S+@\S+\.\S+$/.test(value) ? null : $t("invalid_email");
case "password":
return !value.trim() || value.length >= 8
? null
: $t("required_password");
// case "password2":
// // Note: This case might need special handling as it depends on another field
// return $t("required_password_match");
default:
return null;
}
}
$: error = validateField(name, value);
</script>
<div class="input-box">
<span class="label">{label}</span>
<div class="input-error-container">
{#if error}
<span class="error-message">{error}</span>
{/if}
<input
{name}
{type}
{placeholder}
{value}
on:input={handleInput}
on:blur={handleInput}
class="input"
/>
</div>
</div>
<style>
.input-error-container {
display: flex;
flex-direction: column;
align-items: flex-end;
width: 100%;
max-width: 444px;
}
.error-message {
color: #eb5424;
font-size: 12px;
margin-bottom: 5px;
align-self: flex-start;
}
.input {
width: 100%;
}
</style>

View File

@@ -0,0 +1,127 @@
<script>
import { quintOut } from "svelte/easing";
import { createEventDispatcher } from "svelte";
const modal = (/** @type {Element} */ node, { duration = 300 } = {}) => {
const transform = getComputedStyle(node).transform;
return {
duration,
easing: quintOut,
css: (/** @type {any} */ t, /** @type {number} */ u) => {
return `transform:
${transform}
scale(${t})
translateY(${u * -100}%)
`;
},
};
};
const dispatch = createEventDispatcher();
function closeModal() {
dispatch("close", {});
}
</script>
<div class="modal-background">
<div
transition:modal={{ duration: 1000 }}
class="modal"
role="dialog"
aria-modal="true"
>
<!-- svelte-ignore a11y-missing-attribute -->
<a
title="Close"
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">Close modal</span>
</a>
<div class="container">
<slot />
</div>
</div>
</div>
<style>
.modal-background {
width: 100%;
height: 100%;
position: fixed;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.9);
z-index: 9999;
display: flex;
}
.modal {
position: relative;
left: 50%;
top: 50%;
width: 70%;
box-shadow: 0 0 10px hsl(0 0% 0% / 10%);
transform: translate(-50%, -50%);
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
@media (max-width: 990px) {
.modal {
width: 90%;
}
}
.modal-close {
border: none;
}
.modal-close svg {
display: block;
margin-left: auto;
margin-right: auto;
fill: rgb(14 165 233 /1);
transition: all 0.5s;
}
.modal-close:hover svg {
fill: rgb(225 29 72);
transform: scale(1.5);
}
.modal .container {
max-height: 90vh;
overflow-y: auto;
align-items: center;
}
@media (min-width: 680px) {
.modal .container {
flex-direction: column;
left: 0;
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,24 @@
<script>
/** @type {number | null} */
export let width;
/** @type {string | null} */
export let message;
</script>
<div class="loading">
<p class="simple-loader" style={width ? `width: ${width}px` : ""} />
{#if message}
<p>{message}</p>
{/if}
</div>
<style>
.loading {
display: flex;
align-items: center;
justify-content: center;
}
.loading p {
margin-left: 0.5rem;
}
</style>