feat(improvements): improve landing page & add new components + forms validation

improve landing page, improve register component (add proffesional validation, change styles + etc),
add footer component (with small rotated cube as logo), improve formGenerator
feat/x_gpu/new_version
TBS093A 2024-05-05 23:51:55 +02:00
parent b91896e6a5
commit 9da564e2e6
11 changed files with 571 additions and 146 deletions

View File

@ -0,0 +1,91 @@
import React from 'react';
import '../styles/general.scss';
import SmallCubeComponent from './smallCube.js';
const FootComponent = () => {
return (
<footer>
<div className="content-container">
<div className="content">
<div className="section">
<SmallCubeComponent />
</div>
<div className="section">
<h4>
<a>
BUSINESS
</a>
</h4>
<h4>
<a>
SIGN IN
</a>
</h4>
<h4>
<a>
SIGN UP
</a>
</h4>
</div>
</div>
<div className="content">
<div className="section">
<h4>
Company
</h4>
</div>
<div className="section">
<a>
About Us
</a>
</div>
<div className="section">
<a>
Contact Us
</a>
</div>
</div>
<div className="content">
<div className="section">
<h4>
Resources
</h4>
</div>
<div className="section">
<a>
Blog
</a>
</div>
<div className="section">
<a>
Help Center
</a>
</div>
</div>
<div className="content">
<div className="section">
</div>
</div>
<div className="content">
<div className="section">
</div>
</div>
<div className="content">
<div className="section">
</div>
</div>
</div>
<div className="bottom">
<p>
&copy; {new Date().getFullYear()} FehuDev. All rights reserved.
</p>
</div>
</footer>
)
}
export default FootComponent

View File

@ -109,6 +109,8 @@ export const FormGenerator = ({
? <></> ? <></>
: <button : <button
type='submit' type='submit'
disabled={ info.allowButtonAction }
className={ info.allowButtonAction === false ? "button-disabled" : "" }
> >
{ info.button_value } { info.button_value }
</button> </button>
@ -145,7 +147,17 @@ const TextInputGenerator = ({
id={input.name + info.action + info.endpoint + 'Input'} id={input.name + info.action + info.endpoint + 'Input'}
autoComplete='off' autoComplete='off'
ref={input.ref} ref={input.ref}
onChange={input.onChange}
className={ [ "Empty", "Success"].includes(input.validationInfo) ? "" : "input-incorrect" }
/> />
<div
className="popup"
style={ [ "Empty", "Success"].includes(input.validationInfo) ? {"display": "none", "height": "0px"} : {"display": "block"} }
>
<div className="popup-content">
{ input.validationInfo }
</div>
</div>
</div> </div>
) )
} }
@ -178,7 +190,17 @@ const PasswordInputGenerator = ({
autoComplete='off' autoComplete='off'
ref={input.ref} ref={input.ref}
type='password' type='password'
onChange={input.onChange}
className={ [ "Empty", "Success"].includes(input.validationInfo) ? "" : "input-incorrect" }
/> />
<div
className="popup"
style={ [ "Empty", "Success"].includes(input.validationInfo) ? {"display": "none", "height": "0px"} : {"display": "block"} }
>
<div className="popup-content">
{ input.validationInfo }
</div>
</div>
</div> </div>
) )
} }

View File

@ -15,7 +15,7 @@ const UserLoginForm = () => {
// const dispatch = useDispatch() // const dispatch = useDispatch()
// const { info } = useSelector( userAuthSelector ) // const { info } = useSelector( userAuthSelector )
const info = "" const info = "" // if redux is integrated - delete this line
let refList = [ let refList = [
usernameInput, usernameInput,

View File

@ -1,9 +1,9 @@
import React, { useState, useEffect } from 'react' import React, { useState, useEffect } from 'react'
import { useSelector, useDispatch } from 'react-redux' // import { useSelector, useDispatch } from 'react-redux'
import { userCrudSelector } from '../../../redux/slices/userCrudSlice' // import { userCrudSelector } from '../../../redux/slices/userCrudSlice'
import userCrudAsyncThunk from '../../../redux/asyncThunks/userCrudAsyncThunk' // import userCrudAsyncThunk from '../../../redux/asyncThunks/userCrudAsyncThunk'
import FormGenerator from '../formGenerator' import FormGenerator from '../formGenerator'
@ -12,52 +12,126 @@ const UserRegisterForm = () => {
const usernameInput = React.createRef() const usernameInput = React.createRef()
const passwordInput = React.createRef() const passwordInput = React.createRef()
const emailInput = React.createRef() const confirmPasswordInput = React.createRef()
const dispatch = useDispatch() const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
const { info } = useSelector( userCrudSelector ) const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
const [usernameValidationInfo, setUsernameValidationInfo] = useState("Empty")
const [passwordValidationInfo, setPasswordValidationInfo] = useState("Empty")
const [confirmPasswordValidationInfo, setConfirmPasswordValidationInfo] = useState("Empty")
const [password, setPassword] = useState("")
const [confirmPassword, setConfirmPassword] = useState("")
const [allowButtonAction, setAllowButtonAction] = useState(false)
const usernameValidation = (event) => {
if (event.target.value === "") {
setUsernameValidationInfo("Email is required.")
} else if(!emailRegex.test(event.target.value)) {
setUsernameValidationInfo("Please provide correct email")
} else {
setUsernameValidationInfo("Success")
}
}
const passwordValidation = (event) => {
setPassword(event.target.value)
if (event.target.value === "") {
setPasswordValidationInfo("Password is required.")
} else if(!passwordRegex.test(event.target.value)) {
setPasswordValidationInfo("Password require:\n - At least 8 characters,\n - At least one uppercase letter,\n - At least one lowercase letter,\n - At least one digit,\n - At least one special character.")
} else {
setPasswordValidationInfo("Success")
}
if(event.target.value !== confirmPassword) {
setConfirmPasswordValidationInfo("Passwords are different.")
} else {
setConfirmPasswordValidationInfo("Success")
}
}
const confirmPasswordValidation = (event) => {
setConfirmPassword(event.target.value)
if(event.target.value !== password) {
setConfirmPasswordValidationInfo("Passwords are different.")
} else {
setConfirmPasswordValidationInfo("Success")
}
}
useEffect(() => {
setAllowButtonAction(
usernameValidationInfo === "Success"
&& passwordValidationInfo === "Success"
&& confirmPasswordValidationInfo === "Success"
)
}, [
allowButtonAction,
usernameValidationInfo,
passwordValidationInfo,
confirmPasswordValidationInfo
]
)
// const dispatch = useDispatch()
// const { info } = useSelector( userCrudSelector )
const info = "" // if redux is integrated - delete this line
let refList = [ let refList = [
usernameInput, usernameInput,
passwordInput, passwordInput,
emailInput confirmPasswordInput
] ]
let inputList = [ let inputList = [
{ {
type: 'info', type: 'info',
action: 'Create', action: 'Create',
endpint: 'user/auth/login', endpint: 'user/auth/register',
button_value: 'Sign Up' button_value: 'SIGN UP',
allowButtonAction: allowButtonAction
}, },
{ {
type: 'text', type: 'text',
name: 'Username', name: 'EMAIL',
ref: usernameInput ref: usernameInput,
onChange: usernameValidation,
validationInfo: usernameValidationInfo
}, },
{ {
type: 'password', type: 'password',
name: 'Password', name: 'PASSWORD',
ref: passwordInput ref: passwordInput,
onChange: passwordValidation,
validationInfo: passwordValidationInfo
}, },
{ {
type: 'text', type: 'password',
name: 'Email', name: 'CONFIRM PASSWORD',
ref: emailInput ref: confirmPasswordInput,
onChange: confirmPasswordValidation,
validationInfo: confirmPasswordValidationInfo
} }
] ]
const register = async ( refs ) => { const register = async ( refs ) => {
let pass = { let pass = {
username: refs[0].current.value, username: refs[0].current.value,
password: refs[1].current.value, password: refs[1].current.value
email: refs[2].current.value,
} }
dispatch( // dispatch(
userCrudAsyncThunk.fetchRegister( // userCrudAsyncThunk.fetchRegister(
pass // pass
) // )
) // )
} }
return ( return (

View File

@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
import '../styles/LoginPage.cube.scss'; import '../styles/large.cube.scss';
const GraphicContainerComponent = () => { const LargeCubeComponent = () => {
return ( return (
<div className="graphic-container"> <div className="graphic-container">
<div className="cube"> <div className="cube">
@ -19,4 +19,4 @@ const GraphicContainerComponent = () => {
} }
export default GraphicContainerComponent export default LargeCubeComponent

View File

@ -0,0 +1,25 @@
import React from 'react';
import '../styles/small.cube.scss';
const SmallCubeComponent = () => {
return (
<div className="graphic-container">
<div className="cube">
<div className="face-front"></div>
<div className="face-back"></div>
<div className="face-right"></div>
<div className="face-left"></div>
<div className="face-top"></div>
<div className="face-bottom"></div>
</div>
<div className="title">
XGPU
</div>
</div>
)
}
export default SmallCubeComponent

View File

@ -2,36 +2,83 @@ import React from 'react';
import '../styles/general.scss'; import '../styles/general.scss';
import GraphicContainerComponent from '../components/graphic-container.js'; import UserRegisterForm from '../components/forms/user_auth/userRegister.js';
import FootComponent from '../components/foot.js';
const LandingPage = () => { const LandingPage = () => {
return ( return (
<div className="login-container"> <div className="landing-container">
<header className="landing"> <header className="landing">
<h2>Affordable. Efficient. Accessible.</h2> <h2>Affordable. Efficient. Accessible.</h2>
<h1>GUARANTED.</h1> <h1>GUARANTED.</h1>
<p> <p>
Minimize your expenses without compromising on quality. Minimize your expenses without compromising on quality.
<br /> <br />
Our platform is built for individuals Our platform is built for individuals who need top-tier rendering
<br />
<span className="span-white"> <span className="span-white">
who need top-tier rendering without the top-tier investment. without the top-tier investment.
</span> </span>
</p> </p>
<button> <button>
GET A DEMO GET A DEMO
</button> </button>
<h4>
GPU IS IMPORTANT...
</h4>
<p> <p>
<span className="span-first-color"> ...But <span className="span-white">GPU server can be very expensive.</span>
GPU Is Important... <br />
Configuration and administration<span className="span-white"> take too long time.</span>
<br />
<span className="span-white">
Everything of that needs so much additional knowledge.
</span>
</p>
<h2>
We Offer Ready Solution.
</h2>
<p>
Boost your GPU power for your 3D and AI models stuff
<br />
<span className="span-white">
without overpay for servers...
</span> </span>
<br /> <br />
...but <span className="span-white">
...without overtime for environment adjustments!
</span>
</p>
<h2>
"OK... So What Makes You So Different?"
</h2>
<h4>
GUARANTED
</h4>
<p>
We only win if you win. You won't carry all the risk, we'll share it
</p>
<h4>
RESLUTS
</h4>
<p>
Our first priority is to get you results.
</p>
<h4>
LOCAL
</h4>
<p>
</p>
<h4>
SPECIALIZED
</h4>
<p>
</p> </p>
</header> </header>
<main className="main-content"> <main className="secondary-content">
<GraphicContainerComponent /> <UserRegisterForm />
</main> </main>
<FootComponent />
</div> </div>
) )
} }

View File

@ -1,9 +1,8 @@
import React from 'react'; import React from 'react';
import '../../styles/general.scss'; import '../../styles/general.scss';
import '../../styles/LoginPage.cube.scss';
import GraphicContainerComponent from '../../components/graphic-container.js'; import LargeCubeComponent from '../../components/largeCube.js';
import UserLoginForm from '../../components/forms/user_auth/userLogin.js'; import UserLoginForm from '../../components/forms/user_auth/userLogin.js';
@ -14,7 +13,7 @@ const LoginPage = () => {
<h1>XGPU</h1> <h1>XGPU</h1>
</header> </header>
<main className="main-content"> <main className="main-content">
<GraphicContainerComponent /> <LargeCubeComponent />
<UserLoginForm /> <UserLoginForm />
</main> </main>
</div> </div>

View File

@ -20,8 +20,7 @@ $font-family-default: neue-haas-unica, sans-serif;
body, html { body, html {
margin: 0 auto; margin: 0 auto;
height: 100%; overflow-x: hidden;
overflow: hidden;
font-family: $font-family-default; font-family: $font-family-default;
tab-size: 4; tab-size: 4;
background: $background-color; background: $background-color;
@ -33,8 +32,6 @@ body, html {
// #0a670a 60%, // #0a670a 60%,
// black 100% // black 100%
// ); // );
width: 100%;
height: 100%;
.header { .header {
text-align: center; text-align: center;
@ -49,135 +46,249 @@ body, html {
} }
} }
.landing { .landing-container {
text-align: center; display: flex;
padding: 20px; flex-direction: column;
background: $header-background; width: 100%;
color: $title-color;
z-index: 3; // Ensure the login form is above the cube
position: relative; // Required for z-index to take effect
h1 { .landing {
font-size: 60px; text-align: center;
font-weight: 800; padding: 20px;
color: $title-color; width: 100%;
} background: $header-background;
color: $title-color;
z-index: 3; // Ensure the login form is above the cube
position: relative; // Required for z-index to take effect
h2 { h1 {
font-size: 60px; font-size: 60px;
font-weight: 700; font-weight: 800;
color: $title-color; color: $title-color;
} }
p { h2 {
font-size: 20px; font-size: 60px;
font-weight: 400; font-weight: 700;
color: $subtitle-color; color: $title-color;
} }
span { h3 {
font-size: 20px; font-size: 45px;
font-weight: 400; font-weight: 700;
padding-left: 5px; color: $title-color;
} }
.span-white { h4 {
color: $title-color; font-size: 30px;
} font-weight: 700;
color: $title-color;
margin-bottom: 0px;
}
.span-first-color { p {
color: $first-color; font-size: 20px;
} font-weight: 400;
color: $subtitle-color;
}
span {
font-size: 20px;
font-weight: 400;
padding-left: 5px;
}
.span-white {
color: $title-color;
}
.span-first-color {
color: $first-color;
}
button { button {
margin-top: 35px; margin-top: 35px;
margin-bottom: 35px; margin-bottom: 35px;
padding-top: 20px; padding-top: 20px;
padding: 25px; padding: 25px;
border-radius: 5px; border-radius: 5px;
background: $button-background; background: $button-background;
font-size: 20px; font-size: 20px;
font-family: $font-family-default; font-family: $font-family-default;
font-weight: 800; font-weight: 800;
color: rgba(0,0,0,0.5); color: rgba(0,0,0,0.5);
border: none; border: none;
cursor: pointer; cursor: pointer;
transition-duration: 0.1s; transition-duration: 0.1s;
} }
button:hover { button:hover {
background-color: green; background-color: green;
color: black color: black
}
} }
} }
.main-content { .main-content {
//flex: 1;
//display: flex;
margin-top: 0%; margin-top: 0%;
align-items: center; align-items: center;
justify-content: space-evenly; justify-content: space-evenly;
}
.secondary-content {
width: 100%;
background: $form-background;
.form { .form {
background: $form-background; background: none;
padding: 50px; margin-top: 0px;
width: 350px; }
color: $subtitle-color; }
// border: 2px solid $border-color;
border-radius: 10px;
margin-top: -400px; .form {
margin-right: auto; background: $form-background;
margin-left: auto; padding: 50px;
width: 350px;
color: $subtitle-color;
// border: 2px solid $border-color;
border-radius: 10px;
z-index: 2; // Ensure the login form is above the cube margin-top: -400px;
position: relative; // Required for z-index to take effect margin-right: auto;
margin-left: auto;
.form-field { z-index: 2; // Ensure the login form is above the cube
position: relative; // Required for z-index to take effect
.form-field {
margin-bottom: 15px;
label {
display: block;
margin-bottom: 15px; margin-bottom: 15px;
text-align: left;
label { font-weight: 700;
display: block;
margin-bottom: 15px;
}
input {
width: 90%;
padding: 5%;
background: $input-background;
border: 1px solid $border-color;
border-radius: 5px;
color: white;
transition-duration: 0.5s;
}
input:hover {
border-color: rgba(111,108,106,1);
}
input:active {
background-color: rgba(0,128,0,1);
}
} }
button { input {
width: 90%;
padding: 5%; padding: 5%;
width: 100%; background: $input-background;
margin-top: 30px; border: 1px solid $border-color;
border-radius: 5px; border-radius: 5px;
background: $button-background; color: white;
font-family: $font-family-default; transition-duration: 0.5s;
font-weight: 800;
color: rgba(0,0,0,0.5);
border: none;
cursor: pointer;
transition-duration: 0.1s;
} }
button:hover { input:hover {
background-color: green; border-color: rgba(111,108,106,1);
color: black }
input:active {
background-color: rgba(0,120,0,1);
}
.input-correct {
background: rgba(0,120,0,1);
border: 1px solid $border-color;
}
.input-incorrect {
background: rgba(120,8,0,1);
border: 1px solid red;
}
}
.popup {
margin-top: 10px;
overflow: hidden;
padding: 15px;
background: rgba(120,8,0,0.5);
border: 1px solid red;
border-radius: 5px;
white-space: pre-line;
transition-duration: 0.5s;
.popup-content {
margin: 0 auto;
text-align: left;
}
}
button {
padding: 5%;
width: 100%;
margin-top: 30px;
border-radius: 5px;
background: $button-background;
font-family: $font-family-default;
font-weight: 700;
color: rgba(0,0,0,0.5);
border: none;
cursor: pointer;
transition-duration: 0.1s;
}
button:hover {
background-color: green;
color: black
}
.button-disabled {
background: gray;
}
}
footer {
width: 70%;
background: $form-background;
text-align: left;
padding: 20px 15%;
.content-container {
display: flex;
justify-content: space-between;
.content {
flex-direction: column;
margin-bottom: 20px;
.section {
flex: 1;
margin-top: 20px;
margin-bottom: 20px;
h4 {
color: $title-color;
margin-bottom: 5px;
margin-top: 5px;
}
.graphic-container {
margin-top: 20px;
}
p, a {
color: $subtitle-color;
line-height: 1.6;
text-decoration: none;
}
a:hover {
color: $title-color;
}
}
}
}
.bottom {
border-top: 1px solid $subtitle-color;
padding-top: 10px;
text-align: center;
p {
font-size: 0.8em;
color: $subtitle-color;
} }
} }
} }

View File

@ -0,0 +1,56 @@
$cube-size: 30px;
$cube-color: rgba(0, 140, 0, 0.5);
$perspective: 10000px;
$animation-duration: 20s;
@mixin cube-face {
position: absolute;
width: $cube-size - 4px;
height: $cube-size - 4px;
background: $cube-color;
}
@keyframes rotateCube {
0% {
transform: rotateX(0) rotateY(0) rotateZ(0);
}
100% {
transform: rotateX(360deg) rotateY(360deg) rotateZ(360deg);
}
}
.graphic-container {
display: flex;
perspective: $perspective;
perspective-origin: 50% 100px; // Adjusted for better 3D effect
z-index: 1; // Lower z-index than the login form to place it behind
.cube {
width: $cube-size;
height: $cube-size;
position: relative;
margin: auto; // Centers the cube within the graphic container
transform-style: preserve-3d;
transform-origin: center center; // Rotates around the center of the cube
animation: rotateCube $animation-duration infinite linear;
div {
@include cube-face;
}
.face-front { transform: translateZ($cube-size / 2); }
.face-back { transform: translateZ(-$cube-size / 2) rotateY(180deg); }
.face-right { transform: rotateY(90deg) translateZ($cube-size / 2); }
.face-left { transform: rotateY(-90deg) translateZ($cube-size / 2); }
.face-top { transform: rotateX(90deg) translateZ($cube-size / 2); }
.face-bottom { transform: rotateX(-90deg) translateZ($cube-size / 2); }
}
.title {
color: white;
font-size: 30px;
font-weight: 600;
margin-left: 10px;
}
}