Compare commits

..

No commits in common. "5301940a2f6ab21b6b6bb8eb280d21e2dced2c83" and "e5492401fd869a71aa4d6a338098ae1fe0bae4d7" have entirely different histories.

38 changed files with 4302 additions and 73439 deletions

13447
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,24 @@
{
"homepage": "https://alttd.com/forms",
"name": "forms",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^6.2.0",
"@testing-library/react": "^14.1.2",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.5.11",
"@types/node": "^20.10.6",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.69",
"@types/react": "^18.2.47",
"@types/react-dom": "^18.2.18",
"formik": "^2.4.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0",
"react-scripts": "^5.0.1",
"typescript": "^5.3.3",
"web-vitals": "^3.5.1",
"yup": "^1.3.3"
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "set \"PUBLIC_URL=/forms\" && react-scripts build",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
@ -43,9 +39,5 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/react-helmet": "^6.1.11",
"react-router-dom": "^6.21.1"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Binary file not shown.

View File

@ -1,36 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Survival Minecraft Server on 1.20.4! | Altitude Community</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description"
content="Start your adventure today! We are a 1.20.4 survival community with McMMO, MyPet, Dynmap, &amp; player shop based economy. Monthly server events. Best community in 1.20.4!">
<meta name="keywords"
content="minecraft,survival,server,servers,community,small,vanilla,semivanilla,dynmap,mcmmo,griefing,1.20.4,altitude,alttd,play,join,find,friends,friendly,simple,private,whitelist,whitelisted,creative,worldedit">
<meta name="author" content="Altitude Community">
<meta name="theme-color" content="#1f9bde">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- Twitter Card data -->
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="Community-centered Survival Minecraft Server"/>
<meta name="twitter:description"
content="Start your adventure today! We host 1.20.4 survival with features suggested by our community: McMMO, MyPet, Dynmap, &amp; economy. Monthly server events..."/>
<!-- Twitter Summary card images must be at least 120x120px -->
<meta name="twitter:image" content="https://www.alttd.com/assets/img/random/seo.jpg"/>
<!-- Open Graph data -->
<meta property="og:title" content="Community-centered 1.20.4 Survival Minecraft Server"/>
<meta property="og:type" content="article"/>
<meta property="og:url" content="https://www.alttd.com/"/>
<meta property="og:image" content="https://www.alttd.com/assets/img/random/seo.jpg"/>
<meta property="og:description"
content="Start your adventure today! We host 1.20.4 survival with features suggested by our community: McMMO, MyPet, Dynmap, &amp; economy. Monthly server events..."/>
<meta property="og:site_name" content="Altitude Community"/>
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
<link rel="icon" type="image/png" href="%PUBLIC_URL%/favicon.png"/>
</head>
<body>
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -1,141 +1,3 @@
:root{
--white: #FFFFFF;
--black: #282828;
--pureblack: #000000;
--font-color: #272727;
--grey: #A3A3A3;
--link-color: #1f9bde;
--linkhover: #00d9ff;
--navlink: #4b4b4b;
--switch: #FBFBFE;
--error: #c23c3c;
--footer-color: #e9e9e9;
}
@font-face {
font-family: 'opensans';
src:url('/public/fonts/opensans.ttf') format('truetype'),
url('/public/fonts/opensans.eot') format('embedded-opentype'),
url('/public/fonts/opensans.svg') format('svg'),
url('/public/fonts/opensans.woff') format('woff');
}
@font-face {
font-family: 'opensans-bold';
src:url('/public/fonts/opensans-bold.ttf') format('truetype'),
url('/public/fonts/opensans-bold.eot') format('embedded-opentype'),
url('/public/fonts/opensans-bold.svg') format('svg'),
url('/public/fonts/opensans-bold.woff') format('woff');
}
html, body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp,
small, strong, sub, sup, var,
b, i,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, figcaption, figure,
footer, header, hgroup, menu, nav, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-size: 100%;
vertical-align: baseline;
background: transparent;
}
.flex{
display: flex;
}
.row{
flex-direction: row;
}
.row-rev{
flex-direction: row-reverse;
}
.wrap{
flex-flow: row wrap;
}
.wrap-rev{
flex-flow: row wrap-reverse;
}
.column{
flex-direction: column;
}
.column-rev{
flex-direction: column-reverse;
}
.between{
justify-content: space-between;
}
.around{
justify-content: space-around;
}
.start{
justify-content: flex-start;
}
.end{
justify-content: flex-end;
}
.center{
justify-content: center;
}
/* css starts here */
a:hover{
color: var(--linkhover);
text-decoration: none;
}
h1 {
font-family: 'opensans-bold', sans-serif;
letter-spacing: 3px;
color: var(--font-color);
font-size: 1.7em;
margin-bottom: 30px;
font-weight: normal;
}
h2 {
font-family: 'opensans-bold',sans-serif;
font-size: 1.3em;
color: var(--font-color);
transition: 0.5s ease;
}
h3 {
font-family: 'opensans-bold',sans-serif;
font-size: 1.2em;
color: var(--font-color);
transition: 0.5s ease;
}
p {
font-family: 'opensans',sans-serif;
font-size: 1em;
color: var(--font-color);
transition: 0.5s ease;
}
.App {
text-align: center;
}
@ -152,29 +14,25 @@ p {
}
.App-header {
background-color: var(--color-primary);
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: var(--font-color);
color: white;
}
body {
display: flex;
flex-direction: column;
min-height: 100vh;
.App-link {
color: #61dafb;
}
.app-container {
display: flex;
flex-direction: column;
flex: 1 0 auto; /* This will make sure this container takes all available vertical space */
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#root {
display: flex;
flex-grow: 1;
}

View File

@ -1,29 +1,26 @@
import ContactForm from "./components/contact_form/contact";
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Home from "./components/home/home";
import {BrowserRouter, Route, Routes} from 'react-router-dom';
import Header from "./components/header/header";
import Footer from "./components/footer/footer";
import VerifyMail from "./components/verify_email/verify_mail";
import ThankYou from "./components/verify_email/thank_you";
import DEBUG from "./components/DEBUG/DEBUG";
function App() {
return (
<div className="app-container">
<Header/>
<BrowserRouter basename="/forms">
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/contact" element={<ContactForm/>}/>
<Route path="/verify-email" element={<VerifyMail/>}/>
<Route path="/thank-you" element={<ThankYou/>}/>
{process.env.NODE_ENV === 'development' && <Route path="/debug" element={<DEBUG/>}/>}
</Routes>
</BrowserRouter>
<Footer/>
</div>
);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
export default App;

View File

@ -1,11 +0,0 @@
.container {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
}
button {
margin: 10px;
cursor: pointer;
}

View File

@ -1,33 +0,0 @@
import {FC} from "react";
import {useNavigate} from 'react-router-dom';
import {NavigateOptions} from "react-router/dist/lib/context";
import './DEBUG.css'
const DEBUG: FC = () => {
const navigate = useNavigate()
const handleClick = (link: string, options: NavigateOptions) => {
navigate(link, options);
}
return (
<div className='container'>
<button onClick={() => handleClick('/verify-email', {
state: {
email: 'akastijn@alttd.com'
}
})}>Verify Email Debug</button>
<button onClick={() => handleClick('/thank-you', {
state: {
formData: {
row1: 'This is some text',
row2: 'This is text as well',
row3: 'And finally, here is even more text!'
}
}
})}>Thank You Debug</button>
{/*<button onClick={() => handleClick('/page3', {})}>Page 3</button>*/}
</div>
)
}
export default DEBUG;

View File

@ -1,49 +0,0 @@
.container {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
form {
max-width: 600px;
margin: auto;
padding: 20px;
}
h1 {
text-align: center;
color: #333;
}
label {
display: block;
margin-bottom: 20px;
}
input[type="text"],
input[type="email"],
textarea {
width: 100%;
padding: 10px;
font-size: 1em;
border-radius: 5px;
border: 1px solid #ccc;
}
button,
input[type="submit"] {
padding: 10px 20px;
font-size: 1em;
border-radius: 5px;
border: none;
color: #fff;
background-color: #007BFF;
cursor: pointer;
}
button[disabled],
input[type="submit"][disabled] {
background-color: #ccc;
}

View File

@ -1,152 +0,0 @@
import React, {useState} from "react";
import './Contact.css';
import {useNavigate} from 'react-router-dom'
import {validationSchema} from "./validationSchema";
import {ErrorMessage, Field, Form, Formik, FormikValues} from "formik";
type InputNames = "username" | "email" | "question";
interface Step {
label: string;
name: InputNames;
type: "text" | "email" | "textarea";
min_length: number;
max_length: number;
required: boolean;
pattern: string;
}
const ContactForm = () => {
const navigate = useNavigate();
const [currentStep, setCurrentStep] = useState<number>(0);
const steps: Step[] = [
{
label: "Username",
name: "username",
type: "text",
min_length: 3,
max_length: 16,
required: true,
pattern: ""
},
{
label: "Email",
name: "email",
type: "email",
min_length: 3,
max_length: 254,
required: true,
pattern: ""
},
{
label: "Question",
name: "question",
type: "textarea",
min_length: 10,
max_length: 2000,
required: true,
pattern: ""
},
]
const handleSubmit = async (e: FormikValues) => {
try {
const response = await fetch('http://localhost:8002/api/contact/submitContactForm', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(e)
})
if (!response.ok) {
alert("Your form submission was denied by the server, or the server was unable to process it, if you didn't mess with the data please contact the administrator at admin@alttd.com");
} else {
navigate('/verify-email', {
state: {
email: e['email']
}
});
}
} catch (e) {
alert("Your form submission was denied by the server, if you didn't mess with the data please contact the administrator at admin@alttd.com")
}
};
const next = () => {
setCurrentStep(current => current + 1)
}
const prev = () => {
setCurrentStep(current => Math.max(current - 1, 0))
}
const [prevLength, setPrevLength] = useState(0);
return (
<div className="container">
<div>
<h1>Contact Form</h1>
</div>
<div>
<Formik
initialValues={{username: '', email: '', question: ''}}
validationSchema={validationSchema}
onSubmit={(values: FormikValues) => {
handleSubmit(values);
}}
>
{({
touched,
errors,
isValid,
handleChange,
values,
setFieldTouched
}) => (
<Form>
<div>
<label>
{steps[currentStep].label}
<Field
type={steps[currentStep].type}
name={steps[currentStep].name}
required={steps[currentStep].required}
min={steps[currentStep].min_length}
max={steps[currentStep].max_length}
as={(steps[currentStep].type === "textarea") ? "textarea" : "input"}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
handleChange(e);
if (prevLength != values[steps[currentStep].name].length) {
setFieldTouched(steps[currentStep].name);
setPrevLength(values[steps[currentStep].name].length);
}
}}
/>
<ErrorMessage name={steps[currentStep].name} component="div"/>
</label>
</div>
<button type="button" onClick={prev} disabled={currentStep === 0}>
Previous
</button>
<button type="button" onClick={next}
hidden={currentStep === (steps.length - 1)}
disabled={(!touched[steps[currentStep].name] || !!errors[steps[currentStep].name]) || currentStep === (steps.length - 1)}>
Next
</button>
<input type="submit" value="Submit"
hidden={currentStep !== (steps.length - 1)}
disabled={!isValid || currentStep !== (steps.length - 1)}
/>
</Form>
)}
</Formik>
</div>
</div>
);
};
export default ContactForm;

View File

@ -1,20 +0,0 @@
import * as Yup from 'yup';
export const validationSchema = Yup.object().shape({
username: Yup.string()
.min(3, 'Username should be at least 3 characters')
.max(16, 'Username should not exceed 16 characters')
.matches(/^[a-zA-Z0-9_]*$/, 'Username should only include alphanumeric characters and underscore')
.required('Username is required'),
email: Yup.string()
.email('Invalid email')
.min(3, 'Email should be at least 3 characters')
.max(254, 'Email should not exceed 254 characters')
.required(),
question: Yup.string()
.min(10, 'Question should be at least 10 characters')
.max(2000, 'Question should not exceed 2000 characters')
.required('Question is required')
});

View File

@ -1,134 +0,0 @@
footer {
background-color: var(--footer-color);
transition: 0.5s ease;
}
ul {
padding: 0px;
}
a {
text-decoration: none;
color: var(--link-color);
}
#footer {
padding: 80px 0;
width: 80%;
max-width: 1020px;
margin: auto;
}
#footerinner {
display: flex;
justify-content: space-between;
}
#footertext {
flex-basis: 45%;
margin-right: 60px;
}
.footernav {
flex-grow: 1;
border-left: 1px solid rgb(19, 19, 19);
padding-left: 10px;
}
.footernav li {
padding-bottom: 5px;
}
footer ul, footer p {
list-style: none;
font-size: 0.9em;
color: var(--font-color);
font-family: 'opensans',sans-serif;
}
footer h2 {
margin-bottom: 10px;
font-size: 1.3em;
color: var(--font-color);
}
#copyright {
margin-top: 50px;
}
.followus {
padding-top: 15px;
height: 35px;
display: flex;
align-items: flex-end;
}
.followus img {
margin-right: 5px;
float: left;
filter: grayscale(100%);
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
transition: all 0.2s;
-webkit-transition: all 0.2s;
-moz-transition: all 0.2s;
}
.followus img:hover {
margin-bottom: 5px;
filter: grayscale(0%);
-webkit-filter: grayscale(0%);
-moz-filter: grayscale(0%);
transform: scale(1.1);
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1);
}
@media (max-width: 1150px) {
#footerinner {
flex-wrap: wrap;
text-align: center;
}
#footertext {
flex: 1 1 100%;
margin-right: 0;
margin-bottom: 30px;
}
.footernav {
border-left: none;
padding-left: 0px;
padding-bottom: 15px;
min-width: 200px;
}
.followus {
width: 100px;
margin: 0 auto;
}
.footernav {
border-left: none;
padding-left: 0px;
padding-bottom: 15px;
min-width: 200px;
}
#copyright {
margin-top: 30px;
text-align: center;
}
}
@media (max-width: 1000px) {
}
@media (max-width: 690px) {
}
@media (min-width: 690px) {
}

View File

@ -1,57 +0,0 @@
import React, { FC } from 'react';
import './Footer.css';
const Footer: FC = () => {
const version: string = "1.20.4"; // replace with your version
return (
<footer>
<div id="footer">
<div id="footerinner">
<div id="footertext">
<h2>ABOUT US</h2>
<p>Altitude is a community-centered {version} survival server. We're one of those
servers you come to call "home". We are your place to get together with friends and play
survival, with a few extra features suggested by our community!</p>
<div className="followus">
<a target="_blank" rel="noreferrer" href="https://discordapp.com/invite/TGqpzCJ">
<img src="https://alttd.com//assets/img/logos/discord.png" alt="Discord Button"/>
</a>
<a target="_blank" rel="noreferrer" href="https://twitter.com/alttdmc">
<img src="https://alttd.com//assets/img/logos/twitter.png" alt="Twitter Button"/>
</a>
<a target="_blank" rel="noreferrer" href="https://instagram.com/alttdmc">
<img src="https://alttd.com/assets/img/logos/instagram.png" alt="Instagram Button"/>
</a>
</div>
</div>
<div className="footernav">
<h2>COMMUNITY</h2>
<ul>
<li><a target="_blank" rel="noreferrer"
href="https://discordapp.com/invite/TGqpzCJ">Discord</a></li>
<li><a target="_blank" rel="noreferrer" href="https://alttd.com/blog">Blog</a></li>
<li><a target="_blank" rel="noreferrer" href="https://twitter.com/alttdmc">Twitter</a></li>
<li><a target="_blank" rel="noreferrer" href="https://instagram.com/alttdmc">Instagram</a>
</li>
<li><a target="_blank" rel="noreferrer" href="https://reddit.com/r/alttd">Reddit</a></li>
</ul>
</div>
<div className="footernav">
<h2>SERVER</h2>
<ul>
<li><a href="https://alttd.com/about">About Us</a></li>
<li><a href="https://alttd.com/team">Staffing Team</a></li>
<li><a href="https://alttd.com/policy">Privacy Policy</a></li>
<li><a href="https://alttd.com/terms">Terms of Use</a></li>
</ul>
</div>
</div>
<p id="copyright">Copyright © 2015-2023 Altitude. All rights Reserved. Not affiliated with Mojang AB or
Microsoft.</p>
</div>
</footer>
)
};
export default Footer;

View File

@ -1,5 +0,0 @@
#nav {
position: fixed;
width: 100%;
height: 110px;
}

View File

@ -1,15 +0,0 @@
import React, { FC } from 'react';
import './Header.css';
const Header: FC = () => {
return (
<header>
<div></div>
<div></div>
</header>
)
};
export default Header;

View File

@ -1,19 +0,0 @@
import React, { FunctionComponent } from 'react';
import { Helmet } from 'react-helmet';
const Home: FunctionComponent = () => {
return (
<div>
<Helmet>
<title>Forms</title>
<meta name="Forms home page" content="The home page for all Altitude forms"/>
</Helmet>
<header className="App-header">
<h1>Welcome to the Altitude forms page</h1>
<h2><a href="/contact">Contact us.</a></h2>
</header>
</div>
);
}
export default Home;

View File

@ -1,40 +0,0 @@
/*.fields {*/
/* display: flex;*/
/* flex-direction: column;*/
/* align-items: flex-start;*/
/* max-width: 1020px;*/
/* width: 80%*/
/*}*/
/*.field {*/
/* margin-bottom: -10px*/
/*}*/
.header {
font-size: 30px;
font-weight: bolder;
margin: 30px
}
.key {
margin: 1px;
font-size: 30px;
font-weight: bold;
text-align: left;
}
table {
width: 50%;
margin: 0 auto;
border-collapse: collapse;
}
.form-data-row {
border: 1px solid #ddd;
}
.form-data-key,
.form-data-value {
border: 1px solid #ddd;
padding: 10px;
}

View File

@ -1,30 +0,0 @@
.container {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.content {
max-width: 800px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.verification-code-input {
text-align: center;
max-width: 80px;
width: fit-content;
}
.submit-button {
margin-top: 20px;
}
header {
font-size: xx-large;
font-weight: bolder;
}

View File

@ -1,45 +0,0 @@
import {FC} from "react";
import {useLocation} from "react-router-dom";
import {Helmet} from "react-helmet";
import './ThankYou.css';
const ThankYou: FC = () => {
const location = useLocation();
type DynamicFormData = {
[key: string]: string;
};
if (location.state === null || location.state.formData === undefined) {
return (
<div className="container">
<p>Are you in the right place? It doesn't look like you completed a form or an email verification!</p>
</div>
)
}
const formData: DynamicFormData = location.state.formData;
return (
<div className="container">
<Helmet>
<title>Thank you!</title>
</Helmet>
<header className="header">Thank you for completing the form and verifying your email!<br></br>This is the data you entered:</header>
<div className="fields">
<table>
<tbody>
{Object.entries(formData).map(([key, value]) => (
<tr className="form-data-row" id={key}>
<td className="form-data-key">{key}</td>
<td className="form-data-value">{value}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
export default ThankYou;

View File

@ -1,97 +0,0 @@
import {FC, useState} from "react";
import {useLocation, useNavigate} from "react-router-dom";
import './VerifyMail.css';
import {Helmet} from "react-helmet";
interface VerificationData {
eMail: string;
code: string;
}
const VerifyMail: FC = () => {
const navigate = useNavigate()
const location = useLocation();
const [code, setCode] = useState('000000');
if (location.state === null || location.state.email === undefined) {
return (
<div className="container">
<p>Are you in the right place? It doesn't look like you have an email to verify!</p>
</div>
)
}
const email = location.state.email;
function setFormData(data: DynamicFormData) {
console.log("Setting form data")
navigate('/thank-you', {
state: {
formData: data
}
});
}
type DynamicFormData = {
[key: string]: string;
};
const handleCodeSubmit = async () => {
const verificationData: VerificationData = {
code: code,
eMail: email
}
console.log(verificationData);
fetch('http://localhost:8002/api/verify_email/form', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(verificationData)
})
.then(response => {
if (!response.ok) {
//TODO fix error and make message here?
console.log(response)
throw new Error('Invalid code');
}
//TODO check if its json if not its just text we need to handle and put in a p tag
return response.json()
})
.then((data: DynamicFormData) => {
console.log(data);
setFormData(data);
})
.catch(error => {
console.error('Received an unexpected error: ' + error);
})
}
return (
<div className="container">
<Helmet>
<title>Email validation</title>
</Helmet>
<header>Email validation</header>
<div className="content">
<p>Hi, you just completed a form and need to verify your email ({email}).</p>
<p>Please check your email for a verification code and enter it below:</p>
<input
type="text"
value={code}
onChange={(e) => {
if (/^\d{0,6}$/.test(e.target.value)) {
setCode(e.target.value);
}
}}
pattern="\d{6}"
maxLength={6}
className="verification-code-input"
/>
<button onClick={handleCodeSubmit} className="submit-button">Submit Code</button>
</div>
</div>
)
}
export default VerifyMail

1
src/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB