add: frontend profile data etc
This commit is contained in:
@@ -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);
|
||||
});
|
||||
|
||||
117
frontend/src/lib/components/ImageInput.svelte
Normal file
117
frontend/src/lib/components/ImageInput.svelte
Normal 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>
|
||||
100
frontend/src/lib/components/InputField.svelte
Normal file
100
frontend/src/lib/components/InputField.svelte
Normal 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>
|
||||
127
frontend/src/lib/components/Modal.svelte
Normal file
127
frontend/src/lib/components/Modal.svelte
Normal 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>
|
||||
24
frontend/src/lib/components/SmallLoader.svelte
Normal file
24
frontend/src/lib/components/SmallLoader.svelte
Normal 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>
|
||||
Reference in New Issue
Block a user