mirror of https://github.com/coder/coder.git
132 lines
3.6 KiB
TypeScript
132 lines
3.6 KiB
TypeScript
import { makeStyles } from "@material-ui/core/styles"
|
|
import { FormikContextType, useFormik } from "formik"
|
|
import { Location } from "history"
|
|
import { useNavigate, useLocation } from "react-router-dom"
|
|
import React from "react"
|
|
import { useSWRConfig } from "swr"
|
|
import * as Yup from "yup"
|
|
|
|
import { Welcome } from "./Welcome"
|
|
import { FormTextField } from "../Form"
|
|
import * as API from "./../../api"
|
|
import { LoadingButton } from "./../Button"
|
|
|
|
/**
|
|
* BuiltInAuthFormValues describes a form using built-in (email/password)
|
|
* authentication. This form may not always be present depending on external
|
|
* auth providers available and administrative configurations
|
|
*/
|
|
interface BuiltInAuthFormValues {
|
|
email: string
|
|
password: string
|
|
}
|
|
|
|
const validationSchema = Yup.object({
|
|
email: Yup.string().required("Email is required."),
|
|
password: Yup.string(),
|
|
})
|
|
|
|
const useStyles = makeStyles((theme) => ({
|
|
loginBtnWrapper: {
|
|
marginTop: theme.spacing(6),
|
|
borderTop: `1px solid ${theme.palette.action.disabled}`,
|
|
paddingTop: theme.spacing(3),
|
|
},
|
|
loginTextField: {
|
|
marginTop: theme.spacing(2),
|
|
},
|
|
submitBtn: {
|
|
marginTop: theme.spacing(2),
|
|
},
|
|
}))
|
|
|
|
export interface SignInProps {
|
|
loginHandler?: (email: string, password: string) => Promise<void>
|
|
}
|
|
|
|
export const SignInForm: React.FC<SignInProps> = ({
|
|
loginHandler = (email: string, password: string) => API.login(email, password),
|
|
}) => {
|
|
const navigate = useNavigate()
|
|
const location = useLocation()
|
|
const styles = useStyles()
|
|
const { mutate } = useSWRConfig()
|
|
|
|
const form: FormikContextType<BuiltInAuthFormValues> = useFormik<BuiltInAuthFormValues>({
|
|
initialValues: {
|
|
email: "",
|
|
password: "",
|
|
},
|
|
validationSchema,
|
|
onSubmit: async ({ email, password }, helpers) => {
|
|
try {
|
|
await loginHandler(email, password)
|
|
// Tell SWR to invalidate the cache for the user endpoint
|
|
await mutate("/api/v2/users/me")
|
|
|
|
const redirect = getRedirectFromLocation(location)
|
|
await navigate(redirect)
|
|
} catch (err) {
|
|
helpers.setFieldError("password", "The username or password is incorrect.")
|
|
}
|
|
},
|
|
})
|
|
|
|
return (
|
|
<>
|
|
<Welcome />
|
|
<form onSubmit={form.handleSubmit}>
|
|
<div>
|
|
<FormTextField
|
|
autoComplete="email"
|
|
autoFocus
|
|
className={styles.loginTextField}
|
|
eventTransform={(email: string) => email.trim()}
|
|
form={form}
|
|
formFieldName="email"
|
|
fullWidth
|
|
inputProps={{
|
|
id: "signin-form-inpt-email",
|
|
}}
|
|
placeholder="Email"
|
|
variant="outlined"
|
|
/>
|
|
<FormTextField
|
|
autoComplete="current-password"
|
|
className={styles.loginTextField}
|
|
form={form}
|
|
formFieldName="password"
|
|
fullWidth
|
|
inputProps={{
|
|
id: "signin-form-inpt-password",
|
|
}}
|
|
isPassword
|
|
placeholder="Password"
|
|
variant="outlined"
|
|
/>
|
|
</div>
|
|
<div className={styles.submitBtn}>
|
|
<LoadingButton
|
|
color="primary"
|
|
loading={form.isSubmitting}
|
|
fullWidth
|
|
id="signin-form-submit"
|
|
type="submit"
|
|
variant="contained"
|
|
>
|
|
Sign In
|
|
</LoadingButton>
|
|
</div>
|
|
</form>
|
|
</>
|
|
)
|
|
}
|
|
|
|
const getRedirectFromLocation = (location: Location) => {
|
|
const defaultRedirect = "/"
|
|
|
|
const searchParams = new URLSearchParams(location.search)
|
|
const redirect = searchParams.get("redirect")
|
|
return redirect ? redirect : defaultRedirect
|
|
}
|