frontend: initial commit
This commit is contained in:
24
frontend/src/lib/components/Footer.svelte
Normal file
24
frontend/src/lib/components/Footer.svelte
Normal file
@@ -0,0 +1,24 @@
|
||||
<script>
|
||||
// import Developer from "$lib/img/hero-image.png";
|
||||
|
||||
const year = new Date().getFullYear();
|
||||
</script>
|
||||
|
||||
<footer class="footer-container">
|
||||
<div class="footer-branding-container">
|
||||
<div class="footer-branding">
|
||||
<a class="footer-crafted-by-container" href="https://github.com/Sirneij">
|
||||
<span>Developed by</span>
|
||||
<!-- <img
|
||||
class="footer-branded-crafted-img"
|
||||
src={Developer}
|
||||
alt="Alexander Stölting"
|
||||
/> -->
|
||||
</a>
|
||||
|
||||
<span class="footer-copyright"
|
||||
>© {year} Alexander Stölting. All Rights Reserved.</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
79
frontend/src/lib/components/Header.svelte
Normal file
79
frontend/src/lib/components/Header.svelte
Normal file
@@ -0,0 +1,79 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
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";
|
||||
onMount(() => {
|
||||
console.log("Page data in Header:", $page);
|
||||
});
|
||||
|
||||
$: {
|
||||
console.log("Page data updated:", $page);
|
||||
}
|
||||
</script>
|
||||
|
||||
<header class="header">
|
||||
<div class="header-container">
|
||||
<div class="header-left">
|
||||
<div class="header-crafted-by-container">
|
||||
<!-- <a href="https://tiny-bits.net/">
|
||||
<span>Developed by</span><img
|
||||
src={Developer}
|
||||
alt="Alexander Stölting"
|
||||
/> -->
|
||||
<!-- </a> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div class="header-nav-item" class:active={$page.url.pathname === "/"}>
|
||||
<a href="/">home</a>
|
||||
</div>
|
||||
{#if !$page.data.user}
|
||||
<div
|
||||
class="header-nav-item"
|
||||
class:active={$page.url.pathname === "/auth/login"}
|
||||
>
|
||||
<a href="/auth/login">login</a>
|
||||
</div>
|
||||
<!-- <div
|
||||
class="header-nav-item"
|
||||
class:active={$page.url.pathname === "/auth/register"}
|
||||
>
|
||||
<a href="/auth/register">register</a>
|
||||
</div> -->
|
||||
{:else}
|
||||
<div class="header-nav-item">
|
||||
<a href="/auth/about/{$page.data.user.id}">
|
||||
<img
|
||||
src={$page.data.user.thumbnail
|
||||
? $page.data.user.thumbnail
|
||||
: Avatar}
|
||||
alt={`${$page.data.user.first_name} ${$page.data.user.last_name}`}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<!-- {#if $page.data.user.is_superuser}
|
||||
<div
|
||||
class="header-nav-item"
|
||||
class:active={$page.url.pathname.startsWith("/auth/admin")}
|
||||
>
|
||||
<a href="/auth/admin">admin</a>
|
||||
</div>
|
||||
{/if} -->
|
||||
<form
|
||||
class="header-nav-item"
|
||||
action="/auth/logout"
|
||||
method="POST"
|
||||
use:enhance={async () => {
|
||||
return async ({ result }) => {
|
||||
await applyAction(result);
|
||||
};
|
||||
}}
|
||||
>
|
||||
<button type="submit">logout</button>
|
||||
</form>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
14
frontend/src/lib/components/Transition.svelte
Normal file
14
frontend/src/lib/components/Transition.svelte
Normal file
@@ -0,0 +1,14 @@
|
||||
<script>
|
||||
import { slide } from "svelte/transition";
|
||||
/** @type {string} */
|
||||
export let key;
|
||||
|
||||
/** @type {number} */
|
||||
export let duration = 300;
|
||||
</script>
|
||||
|
||||
{#key key}
|
||||
<div in:slide={{ duration, delay: duration }} out:slide={{ duration }}>
|
||||
<slot />
|
||||
</div>
|
||||
{/key}
|
||||
521
frontend/src/lib/css/styles.css
Normal file
521
frontend/src/lib/css/styles.css
Normal file
@@ -0,0 +1,521 @@
|
||||
@font-face {
|
||||
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");
|
||||
}
|
||||
@font-face {
|
||||
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");
|
||||
}
|
||||
|
||||
html {
|
||||
padding: 0 30px;
|
||||
background-color: black;
|
||||
color: #9b9b9b;
|
||||
font-family: "Quicksand", sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
}
|
||||
body {
|
||||
max-width: 1200px;
|
||||
margin: 5em auto 0 auto;
|
||||
}
|
||||
pre,
|
||||
code {
|
||||
display: inline;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
input {
|
||||
font-family: "Roboto Mono", monospace;
|
||||
color: white;
|
||||
border-style: none;
|
||||
height: 21px;
|
||||
font-size: 16px;
|
||||
}
|
||||
button {
|
||||
font-size: 16px;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
h2 {
|
||||
margin: 0 0 45px 0;
|
||||
color: #fff;
|
||||
font-size: 36px;
|
||||
}
|
||||
h3 {
|
||||
margin: 0 0 2rem 0;
|
||||
color: #fff;
|
||||
font-size: 32px;
|
||||
}
|
||||
p {
|
||||
margin: 0 0 45px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
ul {
|
||||
margin: 0 0 32px;
|
||||
}
|
||||
a {
|
||||
transition: border 0.2s ease-in-out;
|
||||
border-bottom: 1px solid transparent;
|
||||
text-decoration: none;
|
||||
color: #00b7ef;
|
||||
}
|
||||
a:hover {
|
||||
border-bottom-color: #00b7ef;
|
||||
}
|
||||
li {
|
||||
line-height: 1.8;
|
||||
}
|
||||
li strong {
|
||||
color: #fff;
|
||||
}
|
||||
.image {
|
||||
width: 100%;
|
||||
margin: 0 0 32px;
|
||||
padding: 0;
|
||||
}
|
||||
.image img {
|
||||
width: 100%;
|
||||
}
|
||||
.optanon-alert-box-wrapper {
|
||||
left: 0;
|
||||
}
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
.hide-mobile {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
body {
|
||||
margin: 8em auto 0 auto;
|
||||
}
|
||||
.hide-mobile {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 3em 0 0;
|
||||
background: black;
|
||||
}
|
||||
.header.top-banner-open {
|
||||
margin-top: 5px;
|
||||
transition: all 0.2s linear;
|
||||
}
|
||||
.header .header-container {
|
||||
width: 100%;
|
||||
max-width: calc(1200px + 10em);
|
||||
height: 5em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.header .header-container .header-left {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container {
|
||||
font-size: 18px;
|
||||
font-weight: 300;
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container a {
|
||||
display: flex;
|
||||
color: #9b9b9b;
|
||||
border: none;
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container a img {
|
||||
height: 28px;
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container a span {
|
||||
display: inline-block;
|
||||
margin: 2px 1ch 0 0;
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container .auth0 {
|
||||
margin-left: 1ch;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
.header .header-container .header-right {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: space-between;
|
||||
letter-spacing: 1px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item {
|
||||
text-transform: uppercase;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item button {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item.active a,
|
||||
.header .header-container .header-right .header-nav-item.active button {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.header .header-container .header-right a img {
|
||||
margin-top: -0.4rem;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.header .header-container .header-right .header-nav-item a,
|
||||
.header .header-container .header-right .header-nav-item button {
|
||||
transition: color 0.3s ease-in-out;
|
||||
display: block;
|
||||
padding: 20px 0;
|
||||
border: none;
|
||||
color: #9b9b9b;
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item:hover a,
|
||||
.header .header-container .header-right .header-nav-item:hover button {
|
||||
color: #fdfff5;
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
.header {
|
||||
padding: 3em 5rem 0;
|
||||
}
|
||||
.header.top-banner-open {
|
||||
margin-top: 48px;
|
||||
}
|
||||
.header .header-container {
|
||||
flex-direction: row;
|
||||
}
|
||||
.header .header-container .header-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item {
|
||||
margin-left: 26px;
|
||||
}
|
||||
}
|
||||
.button-dark {
|
||||
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: transparent;
|
||||
border: 1px solid #595b5c;
|
||||
}
|
||||
.button-dark:hover {
|
||||
border-color: #fff;
|
||||
}
|
||||
.button-colorful {
|
||||
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;
|
||||
}
|
||||
.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;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
padding: 18px 28px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
background-color: #eb5424;
|
||||
border: 1px solid #eb5424;
|
||||
}
|
||||
.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;
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
padding: 18px 28px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
background-color: #9a9a9a;
|
||||
border: 1px solid #9a9a9a;
|
||||
}
|
||||
.hero-container {
|
||||
max-width: 795px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0 auto 70px auto;
|
||||
}
|
||||
.hero-container .hero-logo {
|
||||
margin-top: 88px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.hero-container .hero-subtitle {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
line-height: 32px;
|
||||
margin: 0 0 45px 0;
|
||||
}
|
||||
.hero-container .hero-buttons-container {
|
||||
display: flex;
|
||||
}
|
||||
.hero-container .hero-buttons-container button {
|
||||
margin: 0 8px;
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
.hero-container {
|
||||
margin: 0 auto 140px auto;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
color: white;
|
||||
letter-spacing: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.container .content {
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.container .content .step-title {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
line-height: 86px;
|
||||
opacity: 1;
|
||||
}
|
||||
.container .content .step-subtitle {
|
||||
position: relative;
|
||||
top: -5px;
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
.container .content {
|
||||
margin-top: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 680px) {
|
||||
.container {
|
||||
position: relative;
|
||||
left: 100px;
|
||||
display: flex;
|
||||
width: calc(100% - 100px);
|
||||
padding: 0;
|
||||
}
|
||||
.container .content {
|
||||
max-width: 795px;
|
||||
padding-left: 116px;
|
||||
}
|
||||
.container .content .step-title {
|
||||
font-size: 36px;
|
||||
}
|
||||
}
|
||||
.input-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 10px 0;
|
||||
padding: 0 20px;
|
||||
width: 100%;
|
||||
height: 73px;
|
||||
box-sizing: border-box;
|
||||
background-color: #2f2f2f;
|
||||
border-radius: 3px;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
.input-box .label {
|
||||
text-transform: lowercase;
|
||||
margin: 0 1ch 0 0;
|
||||
}
|
||||
.input-box .input {
|
||||
background-color: #494848;
|
||||
border-radius: 6px;
|
||||
outline: none;
|
||||
border: 3px solid #494848;
|
||||
width: 100%;
|
||||
max-width: 444px;
|
||||
font-size: 13px;
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
.input-box {
|
||||
padding: 0 30px;
|
||||
margin: 32px 0;
|
||||
}
|
||||
}
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: first baseline;
|
||||
}
|
||||
@media (max-width: 680px) {
|
||||
.btn-container {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.btn-container p {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
.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);
|
||||
border-radius: 6px;
|
||||
color: rgb(225 29 72);
|
||||
font-size: 16px;
|
||||
}
|
||||
.warning a {
|
||||
color: rgb(225 29 72);
|
||||
text-decoration: underline;
|
||||
}
|
||||
.warning.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.error {
|
||||
margin-top: 10rem;
|
||||
padding: 30px 40px;
|
||||
background: #2f3132;
|
||||
color: #fff;
|
||||
}
|
||||
.error p {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
.error p.intro {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
.error .button-colorful {
|
||||
display: inline-block;
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
.error {
|
||||
padding: 65px 80px;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-branding-container {
|
||||
color: white;
|
||||
font-weight: 300;
|
||||
margin-bottom: 73px;
|
||||
}
|
||||
|
||||
.footer-branding-container .footer-branding {
|
||||
display: flex;
|
||||
width: 400px;
|
||||
}
|
||||
.footer-branding-container .footer-branding {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
margin: 30px 0 0;
|
||||
}
|
||||
.footer-branding-container .footer-branding .footer-crafted-by-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16px;
|
||||
color: white;
|
||||
}
|
||||
.footer-branding-container .footer-branding .footer-crafted-by-container span {
|
||||
display: inline-block;
|
||||
margin: 3px 1ch 0 0;
|
||||
}
|
||||
.footer-branding-container
|
||||
.footer-branding
|
||||
.footer-crafted-by-container
|
||||
.footer-branded-crafted-img {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.footer-branding-container .footer-branding .footer-copyright {
|
||||
color: #696969;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.footer-container {
|
||||
width: 100%;
|
||||
color: white;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (min-width: 680px) {
|
||||
.footer-container {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.simple-loader {
|
||||
--b: 20px; /* border thickness */
|
||||
--n: 15; /* number of dashes*/
|
||||
--g: 7deg; /* gap between dashes*/
|
||||
--c: #d43aff; /* the color */
|
||||
|
||||
width: 40px; /* size */
|
||||
aspect-ratio: 1;
|
||||
border-radius: 50%;
|
||||
padding: 1px; /* get rid of bad outlines */
|
||||
background: conic-gradient(#0000, var(--c)) content-box;
|
||||
--_m: /* we use +/-1deg between colors to avoid jagged edges */ repeating-conic-gradient(
|
||||
#0000 0deg,
|
||||
#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))
|
||||
);
|
||||
-webkit-mask: var(--_m);
|
||||
mask: var(--_m);
|
||||
-webkit-mask-composite: destination-in;
|
||||
mask-composite: intersect;
|
||||
animation: load 1s infinite steps(var(--n));
|
||||
}
|
||||
@keyframes load {
|
||||
to {
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
505
frontend/src/lib/css/styles.min.css
vendored
Normal file
505
frontend/src/lib/css/styles.min.css
vendored
Normal file
@@ -0,0 +1,505 @@
|
||||
@font-face {
|
||||
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");
|
||||
}
|
||||
@font-face {
|
||||
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");
|
||||
}
|
||||
html {
|
||||
padding: 0 30px;
|
||||
background-color: #000;
|
||||
color: #9b9b9b;
|
||||
font-family: Quicksand, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
body {
|
||||
max-width: 1200px;
|
||||
margin: 5em auto 0 auto;
|
||||
}
|
||||
code,
|
||||
pre {
|
||||
display: inline;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-size: 16px;
|
||||
}
|
||||
input {
|
||||
font-family: "Roboto Mono", monospace;
|
||||
color: #fff;
|
||||
border-style: none;
|
||||
height: 21px;
|
||||
font-size: 16px;
|
||||
}
|
||||
button {
|
||||
font-size: 16px;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
}
|
||||
h2 {
|
||||
margin: 0 0 45px 0;
|
||||
color: #fff;
|
||||
font-size: 36px;
|
||||
}
|
||||
h3 {
|
||||
margin: 0 0 2rem 0;
|
||||
color: #fff;
|
||||
font-size: 32px;
|
||||
}
|
||||
p {
|
||||
margin: 0 0 45px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
ul {
|
||||
margin: 0 0 32px;
|
||||
}
|
||||
a {
|
||||
transition: border 0.2s ease-in-out;
|
||||
border-bottom: 1px solid transparent;
|
||||
text-decoration: none;
|
||||
color: #00b7ef;
|
||||
}
|
||||
a:hover {
|
||||
border-bottom-color: #00b7ef;
|
||||
}
|
||||
li {
|
||||
line-height: 1.8;
|
||||
}
|
||||
li strong {
|
||||
color: #fff;
|
||||
}
|
||||
.image {
|
||||
width: 100%;
|
||||
margin: 0 0 32px;
|
||||
padding: 0;
|
||||
}
|
||||
.image img {
|
||||
width: 100%;
|
||||
}
|
||||
.optanon-alert-box-wrapper {
|
||||
left: 0;
|
||||
}
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
.hide-mobile {
|
||||
display: none;
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
body {
|
||||
margin: 8em auto 0 auto;
|
||||
}
|
||||
.hide-mobile {
|
||||
display: initial;
|
||||
}
|
||||
}
|
||||
.header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 3em 0 0;
|
||||
background: #000;
|
||||
}
|
||||
.header.top-banner-open {
|
||||
margin-top: 5px;
|
||||
transition: all 0.2s linear;
|
||||
}
|
||||
.header .header-container {
|
||||
width: 100%;
|
||||
max-width: calc(1200px + 10em);
|
||||
height: 5em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.header .header-container .header-left {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container {
|
||||
font-size: 18px;
|
||||
font-weight: 300;
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container a {
|
||||
display: flex;
|
||||
color: #9b9b9b;
|
||||
border: none;
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container a img {
|
||||
height: 28px;
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container a span {
|
||||
display: inline-block;
|
||||
margin: 2px 1ch 0 0;
|
||||
}
|
||||
.header .header-container .header-left .header-crafted-by-container .auth0 {
|
||||
margin-left: 1ch;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
.header .header-container .header-right {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
justify-content: space-between;
|
||||
letter-spacing: 1px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item {
|
||||
text-transform: uppercase;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item button {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item.active a,
|
||||
.header .header-container .header-right .header-nav-item.active button {
|
||||
color: #fff;
|
||||
}
|
||||
.header .header-container .header-right a img {
|
||||
margin-top: -0.4rem;
|
||||
height: 28px;
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item a,
|
||||
.header .header-container .header-right .header-nav-item button {
|
||||
transition: color 0.3s ease-in-out;
|
||||
display: block;
|
||||
padding: 20px 0;
|
||||
border: none;
|
||||
color: #9b9b9b;
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item:hover a,
|
||||
.header .header-container .header-right .header-nav-item:hover button {
|
||||
color: #fdfff5;
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
.header {
|
||||
padding: 3em 5rem 0;
|
||||
}
|
||||
.header.top-banner-open {
|
||||
margin-top: 48px;
|
||||
}
|
||||
.header .header-container {
|
||||
flex-direction: row;
|
||||
}
|
||||
.header .header-container .header-right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.header .header-container .header-right .header-nav-item {
|
||||
margin-left: 26px;
|
||||
}
|
||||
}
|
||||
.button-dark {
|
||||
transition:
|
||||
border-color 0.3s ease-in-out,
|
||||
background-color 0.3s ease-in-out;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
padding: 18px 28px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
border: 1px solid #595b5c;
|
||||
}
|
||||
.button-dark:hover {
|
||||
border-color: #fff;
|
||||
}
|
||||
.button-colorful {
|
||||
transition:
|
||||
border-color 0.3s ease-in-out,
|
||||
background-color 0.3s ease-in-out;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
padding: 18px 28px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
background-color: #d43aff;
|
||||
border: 1px solid #d43aff;
|
||||
}
|
||||
.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;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
padding: 18px 28px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
background-color: #eb5424;
|
||||
border: 1px solid #eb5424;
|
||||
}
|
||||
.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;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
padding: 18px 28px;
|
||||
letter-spacing: 1px;
|
||||
cursor: pointer;
|
||||
background-color: #9a9a9a;
|
||||
border: 1px solid #9a9a9a;
|
||||
}
|
||||
.hero-container {
|
||||
max-width: 795px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin: 0 auto 70px auto;
|
||||
}
|
||||
.hero-container .hero-logo {
|
||||
margin-top: 88px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.hero-container .hero-subtitle {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
line-height: 32px;
|
||||
margin: 0 0 45px 0;
|
||||
}
|
||||
.hero-container .hero-buttons-container {
|
||||
display: flex;
|
||||
}
|
||||
.hero-container .hero-buttons-container button {
|
||||
margin: 0 8px;
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
.hero-container {
|
||||
margin: 0 auto 140px auto;
|
||||
}
|
||||
}
|
||||
.container {
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
color: #fff;
|
||||
letter-spacing: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
.container .content {
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.container .content .step-title {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
line-height: 86px;
|
||||
opacity: 1;
|
||||
}
|
||||
.container .content .step-subtitle {
|
||||
position: relative;
|
||||
top: -5px;
|
||||
font-size: 16px;
|
||||
font-weight: 300;
|
||||
}
|
||||
@media (max-width: 680px) {
|
||||
.container .content {
|
||||
margin-top: 120px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
.container {
|
||||
position: relative;
|
||||
left: 100px;
|
||||
display: flex;
|
||||
width: calc(100% - 100px);
|
||||
padding: 0;
|
||||
}
|
||||
.container .content {
|
||||
max-width: 795px;
|
||||
padding-left: 116px;
|
||||
}
|
||||
.container .content .step-title {
|
||||
font-size: 36px;
|
||||
}
|
||||
}
|
||||
.input-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 10px 0;
|
||||
padding: 0 20px;
|
||||
width: 100%;
|
||||
height: 73px;
|
||||
box-sizing: border-box;
|
||||
background-color: #2f2f2f;
|
||||
border-radius: 3px;
|
||||
font-family: "Roboto Mono", monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
.input-box .label {
|
||||
text-transform: lowercase;
|
||||
margin: 0 1ch 0 0;
|
||||
}
|
||||
.input-box .input {
|
||||
background-color: #494848;
|
||||
border-radius: 6px;
|
||||
outline: 0;
|
||||
border: 3px solid #494848;
|
||||
width: 100%;
|
||||
max-width: 444px;
|
||||
font-size: 13px;
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
.input-box {
|
||||
padding: 0 30px;
|
||||
margin: 32px 0;
|
||||
}
|
||||
}
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: first baseline;
|
||||
}
|
||||
@media (max-width: 680px) {
|
||||
.btn-container {
|
||||
align-items: flex-start;
|
||||
}
|
||||
.btn-container p {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
.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);
|
||||
border-radius: 6px;
|
||||
color: rgb(225 29 72);
|
||||
font-size: 16px;
|
||||
}
|
||||
.warning a {
|
||||
color: rgb(225 29 72);
|
||||
text-decoration: underline;
|
||||
}
|
||||
.warning.hidden {
|
||||
display: none;
|
||||
}
|
||||
.error {
|
||||
margin-top: 10rem;
|
||||
padding: 30px 40px;
|
||||
background: #2f3132;
|
||||
color: #fff;
|
||||
}
|
||||
.error p {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
.error p.intro {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
.error .button-colorful {
|
||||
display: inline-block;
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
.error {
|
||||
padding: 65px 80px;
|
||||
}
|
||||
}
|
||||
.footer-branding-container {
|
||||
color: #fff;
|
||||
font-weight: 300;
|
||||
margin-bottom: 73px;
|
||||
}
|
||||
.footer-branding-container .footer-branding {
|
||||
display: flex;
|
||||
width: 400px;
|
||||
}
|
||||
.footer-branding-container .footer-branding {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
margin: 30px 0 0;
|
||||
}
|
||||
.footer-branding-container .footer-branding .footer-crafted-by-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16px;
|
||||
color: #fff;
|
||||
}
|
||||
.footer-branding-container .footer-branding .footer-crafted-by-container span {
|
||||
display: inline-block;
|
||||
margin: 3px 1ch 0 0;
|
||||
}
|
||||
.footer-branding-container
|
||||
.footer-branding
|
||||
.footer-crafted-by-container
|
||||
.footer-branded-crafted-img {
|
||||
height: 28px;
|
||||
}
|
||||
.footer-branding-container .footer-branding .footer-copyright {
|
||||
color: #696969;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.footer-container {
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
@media (min-width: 680px) {
|
||||
.footer-container {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.simple-loader {
|
||||
--b: 20px;
|
||||
--n: 15;
|
||||
--g: 7deg;
|
||||
--c: #d43aff;
|
||||
width: 40px;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 50%;
|
||||
padding: 1px;
|
||||
background: conic-gradient(#0000, var(--c)) content-box;
|
||||
--_m: repeating-conic-gradient(
|
||||
#0000 0deg,
|
||||
#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))
|
||||
);
|
||||
-webkit-mask: var(--_m);
|
||||
mask: var(--_m);
|
||||
-webkit-mask-composite: destination-in;
|
||||
mask-composite: intersect;
|
||||
animation: load 1s infinite steps(var(--n));
|
||||
}
|
||||
@keyframes load {
|
||||
to {
|
||||
transform: rotate(1turn);
|
||||
}
|
||||
}
|
||||
3
frontend/src/lib/utils/constants.js
Normal file
3
frontend/src/lib/utils/constants.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export const BASE_API_URI = import.meta.env.DEV
|
||||
? import.meta.env.VITE_BASE_API_URI_DEV
|
||||
: import.meta.env.VITE_BASE_API_URI_PROD;
|
||||
105
frontend/src/lib/utils/helpers.js
Normal file
105
frontend/src/lib/utils/helpers.js
Normal file
@@ -0,0 +1,105 @@
|
||||
// @ts-nocheck
|
||||
import { quintOut } from "svelte/easing";
|
||||
import { crossfade } from "svelte/transition";
|
||||
|
||||
export const [send, receive] = crossfade({
|
||||
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;
|
||||
|
||||
return {
|
||||
duration: 600,
|
||||
easing: quintOut,
|
||||
css: (t) => `
|
||||
transform: ${transform} scale(${t});
|
||||
opacity: ${t}
|
||||
`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Validates an email field
|
||||
* @file lib/utils/helpers/input.validation.ts
|
||||
* @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());
|
||||
};
|
||||
/**
|
||||
* Validates a strong password field
|
||||
* @file lib/utils/helpers/input.validation.ts
|
||||
* @param {string} password - The password to validate
|
||||
*/
|
||||
export const isValidPasswordStrong = (password) => {
|
||||
const strongRegex = new RegExp(
|
||||
"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})"
|
||||
);
|
||||
|
||||
return strongRegex.test(password.trim());
|
||||
};
|
||||
/**
|
||||
* Validates a medium password field
|
||||
* @file lib/utils/helpers/input.validation.ts
|
||||
* @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,})"
|
||||
);
|
||||
|
||||
return mediumRegex.test(password.trim());
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether or not an object is empty.
|
||||
* @param {Record<string, string>} obj - The object to test
|
||||
* @returns `true` or `false`
|
||||
*/
|
||||
|
||||
export function isEmpty(obj) {
|
||||
for (const _i in obj) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Test whether or not an object is empty.
|
||||
* @param {any} obj - The object to test
|
||||
* @returns `true` or `false`
|
||||
*/
|
||||
|
||||
export function formatError(obj) {
|
||||
const errors = [];
|
||||
if (typeof obj === "object" && obj !== null) {
|
||||
if (Array.isArray(obj)) {
|
||||
obj.forEach((/** @type {Object} */ error) => {
|
||||
Object.keys(error).map((k) => {
|
||||
errors.push({
|
||||
error: error[k],
|
||||
id: Math.random() * 1000,
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Object.keys(obj).map((k) => {
|
||||
errors.push({
|
||||
error: obj[k],
|
||||
id: Math.random() * 1000,
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
errors.push({
|
||||
error: obj.charAt(0).toUpperCase() + obj.slice(1),
|
||||
id: 0,
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
105
frontend/src/lib/utils/utils.js
Normal file
105
frontend/src/lib/utils/utils.js
Normal file
@@ -0,0 +1,105 @@
|
||||
// @ts-nocheck
|
||||
import { quintOut } from "svelte/easing";
|
||||
import { crossfade } from "svelte/transition";
|
||||
|
||||
export const [send, receive] = crossfade({
|
||||
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;
|
||||
|
||||
return {
|
||||
duration: 600,
|
||||
easing: quintOut,
|
||||
css: (t) => `
|
||||
transform: ${transform} scale(${t});
|
||||
opacity: ${t}
|
||||
`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Validates an email field
|
||||
* @file lib/utils/helpers/input.validation.ts
|
||||
* @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());
|
||||
};
|
||||
/**
|
||||
* Validates a strong password field
|
||||
* @file lib/utils/helpers/input.validation.ts
|
||||
* @param {string} password - The password to validate
|
||||
*/
|
||||
export const isValidPasswordStrong = (password) => {
|
||||
const strongRegex = new RegExp(
|
||||
"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})"
|
||||
);
|
||||
|
||||
return strongRegex.test(password.trim());
|
||||
};
|
||||
/**
|
||||
* Validates a medium password field
|
||||
* @file lib/utils/helpers/input.validation.ts
|
||||
* @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,})"
|
||||
);
|
||||
|
||||
return mediumRegex.test(password.trim());
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether or not an object is empty.
|
||||
* @param {Record<string, string>} obj - The object to test
|
||||
* @returns `true` or `false`
|
||||
*/
|
||||
|
||||
export function isEmpty(obj) {
|
||||
for (const _i in obj) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Test whether or not an object is empty.
|
||||
* @param {any} obj - The object to test
|
||||
* @returns `true` or `false`
|
||||
*/
|
||||
|
||||
export function formatError(obj) {
|
||||
const errors = [];
|
||||
if (typeof obj === "object" && obj !== null) {
|
||||
if (Array.isArray(obj)) {
|
||||
obj.forEach((/** @type {Object} */ error) => {
|
||||
Object.keys(error).map((k) => {
|
||||
errors.push({
|
||||
error: error[k],
|
||||
id: Math.random() * 1000,
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Object.keys(obj).map((k) => {
|
||||
errors.push({
|
||||
error: obj[k],
|
||||
id: Math.random() * 1000,
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
errors.push({
|
||||
error: obj.charAt(0).toUpperCase() + obj.slice(1),
|
||||
id: 0,
|
||||
});
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
Reference in New Issue
Block a user