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
type='submit'
disabled={ info.allowButtonAction }
className={ info.allowButtonAction === false ? "button-disabled" : "" }
>
{ info.button_value }
</button>
@ -145,7 +147,17 @@ const TextInputGenerator = ({
id={input.name + info.action + info.endpoint + 'Input'}
autoComplete='off'
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>
)
}
@ -178,7 +190,17 @@ const PasswordInputGenerator = ({
autoComplete='off'
ref={input.ref}
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>
)
}

View File

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

View File

@ -1,9 +1,9 @@
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 userCrudAsyncThunk from '../../../redux/asyncThunks/userCrudAsyncThunk'
// import { userCrudSelector } from '../../../redux/slices/userCrudSlice'
// import userCrudAsyncThunk from '../../../redux/asyncThunks/userCrudAsyncThunk'
import FormGenerator from '../formGenerator'
@ -12,52 +12,126 @@ const UserRegisterForm = () => {
const usernameInput = React.createRef()
const passwordInput = React.createRef()
const emailInput = React.createRef()
const confirmPasswordInput = React.createRef()
const dispatch = useDispatch()
const { info } = useSelector( userCrudSelector )
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 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 = [
usernameInput,
passwordInput,
emailInput
confirmPasswordInput
]
let inputList = [
{
type: 'info',
action: 'Create',
endpint: 'user/auth/login',
button_value: 'Sign Up'
endpint: 'user/auth/register',
button_value: 'SIGN UP',
allowButtonAction: allowButtonAction
},
{
type: 'text',
name: 'Username',
ref: usernameInput
name: 'EMAIL',
ref: usernameInput,
onChange: usernameValidation,
validationInfo: usernameValidationInfo
},
{
type: 'password',
name: 'Password',
ref: passwordInput
name: 'PASSWORD',
ref: passwordInput,
onChange: passwordValidation,
validationInfo: passwordValidationInfo
},
{
type: 'text',
name: 'Email',
ref: emailInput
type: 'password',
name: 'CONFIRM PASSWORD',
ref: confirmPasswordInput,
onChange: confirmPasswordValidation,
validationInfo: confirmPasswordValidationInfo
}
]
const register = async ( refs ) => {
let pass = {
username: refs[0].current.value,
password: refs[1].current.value,
email: refs[2].current.value,
password: refs[1].current.value
}
dispatch(
userCrudAsyncThunk.fetchRegister(
pass
)
)
// dispatch(
// userCrudAsyncThunk.fetchRegister(
// pass
// )
// )
}
return (

View File

@ -1,9 +1,9 @@
import React from 'react';
import '../styles/LoginPage.cube.scss';
import '../styles/large.cube.scss';
const GraphicContainerComponent = () => {
const LargeCubeComponent = () => {
return (
<div className="graphic-container">
<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 GraphicContainerComponent from '../components/graphic-container.js';
import UserRegisterForm from '../components/forms/user_auth/userRegister.js';
import FootComponent from '../components/foot.js';
const LandingPage = () => {
return (
<div className="login-container">
<div className="landing-container">
<header className="landing">
<h2>Affordable. Efficient. Accessible.</h2>
<h1>GUARANTED.</h1>
<p>
Minimize your expenses without compromising on quality.
<br />
Our platform is built for individuals
Our platform is built for individuals who need top-tier rendering
<br />
<span className="span-white">
who need top-tier rendering without the top-tier investment.
without the top-tier investment.
</span>
</p>
<button>
GET A DEMO
</button>
<h4>
GPU IS IMPORTANT...
</h4>
<p>
<span className="span-first-color">
GPU Is Important...
...But <span className="span-white">GPU server can be very expensive.</span>
<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>
<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>
</header>
<main className="main-content">
<GraphicContainerComponent />
<main className="secondary-content">
<UserRegisterForm />
</main>
<FootComponent />
</div>
)
}

View File

@ -1,9 +1,8 @@
import React from 'react';
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';
@ -14,7 +13,7 @@ const LoginPage = () => {
<h1>XGPU</h1>
</header>
<main className="main-content">
<GraphicContainerComponent />
<LargeCubeComponent />
<UserLoginForm />
</main>
</div>

View File

@ -20,8 +20,7 @@ $font-family-default: neue-haas-unica, sans-serif;
body, html {
margin: 0 auto;
height: 100%;
overflow: hidden;
overflow-x: hidden;
font-family: $font-family-default;
tab-size: 4;
background: $background-color;
@ -33,8 +32,6 @@ body, html {
// #0a670a 60%,
// black 100%
// );
width: 100%;
height: 100%;
.header {
text-align: center;
@ -49,9 +46,15 @@ body, html {
}
}
.landing-container {
display: flex;
flex-direction: column;
width: 100%;
.landing {
text-align: center;
padding: 20px;
width: 100%;
background: $header-background;
color: $title-color;
z-index: 3; // Ensure the login form is above the cube
@ -69,6 +72,19 @@ body, html {
color: $title-color;
}
h3 {
font-size: 45px;
font-weight: 700;
color: $title-color;
}
h4 {
font-size: 30px;
font-weight: 700;
color: $title-color;
margin-bottom: 0px;
}
p {
font-size: 20px;
font-weight: 400;
@ -111,13 +127,23 @@ body, html {
color: black
}
}
}
.main-content {
//flex: 1;
//display: flex;
margin-top: 0%;
align-items: center;
justify-content: space-evenly;
}
.secondary-content {
width: 100%;
background: $form-background;
.form {
background: none;
margin-top: 0px;
}
}
.form {
background: $form-background;
@ -140,6 +166,8 @@ body, html {
label {
display: block;
margin-bottom: 15px;
text-align: left;
font-weight: 700;
}
input {
@ -157,7 +185,33 @@ body, html {
}
input:active {
background-color: rgba(0,128,0,1);
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;
}
}
@ -168,7 +222,7 @@ body, html {
border-radius: 5px;
background: $button-background;
font-family: $font-family-default;
font-weight: 800;
font-weight: 700;
color: rgba(0,0,0,0.5);
border: none;
cursor: pointer;
@ -179,6 +233,63 @@ body, html {
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;
}
}