Skip to content

Commit

Permalink
Merge pull request #32 from osakunta/next-site
Browse files Browse the repository at this point in the history
Refactor cards, styling changes, and page blocking
  • Loading branch information
YB-BigSwan authored Sep 3, 2024
2 parents 29e83df + 26f2923 commit 0c1e799
Show file tree
Hide file tree
Showing 34 changed files with 1,487 additions and 849 deletions.
144 changes: 144 additions & 0 deletions components/ContactTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { Contact } from "@/lib/cmsClient";
import useTranslate from "@/hooks/useTranslate";
import {
TableContainer,
Table,
TableHead,
TableRow,
TableCell,
TableBody,
TextField,
TableSortLabel,
} from "@mui/material";
import styles from "@/styles/contactTable.module.css";
import { useMemo, useState } from "react";

export type ContactTableProps = {
contactData: Contact[];
};

type Order = "asc" | "desc";

const ContactTable = ({ contactData }: ContactTableProps) => {
const t = useTranslate();
const [searchQuery, setSearchQuery] = useState("");
const [order, setOrder] = useState<Order>("asc");
const [orderBy, setOrderBy] = useState<keyof Contact>("first_name");

const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchQuery(event.target.value);
};

const handleSort = (property: keyof Contact) => {
const isAsc = orderBy === property && order === "asc";
setOrder(isAsc ? "desc" : "asc");
setOrderBy(property);
};

const filteredData = useMemo(
() =>
contactData
.filter((contact) =>
["first_name", "last_name", "contact", "label_key"].some((key) =>
contact[key as keyof Contact]
?.toString()
.toLowerCase()
.includes(searchQuery.toLowerCase()),
),
)
.sort((a, b) => {
if (a[orderBy] < b[orderBy]) return order === "asc" ? -1 : 1;
if (a[orderBy] > b[orderBy]) return order === "asc" ? 1 : -1;
return 0;
}),
[searchQuery, order, orderBy, contactData],
);

return (
<div className={styles.tableContainer}>
<TableContainer className={styles.table}>
<div className={styles.tableLabel}>
<h2>{t("contact:tableLabel")}</h2>
<TextField
label={t("contact:searchLabel")}
fullWidth
value={searchQuery}
onChange={handleSearchChange}
className={styles.searchBar}
sx={{
"& .MuiOutlinedInput-root": {
height: "2.5rem",
"& fieldset": {
borderRadius: "5rem",
},
"&.Mui-focused fieldset": {
borderColor: "#132c43", // Border color on focus
},
},
"& .MuiInputLabel-root": {
lineHeight: "1rem", // Align the label vertically to the center
},
"& .MuiInputLabel-root.Mui-focused": {
color: "#132c43", // Label color on focus
},
}}
/>
</div>
<Table>
<TableHead className={styles.tableHead}>
<TableRow>
<TableCell>
<TableSortLabel
active={orderBy === "label_key"}
direction={order}
onClick={() => handleSort("label_key")}
>
{t("contact:title")}
</TableSortLabel>
</TableCell>
<TableCell>
<TableSortLabel
active={orderBy === "first_name"}
direction={order}
onClick={() => handleSort("first_name")}
>
{t("contact:firstName")}
</TableSortLabel>
</TableCell>
<TableCell>
<TableSortLabel
active={orderBy === "last_name"}
direction={order}
onClick={() => handleSort("last_name")}
>
{t("contact:lastName")}
</TableSortLabel>
</TableCell>
<TableCell>
<TableSortLabel
active={orderBy === "contact"}
direction={order}
onClick={() => handleSort("contact")}
>
{t("contact:contactLabel")}
</TableSortLabel>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{filteredData.map((contact) => (
<TableRow key={contact.id}>
<TableCell>{t(contact.label_key)}</TableCell>
<TableCell>{contact.first_name}</TableCell>
<TableCell>{contact.last_name}</TableCell>
<TableCell>{contact.contact}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</div>
);
};

export default ContactTable;
243 changes: 243 additions & 0 deletions components/HarassmentForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import { useState } from "react";
import { Form, Formik, FormikHelpers } from "formik";
import {
TextField,
Button,
Switch,
FormControlLabel,
Alert,
Snackbar,
} from "@mui/material";
import * as Yup from "yup";
import styles from "@/styles/harassmentForm.module.css";

interface Values {
incident: string;
partiesInvolved: string;
wantsAnswer: boolean;
name: string;
email: string;
honeypot1: string;
honeypot2: string;
}

const validationSchema = Yup.object({
email: Yup.string()
.email("Invalid email address")
.when("wantsAnswer", (wantsAnswer, schema) =>
wantsAnswer ? schema.required("Email is required") : schema,
),
name: Yup.string().when("wantsAnswer", (wantsAnswer, schema) =>
wantsAnswer ? schema.required("Name is required") : schema,
),
});

const noValidationSchema = Yup.object({
email: Yup.string(),
name: Yup.string(),
});

const HarassmentForm = () => {
const [wantsAnswer, setWantsAnswer] = useState(false);
const [snackbarOpen, setSnackbarOpen] = useState(false);
const [snackbarMessage, setSnackbarMessage] = useState("");
const [snackbarSeverity, setSnackbarSeverity] = useState<"success" | "error">(
"success",
);
const [buttonText, setButtonText] = useState("Submit");
const [buttonDisabled, setButtonDisabled] = useState(false);
const corsProxy: string = "https://cors-anywhere.herokuapp.com/";

const handleSnackbarClose = () => {
setSnackbarOpen(false);
};

return (
<>
<Formik
initialValues={{
incident: "",
partiesInvolved: "",
wantsAnswer: false,
name: "",
email: "",
honeypot1: "",
honeypot2: "",
}}
validationSchema={wantsAnswer ? validationSchema : noValidationSchema}
onSubmit={(
values: Values,
{ setSubmitting, resetForm }: FormikHelpers<Values>,
) => {
if (values.honeypot1 || values.honeypot2) {
setSnackbarMessage("Service denied");
setSnackbarSeverity("error");
setSnackbarOpen(true);
setSubmitting(false);
setButtonText("Submit");
setButtonDisabled(false);
return;
}

setButtonText("Sending");
setButtonDisabled(true);

const formData = new FormData();
formData.append("entry.898010333", values.incident); // Example ID for 'incident'
formData.append("entry.1968992256", values.partiesInvolved); // Example ID for 'partiesInvolved'
formData.append("entry.200517309", values.name); // Example ID for 'name'
formData.append("entry.1828602041", values.email); // Example ID for 'email'

fetch(`${corsProxy + process.env.NEXT_PUBLIC_HARASSMENT_FORM_URL}`, {
method: "POST",
body: formData,
})
.then((response) => {
if (response.ok) {
setSnackbarMessage(
"Your report has been submitted successfully.",
);
setSnackbarSeverity("success");
resetForm(); // Clear the form on successful submission
} else {
setSnackbarMessage(
"There was a problem submitting your report.",
);
setSnackbarSeverity("error");
}
setSnackbarOpen(true);
})
.catch((error) => {
console.error("An error occurred:", error);
setSnackbarMessage("An error occurred. Please try again later.");
setSnackbarSeverity("error");
setSnackbarOpen(true);
})
.finally(() => {
setSubmitting(false);
setButtonText("Submit");
setButtonDisabled(false);
});
}}
>
{({ values, handleChange, handleBlur, touched, errors }) => (
<Form className={styles.form}>
{/* Honeypot Fields */}
<div style={{ display: "none" }}>
<TextField
id="honeypot1"
name="honeypot1"
value={values.honeypot1}
onChange={handleChange}
onBlur={handleBlur}
/>
<TextField
id="honeypot2"
name="honeypot2"
value={values.honeypot2}
onChange={handleChange}
onBlur={handleBlur}
/>
</div>

<TextField
fullWidth
id="incident"
name="incident"
label="Tell about what happened in your own words"
multiline
rows={4}
variant="outlined"
value={values.incident}
onChange={handleChange}
onBlur={handleBlur}
error={touched.incident && Boolean(errors.incident)}
helperText={touched.incident && errors.incident}
className={styles.inputField}
/>

<TextField
fullWidth
id="partiesInvolved"
name="partiesInvolved"
label="Were there other people there?"
multiline
rows={4}
variant="outlined"
value={values.partiesInvolved}
onChange={handleChange}
onBlur={handleBlur}
error={touched.partiesInvolved && Boolean(errors.partiesInvolved)}
helperText={touched.partiesInvolved && errors.partiesInvolved}
className={styles.inputField}
/>

<FormControlLabel
control={
<Switch
checked={wantsAnswer}
onChange={() => setWantsAnswer(!wantsAnswer)}
name="wantsAnswer"
color="success"
/>
}
label="I want to be answered"
className={styles.switchLabel}
/>

{wantsAnswer && (
<>
<TextField
fullWidth
id="name"
name="name"
label="Your Name"
variant="outlined"
value={values.name}
onChange={handleChange}
onBlur={handleBlur}
error={touched.name && Boolean(errors.name)}
helperText={touched.name && errors.name}
className={styles.inputField}
/>

<TextField
fullWidth
id="email"
name="email"
label="Email"
variant="outlined"
value={values.email}
onChange={handleChange}
onBlur={handleBlur}
error={touched.email && Boolean(errors.email)}
helperText={touched.email && errors.email}
className={styles.inputField}
/>
</>
)}

<Button
type="submit"
className="button darkBlue"
disabled={buttonDisabled}
>
{buttonText}
</Button>
</Form>
)}
</Formik>
<Snackbar
open={snackbarOpen}
autoHideDuration={6000}
onClose={handleSnackbarClose}
>
<Alert onClose={handleSnackbarClose} severity={snackbarSeverity}>
{snackbarMessage}
</Alert>
</Snackbar>
</>
);
};

export default HarassmentForm;
Loading

0 comments on commit 0c1e799

Please sign in to comment.