Skip to content

Commit

Permalink
fix: order of field references (#95)
Browse files Browse the repository at this point in the history
* fix: order of field references

* add missing forms

* new cfl package

* fix formatting

* fix: install new cfl package
  • Loading branch information
SKairinos authored Feb 25, 2025
1 parent b2b57c9 commit 3520092
Show file tree
Hide file tree
Showing 29 changed files with 1,107 additions and 807 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
],
"dependencies": {
"@react-pdf/renderer": "^4.0.0",
"codeforlife": "2.6.6",
"codeforlife": "2.6.10",
"crypto-js": "^4.2.0",
"qrcode": "^1.5.4"
},
Expand Down
6 changes: 4 additions & 2 deletions src/components/form/ClassAutocompleteField.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { type FC, type RefObject } from "react"
import { ApiAutocompleteField } from "codeforlife/components/form"
import { type FC } from "react"

import { type ListClassesArg, useLazyListClassesQuery } from "../../api/klass"

export interface ClassAutocompleteFieldProps {
required?: boolean
name?: string
_id?: ListClassesArg["_id"]
inputRef?: RefObject<HTMLInputElement>
}

const ClassAutocompleteField: FC<ClassAutocompleteFieldProps> = ({
required = false,
name = "klass",
_id,
inputRef,
}) => (
<ApiAutocompleteField
useLazyListQuery={useLazyListClassesQuery}
Expand All @@ -21,7 +23,7 @@ const ClassAutocompleteField: FC<ClassAutocompleteFieldProps> = ({
getOptionLabel={({ name, id, teacher }) =>
`${name} (${id}), ${teacher.user.first_name} ${teacher.user.last_name}`
}
textFieldProps={{ required, name }}
textFieldProps={{ required, name, inputRef }}
/>
)

Expand Down
69 changes: 43 additions & 26 deletions src/components/form/CreateClassForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Stack, Typography } from "@mui/material"
import { type FC } from "react"
import { type SchoolTeacherUser } from "codeforlife/api"
import { type SubmitFormOptions } from "codeforlife/utils/form"
import { useInputRef } from "codeforlife/hooks"

import {
ClassNameField,
Expand All @@ -28,32 +29,48 @@ export interface CreateClassFormProps {
const CreateClassForm: FC<CreateClassFormProps> = ({
authUser,
submitOptions,
}) => (
<>
<Typography>
{authUser.teacher.is_admin
? "When you set up a new class, a unique class access code will automatically be generated for the teacher assigned to the class."
: "When you set up a new class, a unique class access code will automatically be generated, with you being identified as the teacher for that class."}
</Typography>
<forms.Form
initialValues={{
name: "",
teacher: authUser.teacher.id,
read_classmates_data: false,
}}
useMutation={useCreateClassMutation}
submitOptions={submitOptions}
>
<Stack gap={2}>
<Stack direction={{ sm: "row" }} gap={2}>
<ClassNameField required />
{authUser.teacher.is_admin && <TeacherAutocompleteField required />}
}) => {
const nameFieldRef = useInputRef()
const teacherFieldRef = useInputRef()
const readClassmatesDataFieldRef = useInputRef()

return (
<>
<Typography>
{authUser.teacher.is_admin
? "When you set up a new class, a unique class access code will automatically be generated for the teacher assigned to the class."
: "When you set up a new class, a unique class access code will automatically be generated, with you being identified as the teacher for that class."}
</Typography>
<forms.Form
initialValues={{
name: "",
teacher: authUser.teacher.id,
read_classmates_data: false,
}}
fieldRefs={[
{ name: "name", inputRef: nameFieldRef },
{ name: "teacher", inputRef: teacherFieldRef },
{
name: "read_classmates_data",
inputRef: readClassmatesDataFieldRef,
},
]}
useMutation={useCreateClassMutation}
submitOptions={submitOptions}
>
<Stack gap={2}>
<Stack direction={{ sm: "row" }} gap={2}>
<ClassNameField inputRef={nameFieldRef} required />
{authUser.teacher.is_admin && (
<TeacherAutocompleteField required inputRef={teacherFieldRef} />
)}
</Stack>
<ReadClassmatesDataField inputRef={readClassmatesDataFieldRef} />
<forms.SubmitButton>Create class</forms.SubmitButton>
</Stack>
<ReadClassmatesDataField />
<forms.SubmitButton>Create class</forms.SubmitButton>
</Stack>
</forms.Form>
</>
)
</forms.Form>
</>
)
}

export default CreateClassForm
5 changes: 4 additions & 1 deletion src/components/form/CreateStudentsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import * as forms from "codeforlife/components/form"
import { Add as AddIcon, Upload as UploadIcon } from "@mui/icons-material"
import { type FC, type MutableRefObject, useEffect, useRef } from "react"
import { Stack, Typography } from "@mui/material"
import { useInputRef, useNavigate } from "codeforlife/hooks"
import { type Class } from "codeforlife/api"
import { InputFileButton } from "codeforlife/components"
import { type SubmitFormOptions } from "codeforlife/utils/form"
import { firstNameSchema } from "codeforlife/schemas/user"
import { useNavigate } from "codeforlife/hooks"

import {
type CreateStudentsArg,
Expand All @@ -31,6 +31,7 @@ const CreateStudentsForm: FC<CreateStudentsFormProps> = ({
submitOptions,
}) => {
const fileInput = useRef<HTMLInputElement>()
const firstNamesFieldRef = useInputRef()
const navigate = useNavigate()

const reader = new FileReader()
Expand Down Expand Up @@ -64,6 +65,7 @@ const CreateStudentsForm: FC<CreateStudentsFormProps> = ({
</InputFileButton>
<forms.Form
initialValues={{ first_names: [] as string[] }}
fieldRefs={[{ name: "first_names", inputRef: firstNamesFieldRef }]}
useMutation={useCreateStudentsMutation}
submitOptions={{
...submitOptions,
Expand Down Expand Up @@ -105,6 +107,7 @@ const CreateStudentsForm: FC<CreateStudentsFormProps> = ({
{/* TODO: show errors from backend */}
<forms.TextField
name="first_names"
inputRef={firstNamesFieldRef}
required
split={split}
uniqueCaseInsensitive
Expand Down
13 changes: 12 additions & 1 deletion src/components/form/DeleteAccountForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import {
Typography,
} from "@mui/material"
import { type FC, useState } from "react"
import { useInputRef, useNavigate } from "codeforlife/hooks"
import { DeleteOutline as DeleteOutlineIcon } from "@mui/icons-material"
import { logout } from "codeforlife/utils/auth"
import { useNavigate } from "codeforlife/hooks"

import {
type DestroyIndependentUserArg,
Expand Down Expand Up @@ -79,6 +79,8 @@ const DeleteAccountForm: FC<DeleteAccountFormProps> = ({ authUser }) => {
open: boolean
destroyIndyUserArg?: DestroyIndependentUserArg
}>({ open: false })
const passwordFieldRef = useInputRef()
const removeFromNewsletterFieldRef = useInputRef()
const [validatePassword] = useLazyValidatePasswordQuery()

return (
Expand All @@ -102,6 +104,13 @@ const DeleteAccountForm: FC<DeleteAccountFormProps> = ({ authUser }) => {
password: "",
remove_from_newsletter: false,
}}
fieldRefs={[
{ name: "password", inputRef: passwordFieldRef },
{
name: "remove_from_newsletter",
inputRef: removeFromNewsletterFieldRef,
},
]}
onSubmit={values => {
void validatePassword({ id: values.id, password: values.password })
.unwrap()
Expand All @@ -113,6 +122,7 @@ const DeleteAccountForm: FC<DeleteAccountFormProps> = ({ authUser }) => {
<Grid container columnSpacing={4}>
<Grid xs={12} sm={6}>
<forms.PasswordField
inputRef={passwordFieldRef}
required
label="Current password"
placeholder="Enter your current password"
Expand All @@ -122,6 +132,7 @@ const DeleteAccountForm: FC<DeleteAccountFormProps> = ({ authUser }) => {
{/* TODO: only display this checkbox if the user has been added to the newsletter. */}
<forms.CheckboxField
name="remove_from_newsletter"
inputRef={removeFromNewsletterFieldRef}
formControlLabelProps={{
label:
"Please remove me from the newsletter and marketing emails too.",
Expand Down
11 changes: 8 additions & 3 deletions src/components/form/SchoolNameField.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import * as yup from "yup"
import { TextField, type TextFieldProps } from "codeforlife/components/form"
import { Business as BusinessIcon } from "@mui/icons-material"
import { type FC } from "react"
import { InputAdornment } from "@mui/material"
import { TextField } from "codeforlife/components/form"

export interface SchoolNameFieldProps {}
export interface SchoolNameFieldProps
extends Omit<
TextFieldProps,
"required" | "schema" | "name" | "label" | "placeholder" | "InputProps"
> {}

const SchoolNameField: FC<SchoolNameFieldProps> = () => {
const SchoolNameField: FC<SchoolNameFieldProps> = textFieldProps => {
return (
<TextField
required
Expand All @@ -21,6 +25,7 @@ const SchoolNameField: FC<SchoolNameFieldProps> = () => {
</InputAdornment>
),
}}
{...textFieldProps}
/>
)
}
Expand Down
6 changes: 4 additions & 2 deletions src/components/form/TeacherAutocompleteField.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import { type FC, type RefObject } from "react"
import { ApiAutocompleteField } from "codeforlife/components/form"
import { type FC } from "react"

import { type ListUsersArg, useLazyListUsersQuery } from "../../api/user"

export interface TeacherAutocompleteFieldProps {
required?: boolean
name?: string
_id?: ListUsersArg["_id"]
inputRef?: RefObject<HTMLInputElement>
}

const TeacherAutocompleteField: FC<TeacherAutocompleteFieldProps> = ({
required = false,
name = "teacher",
_id,
inputRef,
}) => (
<ApiAutocompleteField
useLazyListQuery={useLazyListUsersQuery}
searchKey="name"
filterOptions={{ type: "teacher", _id }}
getOptionLabel={({ first_name, last_name }) => `${first_name} ${last_name}`}
getOptionKey={({ teacher }) => teacher!.id}
textFieldProps={{ required, name }}
textFieldProps={{ required, name, inputRef }}
/>
)

Expand Down
31 changes: 26 additions & 5 deletions src/components/form/UpdateAccountForm.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as forms from "codeforlife/components/form"
import { getDirty, isDirty } from "codeforlife/utils/form"
import { useInputRef, useNavigate } from "codeforlife/hooks"
import { type FC } from "react"
import { Typography } from "@mui/material"
import { useNavigate } from "codeforlife/hooks"

import {
type RetrieveUserResult,
Expand All @@ -23,6 +23,12 @@ export interface UpdateAccountFormProps {
// TODO: Split this form into two or three forms. Needs UX work
const UpdateAccountForm: FC<UpdateAccountFormProps> = ({ authUser }) => {
const navigate = useNavigate()
const passwordFieldRef = useInputRef()
const passwordRepeatFieldRef = useInputRef()
const currentPasswordFieldRef = useInputRef()
const firstNameFieldRef = useInputRef()
const lastNameFieldRef = useInputRef()
const emailFieldRef = useInputRef()

const initialValues = authUser.student
? {
Expand Down Expand Up @@ -68,9 +74,19 @@ const UpdateAccountForm: FC<UpdateAccountFormProps> = ({ authUser }) => {
)}
<forms.Form
initialValues={initialValues}
fieldRefs={[
{ name: "first_name", inputRef: firstNameFieldRef },
{ name: "last_name", inputRef: lastNameFieldRef },
{ name: "email", inputRef: emailFieldRef },
{ name: "password", inputRef: passwordFieldRef },
{ name: "password_repeat", inputRef: passwordRepeatFieldRef },
{ name: "current_password", inputRef: currentPasswordFieldRef },
]}
useMutation={useUpdateUserMutation}
submitOptions={{
onlyDirtyValues: true,
exclude: ["password_repeat"],
include: ["id"],
clean: (values: typeof initialValues) => {
const arg: UpdateUserArg = { id: values.id }
if (isDirty(values, initialValues, "password")) {
Expand Down Expand Up @@ -132,22 +148,27 @@ const UpdateAccountForm: FC<UpdateAccountFormProps> = ({ authUser }) => {
<>
{!authUser.student && (
<>
<forms.FirstNameField />
<LastNameField />
<forms.EmailField />
<forms.FirstNameField inputRef={firstNameFieldRef} />
<LastNameField inputRef={lastNameFieldRef} />
<forms.EmailField inputRef={emailFieldRef} />
</>
)}
<forms.PasswordField
inputRef={passwordFieldRef}
required={Boolean(authUser.student)}
label="New password"
repeatFieldProps={{ label: "Repeat new password" }}
repeatFieldProps={{
label: "Repeat new password",
inputRef: passwordRepeatFieldRef,
}}
withRepeatField={Boolean(authUser.student) || dirty.password}
schema={passwordSchema}
/>
{(Boolean(authUser.student) || dirty.email || dirty.password) && (
<forms.PasswordField
required
name="current_password"
inputRef={currentPasswordFieldRef}
label="Current password"
placeholder="Enter your current password"
/>
Expand Down
9 changes: 9 additions & 0 deletions src/features/footer/RegisterToNewsletterForm.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as forms from "codeforlife/components/form"
import { FormHelperText, Stack, useMediaQuery, useTheme } from "@mui/material"
import { type FC } from "react"
import { useInputRef } from "codeforlife/hooks"
import { useNavigate } from "react-router-dom"

import { useRegisterToNewsletterMutation } from "../../api/user"
Expand All @@ -11,6 +12,8 @@ const RegisterToNewsletterForm: FC<RegisterToNewsletterFormProps> = () => {
const theme = useTheme()
const onlyXS = useMediaQuery(theme.breakpoints.only("xs"))
const navigate = useNavigate()
const emailFieldRef = useInputRef()
const over18FieldRef = useInputRef()

return (
<Stack id="register-to-newsletter-form">
Expand All @@ -23,6 +26,10 @@ const RegisterToNewsletterForm: FC<RegisterToNewsletterFormProps> = () => {
email: "",
over18: false,
}}
fieldRefs={[
{ name: "email", inputRef: emailFieldRef },
{ name: "over18", inputRef: over18FieldRef },
]}
useMutation={useRegisterToNewsletterMutation}
submitOptions={{
exclude: ["over18"],
Expand Down Expand Up @@ -60,6 +67,7 @@ const RegisterToNewsletterForm: FC<RegisterToNewsletterFormProps> = () => {
id="newsletter-email" // Avoid duplicate IDs on pages which have forms with an email field
FormHelperTextProps={{ style: { color: "white" } }}
required
inputRef={emailFieldRef}
/>
<Stack
spacing="auto"
Expand All @@ -70,6 +78,7 @@ const RegisterToNewsletterForm: FC<RegisterToNewsletterFormProps> = () => {
<forms.CheckboxField
required
name="over18"
inputRef={over18FieldRef}
formControlLabelProps={{
label: "Please confirm that you are over 18.",
}}
Expand Down
Loading

0 comments on commit 3520092

Please sign in to comment.