impress-2020/src/app/components/LoginModal.js
Matchu 4d0c48ab7c Login form checks the db, and saves a cookie
Okay so one of the trickiest parts of login is done! 🤞 and now we need to make it actually show up in the UI. (and also pressure-test the security a bit, I've only really checked the happy path!)
2022-08-17 00:58:52 -07:00

187 lines
4.8 KiB
JavaScript

import { gql, useMutation } from "@apollo/client";
import {
Box,
Button,
FormControl,
FormHelperText,
FormLabel,
Input,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalHeader,
ModalOverlay,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
} from "@chakra-ui/react";
import React from "react";
import { ErrorMessage, getGraphQLErrorMessage } from "../util";
export default function LoginModal({ isOpen, onClose }) {
return (
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Welcome back to Dress to Impress! </ModalHeader>
<ModalCloseButton />
<Tabs>
<TabList>
<Tab>Log in</Tab>
<Tab>Create account</Tab>
</TabList>
<TabPanels>
<TabPanel>
<ModalBody>
<LoginForm onSuccess={() => onClose()} />
</ModalBody>
</TabPanel>
<TabPanel>
<ModalBody>
<CreateAccountForm />
</ModalBody>
</TabPanel>
</TabPanels>
</Tabs>
</ModalContent>
</Modal>
);
}
function LoginForm({ onSuccess }) {
const [username, setUsername] = React.useState("");
const [password, setPassword] = React.useState("");
const [
sendLoginMutation,
{ loading, error, data, called, reset },
] = useMutation(gql`
mutation LoginForm_Login($username: String!, $password: String!) {
login(username: $username, password: $password) {
id
username
}
}
`);
return (
<form
onSubmit={(e) => {
e.preventDefault();
sendLoginMutation({
variables: { username, password },
})
.then(({ data }) => {
if (data?.login != null) {
onSuccess();
}
})
.catch((e) => {}); // handled in error UI
}}
>
<FormControl>
<FormLabel>DTI Username</FormLabel>
<Input
type="text"
value={username}
onChange={(e) => {
setUsername(e.target.value);
reset();
}}
/>
<FormHelperText>
This is separate from your Neopets.com account.
</FormHelperText>
</FormControl>
<Box height="4" />
<FormControl>
<FormLabel>DTI Password</FormLabel>
<Input
type="password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
reset();
}}
/>
<FormHelperText>
Careful, never enter your Neopets password on another site!
</FormHelperText>
</FormControl>
{error && (
<ErrorMessage marginTop="4">
Oops, login failed: "{getGraphQLErrorMessage(error)}". Try again?
</ErrorMessage>
)}
{called && !loading && !error && data?.login == null && (
<ErrorMessage marginTop="4">
We couldn't find a match for that username and password. Try again?
</ErrorMessage>
)}
<Box marginTop="6" display="flex" alignItems="center">
<Button size="sm" onClick={() => alert("TODO: Forgot password")}>
Forgot password?
</Button>
<Box flex="1 0 auto" width="4" />
<Button type="submit" colorScheme="green" isLoading={loading}>
Log in
</Button>
</Box>
</form>
);
}
function CreateAccountForm() {
const onSubmit = (e) => {
e.preventDefault();
alert("TODO: Create account!");
};
return (
<form onSubmit={onSubmit}>
<FormControl>
<FormLabel>DTI Username</FormLabel>
<Input type="text" />
<FormHelperText>
This will be separate from your Neopets.com account.
</FormHelperText>
</FormControl>
<Box height="4" />
<FormControl>
<FormLabel>DTI Password</FormLabel>
<Input type="password" />
<FormHelperText>
Careful, never use your Neopets password for another site!
</FormHelperText>
</FormControl>
<Box height="4" />
<FormControl>
<FormLabel>Confirm DTI Password</FormLabel>
<Input type="password" />
<FormHelperText>One more time, to make sure!</FormHelperText>
</FormControl>
<Box height="4" />
<FormControl>
<FormLabel>Email address</FormLabel>
<Input type="password" />
<FormHelperText>
We'll use this in the future if you need to reset your password, or
for us to contact you about your account. We won't sell this address,
and we won't send marketing-y emails.
</FormHelperText>
</FormControl>
<Box height="6" />
<Box display="flex" justifyContent="flex-end">
<Button type="submit" colorScheme="green">
Create account
</Button>
</Box>
</form>
);
}