Initial appeal commit

This commit is contained in:
Teriuihi 2024-10-06 00:27:29 +02:00
parent ee54e91051
commit 8fda5d9fb9
8 changed files with 167 additions and 9 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

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