Compare commits

..

1 Commits

Author SHA1 Message Date
Teriuihi 8fda5d9fb9 Initial appeal commit 2024-10-06 00:27:29 +02:00
9 changed files with 169 additions and 52 deletions

View File

@ -9,6 +9,7 @@ import DEBUG from "./components/DEBUG/DEBUG";
import {getFormProperties} from "./components/form/formData";
import {FormProperties} from "./components/form/formInterfaces";
import FormActiveRedirect from "./components/form/formActiveRedirect";
import Appeal from "./components/appeal";
function App() {
return (
@ -26,6 +27,7 @@ function App() {
))}
<Route path="/verify-email" element={<VerifyMail/>}/>
<Route path="/thank-you" element={<ThankYou/>}/>
<Route path="/appeal" element={<Appeal/>}/>
{process.env.NODE_ENV === 'development' && <Route path="/debug" element={<DEBUG/>}/>}
</Routes>
</BrowserRouter>

21
src/components/appeal.tsx Normal file
View File

@ -0,0 +1,21 @@
// you might need to adjust imports according to your project structure
import React, { FunctionComponent } from 'react';
import { Helmet } from 'react-helmet';
const Appeal: FunctionComponent = () => {
return (
<div>
<Helmet>
<title>Appeals Selection</title>
<meta name="Appeals selection page" content="Choose the type of appeal"/>
</Helmet>
<header className="App-header">
<h1>Welcome to the Appeals page</h1>
<h2><a href="/appeal/minecraft">Appeal a Minecraft punishment.</a></h2>
<h2><a href="/appeal/discord">Appeal a Discord punishment.</a></h2>
</header>
</div>
);
}
export default Appeal;

View File

@ -0,0 +1,104 @@
import {FormData} from "../formInterfaces";
import * as Yup from "yup";
type PunishmentsForUser = {
punishments: string[]
};
type UserForPunishments = {
username: string
};
export const minecraft_appeal: FormData = {
steps: [
{
label: "What is your Minecraft username?",
additional_info: "Use the username you had when you last tried to join the server.",
name: "username",
type: "text",
min_length: 3,
max_length: 16,
required: true,
},
{
label: "What is your email?",
additional_info: "It does not have to be your minecraft email.",
name: "email",
type: "email",
min_length: 3,
max_length: 254,
required: true,
},
{
label: "What punishment would you like to appeal?",
additional_info: "Please select it below.",
name: "punishment",
type: "dropdown",
min_length: 10,
max_length: 2000,
required: true,
drop_down: [],
processInput: (input: string): Promise<string[]> => {
return new Promise((resolve, reject) => {
const userForPunishments: UserForPunishments = {
username: input,
}
fetch(`${process.env.REACT_APP_BACKEND_BASE_URL}/api/appeal/retrieve-minecraft-punishments`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userForPunishments)
})
.then(response => {
if (!response.ok) {
console.log(response)
reject(new Error('Invalid username'));
}
return response.json()
})
.then((punishmentsForUser: PunishmentsForUser) => {
resolve(punishmentsForUser.punishments);
})
.catch(error => {
console.error('Received an unexpected error: ' + error);
reject(error);
})
});
}
},
{
label: "Why should your punishment be reduced or removed?",
additional_info: "Please take your time writing this, we're more likely to accept an appeal if effort was put into it.",
name: "appeal",
type: "textarea",
min_length: 2,
max_length: 2000,
required: true,
},
],
//TODO warning if no punishments
backend: `${process.env.REACT_APP_BACKEND_BASE_URL}/api/appeal/minecraft`,
userInput: {username: '', email: '', punishment: '', appeal: ''},
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('Email is required'),
punishment: Yup.string()
.required('You are required to select a punishment'),
appeal: Yup.string()
.min(3, 'Your appeal needs to be at least 100 characters')
.max(2000, 'Your appeal can not be longer than 2000 characters')
.required()
}),
title: "Minecraft Appeal",
backendFormName: "MinecraftAppeal",
};

View File

@ -60,32 +60,6 @@ export const event_apply: FormData = {
max_length: 2000,
required: true,
},
{
label: "What things are you good at building?",
additional_info: "You can include links to pictures of your builds",
name: "good_builds",
type: "textarea",
min_length: 2,
max_length: 2000,
required: true,
},
{
label: "What things do you struggle to build?",
name: "bad_builds",
type: "textarea",
min_length: 2,
max_length: 2000,
required: true,
},
{
label: "Do you have experience with the technical aspects of Minecraft? If so please list what you can do",
additional_info: "With technical aspects we mean things like plugins, mods, adventure maps, etc.",
name: "technical_aspects",
type: "textarea",
min_length: 2,
max_length: 2000,
required: true,
},
{
label: "Are you able to use voice chat on Discord",
name: "discord_vc",
@ -107,11 +81,11 @@ export const event_apply: FormData = {
required: false,
},
],
backend: `${process.env.REACT_APP_BACKEND_BASE_URL}/api/event-apply/eventApplication`,
backend: `${process.env.REACT_APP_BACKEND_BASE_URL}/api/apply/staffApplication`,
userInput: {
username: '', email: '', discord: ''
, age: '', pronoun: '', avg_time: '', event_experience: ''
, discord_vc: '', good_builds: '', bad_builds: '', technical_aspects: '', other: ''
, discord_vc: '', other: ''
},
spec: Yup.object().shape({
username: Yup.string()
@ -158,21 +132,6 @@ export const event_apply: FormData = {
.matches(/(yes|no)$/i, 'Yes or no')
.required('An answer is required'),
good_builds: Yup.string()
.min(2, 'Please provide examples of things you are good at building')
.max(2000, 'Please provide at most 2000 characters')
.required('Please enter your build experience or simply say you don\'t have any'),
bad_builds: Yup.string()
.min(2, 'Please provide examples of things you are bad at building')
.max(2000, 'Please provide at most 2000 characters')
.required('Please enter your build experience or simply say you don\'t have any'),
technical_aspects: Yup.string()
.min(2, 'Please provide your experience with the technical aspects of Minercraft, if you don\'t have any you can say that instead')
.max(2000, 'Please provide at most 2000 characters')
.required('Please enter your technical experience or simply say you don\'t have any'),
other: Yup.string()
.max(2000, 'Please provide at most 2000 characters')
}),

View File

@ -7,14 +7,16 @@ const FormActiveRedirect = (formData: FormData) => {
const [isFormActive, setFormActive] = useState(false);
const handleCheckForm = useCallback(async () => {
const result = await fetch(`${process.env.REACT_APP_BACKEND_BASE_URL}/api/checks/formActive`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ formName: formData.backendFormName })
});
const response = await result.json();
setFormActive(response.isActive);
setFormActive(true);
setLoading(false);
// const result = await fetch(`${process.env.REACT_APP_BACKEND_BASE_URL}/api/checks/formActive`, {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify({ formName: formData.backendFormName })
// });
// const response = await result.json();
// setFormActive(response.isActive);
// setLoading(false);
}, [formData.backendFormName]);
useEffect(() => {

View File

@ -2,6 +2,7 @@ import {FormProperties} from "./formInterfaces";
import {contact} from "./data/contact";
import {apply} from "./data/apply";
import {event_apply} from "./data/event_apply";
import {minecraft_appeal} from "./data/appeal";
const formProperties: FormProperties[] = [
{
@ -16,6 +17,10 @@ const formProperties: FormProperties[] = [
path: 'event-apply',
formData: event_apply
},
{
path: 'appeal/minecraft',
formData: minecraft_appeal
},
]
export function getFormProperties(): FormProperties[] {

View File

@ -1,4 +1,4 @@
import React, {useState} from "react";
import React, {useEffect, useState} from "react";
import './GenericForm.css';
import {Field} from "formik";
import {FormHandlerProps} from "./formInterfaces";
@ -22,6 +22,22 @@ const FormHTML: React.FC<FormHandlerProps> = ({
const [selectedOptions, setSelectedOptions] = useState(fieldValues);
const [options, setOptions] = useState<string[]>([]);
useEffect(() => {
const { processInput, drop_down = [] } = steps[currentStep];
if (processInput) {
processInput(values['username']).then(newOptions => {
setOptions(newOptions);
//TODO be an unsetter for some error field
});
} else {
setOptions(drop_down);
console.log("No data") //TODO be a setter for some error field
}
}, [currentStep, steps]);
if (currentField.type === 'select') {
return (
<ReactSelect
@ -65,6 +81,12 @@ const FormHTML: React.FC<FormHandlerProps> = ({
{option}
</option>
))}
{options.map((option, i) => (
<option key={i} value={option}>
{option}
</option>
))}
{options.length === 0 ? "<p>No data found, please check if your username is valid. Or if this was a discord punishment, please use the discord appeal form</p>":"<p></p>"}
</Field>
);
} else {

View File

@ -3,7 +3,7 @@ import React from "react";
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" | "event_experience" | "discord_vc" | "other" | "good_builds" | "bad_builds" | "technical_aspects";
"expectations_mod" | "event_experience" | "discord_vc" | "other" | "punishment" | "appeal";
export interface Step {
label: string;
@ -15,6 +15,7 @@ export interface Step {
additional_info?: string;
drop_down?: string[]
multiple?: boolean
processInput?: (input: string) => Promise<string[]>;
}
export interface UserInput {

View File

@ -13,6 +13,7 @@ const Home: FunctionComponent = () => {
<h2><a href="/contact">Contact us.</a></h2>
<h2><a href="/apply">Apply for staff.</a></h2>
<h2><a href="/event-apply">Apply for events.</a></h2>
<h2><a href="/appeal">Appeal a punishment.</a></h2>
</header>
</div>
);