Compare commits

...

2 Commits

Author SHA1 Message Date
Teriuihi 857d61e09d Add staff application form
Introduced a new form for staff applications based on the old staff application form.
2024-08-06 22:23:35 +02:00
Teriuihi d2f15d2627 Refactor forms to use a generic form component
Replaced the contact form with a generic form component to support multiple form configurations. Moved validation schema and form data to a new structure, allowing dynamic form rendering. Updated the App component to utilize this generic approach for handling different forms.
2024-08-06 21:07:20 +02:00
8 changed files with 337 additions and 72 deletions

View File

@ -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/>}/>}

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

@ -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')
})
};

View 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')
})
};

View 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
}

View 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;
}

View File

@ -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;