Skip to content

Commit

Permalink
add login forms
Browse files Browse the repository at this point in the history
  • Loading branch information
SKairinos committed Jun 13, 2024
1 parent b1f1219 commit 63d6726
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src/api/sso.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const ssoApi = api.injectEndpoints({
body,
}),
}),
loginWithOtp: build.mutation<null, { otp: number }>({
loginWithOtp: build.mutation<null, { otp: string }>({
query: body => ({
url: baseUrl + "login-with-otp/",
method: "POST",
Expand Down
2 changes: 1 addition & 1 deletion src/app/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as yup from "yup"

export const accessCodeSchema = yup
export const classIdSchema = yup
.string()
.matches(/^[A-Z0-9]{5}$/, "Invalid access code")

Expand Down
2 changes: 2 additions & 0 deletions src/app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const reducer = combineSlices(api)
// Infer the `RootState` type from the root reducer
export type RootState = ReturnType<typeof reducer>

// TODO: create middleware for api errors.
// https://redux-toolkit.js.org/rtk-query/usage/error-handling#handling-errors-at-a-macro-level
const store = makeStore({ reducer, middlewares: [api.middleware] })

export default store
Expand Down
12 changes: 7 additions & 5 deletions src/pages/login/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import * as page from "codeforlife/components/page"
// import { tryValidateSync } from "codeforlife/utils/schema"

import IndyForm from "./IndyForm"
import StudentForm from "./StudentForm"
import * as studentForms from "./studentForms"
import * as teacherForms from "./teacherForms"

export interface LoginProps {
form:
| "teacher-password"
| "teacher-email"
| "teacher-otp"
| "teacher-otp-bypass-token"
| "student"
| "student-class"
| "student-first-name"
| "indy"
}

Expand All @@ -36,10 +37,11 @@ const Login: FC<LoginProps> = ({ form }) => {
<page.Section maxWidth="md">
{
{
"teacher-password": <teacherForms.Password />,
"teacher-email": <teacherForms.Email />,
"teacher-otp": <teacherForms.Otp />,
"teacher-otp-bypass-token": <teacherForms.OtpBypassToken />,
student: <StudentForm />,
"student-class": <studentForms.Class />,
"student-first-name": <studentForms.FirstName />,
indy: <IndyForm />,
}[form]
}
Expand Down
37 changes: 0 additions & 37 deletions src/pages/login/StudentForm.tsx

This file was deleted.

47 changes: 47 additions & 0 deletions src/pages/login/studentForms/Class.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ChevronRight as ChevronRightIcon } from "@mui/icons-material"
import { Stack, Typography } from "@mui/material"
import type { FC } from "react"
import { generatePath } from "react-router-dom"

import * as form from "codeforlife/components/form"
import { useNavigate } from "codeforlife/hooks"

import { classIdSchema } from "../../../app/schemas"
import { paths } from "../../../router"
import BaseForm from "../BaseForm"

export interface ClassProps {}

const Class: FC<ClassProps> = () => {
const navigate = useNavigate()

return (
<BaseForm
themedBoxProps={{ userType: "student" }}
header="Welcome"
subheader="Please enter your class code."
initialValues={{ classId: "" }}
onSubmit={({ classId }) => {
navigate(generatePath(paths.login.student.class._, { classId }))
}}
>
<form.TextField
name="classId"
placeholder="Access code"
helperText="Enter your access code"

Check failure on line 31 in src/pages/login/studentForms/Class.tsx

View workflow job for this annotation

GitHub Actions / main / test / test-js-code

Type '{ name: string; placeholder: string; helperText: string; schema: StringSchema<string | undefined, AnyObject, undefined, "">; required: true; }' is not assignable to type 'IntrinsicAttributes & Omit<TextFieldProps, "defaultValue" | "id" | "onBlur" | "onChange" | "value" | "name" | "error" | "helperText"> & { ...; }'.
schema={classIdSchema}
required
/>
<Typography variant="body2" fontWeight="bold">
Forgotten your login details? Please check with your teacher.
</Typography>
<Stack alignItems="end">
<form.SubmitButton endIcon={<ChevronRightIcon />}>
Next
</form.SubmitButton>
</Stack>
</BaseForm>
)
}

export default Class
80 changes: 80 additions & 0 deletions src/pages/login/studentForms/FirstName.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { ChevronRight as ChevronRightIcon } from "@mui/icons-material"
import { Stack } from "@mui/material"
import { useEffect, type FC } from "react"
import { useParams } from "react-router-dom"
import * as yup from "yup"

import * as form from "codeforlife/components/form"
import { useNavigate } from "codeforlife/hooks"
import { submitForm } from "codeforlife/utils/form"
import { tryValidateSync } from "codeforlife/utils/schema"

import { useLoginAsStudentMutation } from "../../../api/sso"
import { classIdSchema } from "../../../app/schemas"
import { paths } from "../../../router"
import BaseForm from "../BaseForm"

export interface FirstNameProps {}

const FirstName: FC<FirstNameProps> = () => {
const [loginAsStudent] = useLoginAsStudentMutation()
const navigate = useNavigate()

const params = tryValidateSync(
useParams(),
yup.object({ classId: classIdSchema.required() }),
)

useEffect(() => {
if (!params) {
navigate(paths.login.student._, {
state: {
notifications: [
{
props: {
error: true,
children: "Please provide a valid access code for your class.",
},
},
],
},
})
}
}, [navigate, params])

return (
<>
{params && (
<BaseForm
themedBoxProps={{ userType: "student" }}
header={`Welcome to class: ${params.classId}`}
subheader="Please enter your login details."
initialValues={{
first_name: "",
password: "",
class_id: params.classId,
}}
onSubmit={submitForm(loginAsStudent, {
then: () => {
navigate(paths.student.dashboard._)
},
})}
>
<form.FirstNameField

Check failure on line 63 in src/pages/login/studentForms/FirstName.tsx

View workflow job for this annotation

GitHub Actions / main / test / test-js-code

Property 'FirstNameField' does not exist on type 'typeof import("/home/runner/work/codeforlife-portal-frontend/codeforlife-portal-frontend/node_modules/codeforlife/src/components/form/index")'.
placeholder="Username"
helperText="Enter your username"
required
/>
<form.PasswordField helperText="Enter your password" required />

Check failure on line 68 in src/pages/login/studentForms/FirstName.tsx

View workflow job for this annotation

GitHub Actions / main / test / test-js-code

Type '{ helperText: string; required: true; }' is not assignable to type 'IntrinsicAttributes & Omit<TextFieldProps, "name" | "type" | "schema"> & Partial<Pick<TextFieldProps, "name" | "schema">>'.
<Stack alignItems="end">
<form.SubmitButton endIcon={<ChevronRightIcon />}>
Log in
</form.SubmitButton>
</Stack>
</BaseForm>
)}
</>
)
}

export default FirstName
4 changes: 4 additions & 0 deletions src/pages/login/studentForms/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import Class, { type ClassProps } from "./Class"
import FirstName, { type FirstNameProps } from "./FirstName"

export { Class, FirstName, type ClassProps, type FirstNameProps }
2 changes: 1 addition & 1 deletion src/router/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const paths = _("", {
}),
}),
student: _("/student", {
class: _("/:accessCode"),
class: _("/:classId"),
}),
independent: _("/independent"),
}),
Expand Down
11 changes: 9 additions & 2 deletions src/router/routes/authentication.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const authentication = (
<>
<Route
path={paths.login.teacher._}
element={<Login form="teacher-password" />}
element={<Login form="teacher-email" />}
/>
<Route
path={paths.login.teacher.otp._}
Expand All @@ -34,7 +34,14 @@ const authentication = (
path={paths.login.teacher.otp.bypassToken._}
element={<Login form="teacher-otp-bypass-token" />}
/>
<Route path={paths.login.student._} element={<Login form="student" />} />
<Route
path={paths.login.student._}
element={<Login form="student-class" />}
/>
<Route
path={paths.login.student.class._}
element={<Login form="student-first-name" />}
/>
<Route path={paths.login.independent._} element={<Login form="indy" />} />
</>
)
Expand Down

0 comments on commit 63d6726

Please sign in to comment.