Compare commits
2 Commits
2eef14ba2e
...
857d61e09d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
857d61e09d | ||
|
|
d2f15d2627 |
12
src/App.tsx
12
src/App.tsx
|
|
@ -1,4 +1,3 @@
|
|||
import ContactForm from "./components/contact_form/contact";
|
||||
import './App.css';
|
||||
import Home from "./components/home/home";
|
||||
import {BrowserRouter, Route, Routes} from 'react-router-dom';
|
||||
|
|
@ -7,6 +6,9 @@ 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";
|
||||
import GenericForm from "./components/form/genericForm";
|
||||
import {getFormProperties} from "./components/form/formData";
|
||||
import {FormProperties} from "./components/form/formInterfaces";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
|
|
@ -15,7 +17,13 @@ function App() {
|
|||
<BrowserRouter basename="/">
|
||||
<Routes>
|
||||
<Route path="/" element={<Home/>}/>
|
||||
<Route path="/contact" element={<ContactForm/>}/>
|
||||
{getFormProperties().map((property: FormProperties) => (
|
||||
<Route
|
||||
key={property.path}
|
||||
path={property.path}
|
||||
element={<GenericForm {...property.formData} />}
|
||||
/>
|
||||
))}
|
||||
<Route path="/verify-email" element={<VerifyMail/>}/>
|
||||
<Route path="/thank-you" element={<ThankYou/>}/>
|
||||
{process.env.NODE_ENV === 'development' && <Route path="/debug" element={<DEBUG/>}/>}
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
});
|
||||
206
src/components/form/data/apply.tsx
Normal file
206
src/components/form/data/apply.tsx
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
import {FormData} from "../formInterfaces";
|
||||
import * as Yup from "yup";
|
||||
|
||||
export const apply: FormData = {
|
||||
steps: [
|
||||
{
|
||||
label: "What is your username?",
|
||||
name: "username",
|
||||
type: "text",
|
||||
min_length: 3,
|
||||
max_length: 16,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "What is your email?",
|
||||
name: "email",
|
||||
type: "email",
|
||||
min_length: 3,
|
||||
max_length: 254,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "What is your discord?",
|
||||
name: "discord",
|
||||
type: "text",
|
||||
min_length: 2,
|
||||
max_length: 32,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "Do you meet the minimum pc requirements?",
|
||||
name: "pc_requirements",
|
||||
type: "text",
|
||||
min_length: 2,
|
||||
max_length: 3,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "What is your age?",
|
||||
name: "age",
|
||||
type: "text",
|
||||
min_length: 1,
|
||||
max_length: 3,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "What is your preferred pronoun?",
|
||||
name: "pronoun",
|
||||
type: "text",
|
||||
min_length: 0,
|
||||
max_length: 16,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
label: "When did you join Altitude",
|
||||
name: "join_date",
|
||||
type: "date",
|
||||
min_length: 4,
|
||||
max_length: 32,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "On average, how many hours per week are you available to moderate Altitude?",
|
||||
name: "avg_time",
|
||||
type: "text",
|
||||
min_length: 1,
|
||||
max_length: 2,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "Which days are you normally able to come on?",
|
||||
name: "available_days",
|
||||
type: "textarea",
|
||||
min_length: 6,
|
||||
max_length: 128,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "What time of the day are you normally online?",
|
||||
name: "available_time",
|
||||
type: "textarea",
|
||||
min_length: 3,
|
||||
max_length: 256,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "Do you have any previous experience being staff?",
|
||||
name: "staff_experience",
|
||||
type: "textarea",
|
||||
min_length: 2,
|
||||
max_length: 2000,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "Which server plugins do you have experience with?",
|
||||
name: "plugin_experience",
|
||||
type: "textarea",
|
||||
min_length: 2,
|
||||
max_length: 2000,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "Why are you interested in being a moderator on Altitude?",
|
||||
name: "why_staff",
|
||||
type: "textarea",
|
||||
min_length: 2,
|
||||
max_length: 2000,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "What do you believe are the expectations of a moderator?",
|
||||
name: "expectations_mod",
|
||||
type: "textarea",
|
||||
min_length: 2,
|
||||
max_length: 2000,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "You may share anything else you wish here.",
|
||||
name: "other",
|
||||
type: "textarea",
|
||||
min_length: 2,
|
||||
max_length: 2000,
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
backend: 'https://forms-backend.alttd.com/api/apply/staffApplication',
|
||||
userInput: {username: '', email: '', discord: '', pc_requirements: ''
|
||||
, age: '', pronoun: '', join_date: '', avg_time: '', available_days: '', available_time: '', staff_experience: ''
|
||||
, plugin_experience: '', why_staff: '', expectations_mod: '', other: ''
|
||||
},
|
||||
spec: 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(),
|
||||
|
||||
discord: Yup.string()
|
||||
.min(2, 'Discord name should be at least 2 characters')
|
||||
.max(32, 'Discord name should be at most 32 characters')
|
||||
.matches(/^(?!.*\.\.)([a-z0-9._]{2,32})$/, 'Please enter a valid Discord name')
|
||||
.required('Discord name is required'),
|
||||
|
||||
pc_requirements: Yup.string()
|
||||
.min(2, "Please answer yes or no")
|
||||
.max(3, "Please answer yes or no")
|
||||
.matches(/(yes|no)$/i, 'Yes or no')
|
||||
.required('An answer is required'),
|
||||
|
||||
age: Yup.number()
|
||||
.min(0, 'Please enter a valid age')
|
||||
.max(999, 'We do not accept players older than 999 years old sorry!')
|
||||
.required('You are required to fill out your age'),
|
||||
|
||||
pronoun: Yup.string()
|
||||
.max(16, 'Pronouns can\'t be longer than 16 characters'),
|
||||
|
||||
join_date: Yup.date()
|
||||
.required('Your join date is required, if you\'re not sure enter an estimated date'),
|
||||
|
||||
avg_time: Yup.number()
|
||||
.min(0, 'Please enter a positive number')
|
||||
.max(168, 'There are only 168 hours in a week')
|
||||
.required(),
|
||||
|
||||
available_days: Yup.string()
|
||||
.min(6, 'Please provide at least 6 characters')
|
||||
.max(128, 'Please provide at most 128 characters')
|
||||
.required('Available days are required'),
|
||||
|
||||
available_time: Yup.string()
|
||||
.min(3, 'Please provide at least 3 characters')
|
||||
.max(256, 'Please provide at most 256 characters')
|
||||
.required('Available time is required'),
|
||||
|
||||
staff_experience: Yup.string()
|
||||
.min(2, 'Please provide your experience as staff, if you don\'t have any you can say that instead')
|
||||
.max(2000, 'Please provide at most 2000 characters')
|
||||
.required('Staff experience is required'),
|
||||
|
||||
plugin_experience: Yup.string()
|
||||
.min(2, 'Please provide your experience with moderation plugins, if you don\'t have any you can say that instead')
|
||||
.max(2000, 'Please provide at most 2000 characters')
|
||||
.required('Plugin experience is required'),
|
||||
|
||||
why_staff: Yup.string()
|
||||
.min(2, 'Please provide your reason for wanting to be a moderator')
|
||||
.max(2000, 'Please provide at most 2000 characters')
|
||||
.required('Reason for wanting to be a moderator is required'),
|
||||
|
||||
expectations_mod: Yup.string()
|
||||
.min(2, 'Please provide your expectations of a moderator')
|
||||
.max(2000, 'Please provide at most 2000 characters')
|
||||
.required('Expectations of a moderator is required'),
|
||||
|
||||
other: Yup.string()
|
||||
.max(2000, 'Please provide at most 2000 characters')
|
||||
})
|
||||
};
|
||||
51
src/components/form/data/contact.tsx
Normal file
51
src/components/form/data/contact.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import {FormData} from "../formInterfaces";
|
||||
import * as Yup from "yup";
|
||||
|
||||
export const contact: FormData = {
|
||||
steps: [
|
||||
{
|
||||
label: "Username",
|
||||
name: "username",
|
||||
type: "text",
|
||||
min_length: 3,
|
||||
max_length: 16,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "Email",
|
||||
name: "email",
|
||||
type: "email",
|
||||
min_length: 3,
|
||||
max_length: 254,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
label: "Question",
|
||||
name: "question",
|
||||
type: "textarea",
|
||||
min_length: 10,
|
||||
max_length: 2000,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
backend: 'https://forms-backend.alttd.com/api/contact/submitContactForm',
|
||||
userInput: {username: '', email: '', question: ''},
|
||||
spec: 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')
|
||||
})
|
||||
};
|
||||
18
src/components/form/formData.tsx
Normal file
18
src/components/form/formData.tsx
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import {FormProperties} from "./formInterfaces";
|
||||
import {contact} from "./data/contact";
|
||||
import {apply} from "./data/apply";
|
||||
|
||||
const formProperties: FormProperties[] = [
|
||||
{
|
||||
path: 'contact',
|
||||
formData: contact
|
||||
},
|
||||
{
|
||||
path: 'apply',
|
||||
formData: apply
|
||||
},
|
||||
]
|
||||
|
||||
export function getFormProperties(): FormProperties[] {
|
||||
return formProperties
|
||||
}
|
||||
30
src/components/form/formInterfaces.tsx
Normal file
30
src/components/form/formInterfaces.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import * as Yup from "yup";
|
||||
|
||||
type InputNames = "username" | "email" | "question" | "discord" | "pc_requirements" | "age" | "pronoun" | "join_date" |
|
||||
"avg_time" | "available_days" | "available_time" | "staff_experience" | "plugin_experience" | "why_staff" |
|
||||
"expectations_mod" | "other";
|
||||
|
||||
export interface Step {
|
||||
label: string;
|
||||
name: InputNames;
|
||||
type: "text" | "email" | "textarea" | "date";
|
||||
min_length: number;
|
||||
max_length: number;
|
||||
required: boolean;
|
||||
}
|
||||
|
||||
export interface UserInput {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export type FormData = {
|
||||
steps: Step[];
|
||||
backend: string;
|
||||
userInput: UserInput;
|
||||
spec: Yup.Schema<any>
|
||||
}
|
||||
|
||||
export interface FormProperties {
|
||||
path: string
|
||||
formData: FormData;
|
||||
}
|
||||
|
|
@ -1,61 +1,23 @@
|
|||
import React, {useState} from "react";
|
||||
import './Contact.css';
|
||||
import './GenericForm.css';
|
||||
import {useNavigate} from 'react-router-dom'
|
||||
import {validationSchema} from "./validationSchema";
|
||||
import {ErrorMessage, Field, Form, Formik, FormikValues} from "formik";
|
||||
import {Step, UserInput, FormData} from './formInterfaces';
|
||||
import * as Yup from "yup";
|
||||
|
||||
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 GenericForm = (formData: FormData) => {
|
||||
const steps: Step[] = formData.steps;
|
||||
const backend: string = formData.backend;
|
||||
const userInput: UserInput = formData.userInput;
|
||||
const spec: Yup.Schema<any> = formData.spec;
|
||||
|
||||
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('https://forms-backend.alttd.com/api/contact/submitContactForm', {
|
||||
const response = await fetch(backend, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
@ -63,6 +25,16 @@ const ContactForm = () => {
|
|||
body: JSON.stringify(e)
|
||||
})
|
||||
if (!response.ok) {
|
||||
let json: string = JSON.stringify(steps);
|
||||
const blob = new Blob([json], {type: "application/json"});
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'form_data.json';
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
//TODO clean up
|
||||
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', {
|
||||
|
|
@ -92,8 +64,8 @@ const ContactForm = () => {
|
|||
</div>
|
||||
<div>
|
||||
<Formik
|
||||
initialValues={{username: '', email: '', question: ''}}
|
||||
validationSchema={validationSchema}
|
||||
initialValues={userInput}
|
||||
validationSchema={spec}
|
||||
onSubmit={(values: FormikValues) => {
|
||||
handleSubmit(values);
|
||||
}}
|
||||
|
|
@ -149,4 +121,4 @@ const ContactForm = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export default ContactForm;
|
||||
export default GenericForm;
|
||||
Loading…
Reference in New Issue
Block a user