Compare commits

...

10 Commits

Author SHA1 Message Date
Teriuihi 5301940a2f Update fetch URLs and set app base URL
The localhost:8080 server references in fetch calls within verify_mail.tsx and contact.tsx files have been updated to localhost:8002. Furthermore, the application base URL has been set to '/forms' in package.json, and in the BrowserRouter element within App.tsx, to ensure proper routing on the production server.
2024-04-28 21:41:29 +02:00
Teriuihi 011fb9a282 Refactor CSS and modify data display layout
The commit includes modification to the CSS of the verify email component and changes how form data is displayed. The form data that was previously shown in a standard div is now presented in a table format for better readability. Also, it contains minor debug changes in verify_mail.tsx to handle invalid responses.
2024-04-28 17:59:17 +02:00
Teriuihi 394fd6069e Add Formik and Yup to handle forms
Formik and Yup libraries have been introduced to handle forms and their validation, respectively. This improves the clarity and robustness of form validation. Handled logic includes character restrictions, requirement conditions, and minimum and maximum length constraints for the 'username', 'email', and 'question' fields of the contact form.
2024-03-01 20:42:54 +01:00
Peter f210c292f1 missing files for header and fonts 2024-02-10 19:53:56 +01:00
Peter 3baee7f8e5 Fixed footer, messed with some colors. Wasted time on a failed attempted at a darkmode switch 2024-01-14 21:05:13 +01:00
Teriuihi e967a42423 Update styling and header of 'Thank you' page
Refactored the 'Thank you' page for email verification feature by enhancing the display layout and typography. This includes the introduction of a header with dynamic page title provided by 'Helmet', the adoption of a flexbox-based layout for the display of form data, as well as the adjustment of font sizes and margins across elements for improved readability.
2024-01-14 10:18:09 +01:00
Teriuihi 737460a2c2 Add DEBUG component for development environment
Introduced a new DEBUG component exclusively for the development environment. This component contains navigation options for email verification and the 'thank you' page. Updated the .gitignore file to no longer exclude DEBUG files, and included a basic layout and styling for the component.
2024-01-14 10:17:50 +01:00
Teriuihi f2d932bcd7 Add Helmet to manage email verification page title
Integrated the Helmet library in the 'verify_mail.tsx' component to handle setting the page title dynamically to "Email validation". This helps in improving page context for users and SEO. As part of the update, the page title has been set within the newly added Helmet wrapper.
2024-01-14 09:59:26 +01:00
Teriuihi d02df0e418 Update email verification UI and functionality
Updated the user interface for the email verification component, refining the visual organization and style through updates to `verify_mail.tsx` and `VerifyMail.css`. Also, improved the functionality by enforcing a condition that validation code input must be numeric and not more than six characters long. Additionally, added more files to .gitignore to ignore debug files.
2024-01-14 09:52:21 +01:00
Teriuihi 49a71097bc Create site with contact form and email verification
This commit removes the redundant logo.svg file. It then adds several new components including 'footer', 'contact_form', 'home', 'verify_email' in forms/src/components directory. It also includes associated CSS files for styling these components. Updates have also been made in the index.html file for SEO metadata. Changes made aim to enhance functionality and improve user interface.
2024-01-13 15:53:47 +01:00
38 changed files with 73443 additions and 4306 deletions

13455
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,24 +1,28 @@
{
"homepage": "https://alttd.com/forms",
"name": "forms",
"version": "0.1.0",
"private": true,
"dependencies": {
"@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",
"@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",
"@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-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
"react-helmet": "^6.1.0",
"react-scripts": "^5.0.1",
"typescript": "^5.3.3",
"web-vitals": "^3.5.1",
"yup": "^1.3.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"build": "set \"PUBLIC_URL=/forms\" && react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
@ -39,5 +43,9 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/react-helmet": "^6.1.11",
"react-router-dom": "^6.21.1"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

BIN
public/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

21060
public/fonts/opensans-bold.svg Normal file

File diff suppressed because it is too large Load Diff

After

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

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Binary file not shown.

BIN
public/fonts/opensans.eot Normal file

Binary file not shown.

21062
public/fonts/opensans.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
public/fonts/opensans.ttf Normal file

Binary file not shown.

BIN
public/fonts/opensans.woff Normal file

Binary file not shown.

View File

@ -1,43 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<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.
<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"/>
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>
<link rel="icon" type="image/png" href="%PUBLIC_URL%/favicon.png"/>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
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>
</body>
</html>

BIN
public/log.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

BIN
public/logosmall.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -1,3 +1,141 @@
: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;
}
@ -14,25 +152,29 @@
}
.App-header {
background-color: #282c34;
background-color: var(--color-primary);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
color: var(--font-color);
}
.App-link {
color: #61dafb;
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
.app-container {
display: flex;
flex-direction: column;
flex: 1 0 auto; /* This will make sure this container takes all available vertical space */
}
#root {
display: flex;
flex-grow: 1;
}

View File

@ -1,26 +1,29 @@
import React from 'react';
import logo from './logo.svg';
import ContactForm from "./components/contact_form/contact";
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">
<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>
);
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>
);
}
export default App;
export default App;

View File

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

View File

@ -0,0 +1,33 @@
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

@ -0,0 +1,49 @@
.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

@ -0,0 +1,152 @@
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

@ -0,0 +1,20 @@
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

@ -0,0 +1,134 @@
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

@ -0,0 +1,57 @@
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

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

View File

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

View File

@ -0,0 +1,19 @@
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

@ -0,0 +1,40 @@
/*.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

@ -0,0 +1,30 @@
.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

@ -0,0 +1,45 @@
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

@ -0,0 +1,97 @@
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

View File

@ -1 +0,0 @@
<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>

Before

Width:  |  Height:  |  Size: 2.6 KiB