Upgrade design & moved backend here & login and settings work with api

This commit is contained in:
2024-09-29 18:04:41 +02:00
parent 42d3212624
commit f2d1eb4289
43 changed files with 1881 additions and 359 deletions
+80
View File
@@ -0,0 +1,80 @@
'use client';
// ---------------------------------------------------------------------------------------------------------------------
//! Imports
// ---------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------ React --------------------------------------------------------
import { FormEvent } from 'react';
// ---------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------- Assets & Styles ---------------------------------------------------
import CheckIcon from '@/assets/CheckIcon';
import CloseIcon from '@/assets/CloseIcon';
import './styles.scss';
// ---------------------------------------------------------------------------------------------------------------------
const InlineInput = ({
label = '',
name = '',
type = 'text',
placeholder = '',
defaultValue = '',
autoComplete = 'off',
onSubmit = (value?: string) => {},
}) => {
const handleSubmit = (ev: FormEvent<HTMLFormElement>) => {
ev.preventDefault();
if (!ev.currentTarget.reportValidity()) {
return;
}
const formData = new FormData(ev.currentTarget);
const value = formData.get(name);
onSubmit(value ? value.toString() : null);
};
return (
<form
className='inlineInputContainer'
onSubmit={handleSubmit}
>
<label
htmlFor={name}
className='inlineInputLabel'
>
{label}
</label>
<div className='inlineInputRow'>
<input
type={type}
name={name}
id={name}
defaultValue={defaultValue}
placeholder={placeholder}
autoComplete={autoComplete}
className='inlineInput'
required
/>
<button
type='reset'
className='inlineInputButton'
>
<CloseIcon />
</button>
<button
type='submit'
className='inlineInputButton'
>
<CheckIcon />
</button>
</div>
</form>
);
};
export default InlineInput;
+50
View File
@@ -0,0 +1,50 @@
.inlineInputContainer {
display: flex;
flex-direction: column;
gap: 5px;
& > .inlineInputLabel {
margin-left: 5px;
font-size: 1.1rem;
color: #d94253;
}
& > .inlineInputRow {
display: grid;
grid-template-columns: 1fr repeat(2, calc(1rem + 14px));
align-items: center;
gap: 5px;
& > .inlineInput {
padding: 7px 14px;
border: 1px solid #d94253;
border-radius: 5px;
background-color: #f5cfd3;
outline: none;
font-size: 0.9rem;
color: #d94253;
outline: none;
&::placeholder {
color: #d94253;
}
}
& > .inlineInputButton {
display: flex;
justify-content: center;
align-items: center;
padding: 5px;
border: none;
border-radius: 5px;
background-color: transparent;
color: #d94253;
cursor: pointer;
transition: background-color 0.3s ease;
&:hover {
background-color: rgba(#d94253, 0.5);
}
}
}
}
@@ -5,7 +5,7 @@
// ---------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------------ React --------------------------------------------------------
import { forwardRef, useState } from 'react';
import { forwardRef, MouseEvent, useState } from 'react';
// ---------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------- Assets & Styles ---------------------------------------------------
@@ -14,75 +14,51 @@ import EyeIcon from '@/assets/EyeIcon';
import './styles.scss';
// ---------------------------------------------------------------------------------------------------------------------
const Input = forwardRef(function InputComponent(
{
type = 'text',
label,
name,
value,
defaultValue,
autoComplete,
required = false,
onChange,
onKeyUp,
onKeyDown,
},
ref
) {
const [isFocused, setIsFocused] = useState(
defaultValue || value ? true : false
);
const [passwordVisibility, setPasswordVisibility] = useState(false);
const Input = ({
name = '',
type = '',
placeholder = '',
defaultValue = '',
required = false,
autoComplete = 'off',
}) => {
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
const defaultOnBlur = (ev) => {
if (ev.currentTarget.value != '') {
return;
}
const togglePasswordVisibility = (ev: MouseEvent) => {
ev.stopPropagation();
setIsFocused(false);
setIsPasswordVisible((prev) => !prev);
};
return (
<div className='inputContainer'>
<input
ref={ref}
type={
type == 'password'
? passwordVisibility
? isPasswordVisible
? 'text'
: 'password'
: type
}
name={name}
id={name}
value={value}
placeholder={placeholder}
defaultValue={defaultValue}
autoComplete={autoComplete}
required={required}
onChange={onChange}
onKeyUp={onKeyUp}
onKeyDown={onKeyDown}
className='input'
onFocus={() => setIsFocused(true)}
onBlur={defaultOnBlur}
/>
<label
htmlFor={name}
className={`inputLabel ${isFocused ? 'focused' : ''}`}
>
{label}
</label>
{type == 'password' && (
<button
type='button'
className='passwordToggleButton'
onClick={() => setPasswordVisibility((prev) => !prev)}
onClick={togglePasswordVisibility}
>
{passwordVisibility ? <EyeSlashIcon /> : <EyeIcon />}
{isPasswordVisible ? <EyeSlashIcon /> : <EyeIcon />}
</button>
)}
</div>
);
});
};
export default Input;
+10 -26
View File
@@ -2,35 +2,19 @@
position: relative;
font-size: 1.1rem;
& > .inputLabel {
position: absolute;
top: 48%;
left: 10px;
transform: translateY(-50%);
padding: 0 4px;
background-color: #d94253;
font-size: 1em;
font-weight: 300;
color: #ffffff;
transition: top 0.3s ease, font-size 0.3s ease;
pointer-events: none;
&.focused {
top: 0;
font-size: 0.8em;
}
}
& > .input {
padding: 10px 20px;
padding: 7px 14px;
width: 100%;
border: 1px solid #ffffff;
border: 1px solid #d94253;
border-radius: 5px;
background-color: #d94253;
font-size: 1em;
font-weight: 300;
color: #ffffff;
background-color: #f5cfd3;
font-size: 0.9rem;
color: #d94253;
outline: none;
&::placeholder {
color: #d94253;
}
}
& > .passwordToggleButton {
@@ -45,6 +29,6 @@
height: 100%;
border: none;
background-color: transparent;
color: #ffffff;
color: #d94253;
}
}
-34
View File
@@ -1,34 +0,0 @@
'use client';
import Link from 'next/link';
import config from './config';
import './styles.scss';
import { usePathname } from 'next/navigation';
const Navbar = () => {
const pathname = usePathname();
return (
<nav
className='navbar'
style={{
gridTemplateColumns: `repeat(${config.length}, 1fr)`,
}}
>
{config.map((element) => (
<Link
key={element.title}
href={element.link}
className={`navButton ${
element.link == pathname ? 'selected' : ''
}`}
>
{element.icon()}
<span className='navButtonLabel'>{element.title}</span>
</Link>
))}
</nav>
);
};
export default Navbar;
+46
View File
@@ -0,0 +1,46 @@
'use client';
// ---------------------------------------------------------------------------------------------------------------------
//! Imports
// ---------------------------------------------------------------------------------------------------------------------
// --------------------------------------------------- Components ------------------------------------------------------
import Link from 'next/link';
// ---------------------------------------------------------------------------------------------------------------------
// -------------------------------------------------- Hooks & Utils ----------------------------------------------------
import { usePathname } from 'next/navigation';
import config from './config';
// ---------------------------------------------------------------------------------------------------------------------
// ------------------------------------------------- Assets & Styles ---------------------------------------------------
import './styles.scss';
// ---------------------------------------------------------------------------------------------------------------------
const Navbar = () => {
const pathname = usePathname();
return (
<nav
className='navbar'
style={{
gridTemplateColumns: `repeat(${config.length}, 1fr)`,
}}
>
{config.map((element) => (
<Link
key={element.title}
href={element.link}
className={`navButton ${
element.link == pathname ? 'selected' : ''
}`}
>
{element.icon()}
<span className='navButtonLabel'>{element.title}</span>
</Link>
))}
</nav>
);
};
export default Navbar;