1
0
mirror of https://github.com/spacebarchat/client.git synced 2024-11-25 11:42:30 +01:00

refine login page, add register page

This commit is contained in:
Puyodead1 2023-04-04 09:57:45 -04:00
parent d0c96288a0
commit 5606f8e075
No known key found for this signature in database
GPG Key ID: A4FA4FEC0DD353FC
4 changed files with 296 additions and 14 deletions

View File

@ -31,13 +31,14 @@ function App() {
// handles token changes // handles token changes
React.useEffect(() => { React.useEffect(() => {
if (!app.api.token && !isLoading) { // FIXME: this triggers on load and causes a redirect to login no matter what page we actually want to go to
console.log("TOKEN REMOVED"); // if (!app.api.token && !isLoading) {
// remove token // console.log("TOKEN REMOVED");
secureLocalStorage.removeItem("token"); // // remove token
// navigate to login page if token is removed // secureLocalStorage.removeItem("token");
navigate("/login", { replace: true }); // // navigate to login page if token is removed
} // navigate("/login", { replace: true });
// }
if (app.api.token) { if (app.api.token) {
console.log("TOKEN ADDED"); console.log("TOKEN ADDED");

View File

@ -11,6 +11,7 @@ export type ThemeVariables =
| "tertiary" | "tertiary"
| "text" | "text"
| "textMuted" | "textMuted"
| "textLink"
| "inputBackground" | "inputBackground"
| "error" | "error"
| "buttonPrimary" | "buttonPrimary"
@ -47,6 +48,7 @@ export const ThemePresets: Record<string, Theme> = {
tertiary: "#e9e2e1", tertiary: "#e9e2e1",
text: "#000000", text: "#000000",
textMuted: "#232120", textMuted: "#232120",
textLink: "#00a8fc",
inputBackground: "#757575", inputBackground: "#757575",
error: "#e83f36", error: "#e83f36",
buttonPrimary: "", buttonPrimary: "",
@ -74,6 +76,7 @@ export const ThemePresets: Record<string, Theme> = {
tertiary: "#141212", tertiary: "#141212",
text: "#e9e2e1", text: "#e9e2e1",
textMuted: "#85898f", textMuted: "#85898f",
textLink: "#00a8fc",
inputBackground: "#121212", inputBackground: "#121212",
error: "#e83f36", error: "#e83f36",
// buttons // buttons

View File

@ -1,5 +1,6 @@
import { APIError, CaptchaError, MFAError } from "@puyodead1/fosscord-ts"; import { APIError, CaptchaError, MFAError } from "@puyodead1/fosscord-ts";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
import styled from "styled-components"; import styled from "styled-components";
import Button from "../components/Button"; import Button from "../components/Button";
import Container from "../components/Container"; import Container from "../components/Container";
@ -98,13 +99,20 @@ const Input = styled.input<{ error?: boolean }>`
aria-invalid: ${(props) => (props.error ? "true" : "false")}; aria-invalid: ${(props) => (props.error ? "true" : "false")};
`; `;
const PasswordResetLink = styled.a` const PasswordResetLink = styled.button`
margin-bottom: 20px; margin-bottom: 20px;
margin-top: 4px; margin-top: 4px;
padding: 2px 0; padding: 2px 0;
font-size: 14px; font-size: 14px;
display: flex; display: flex;
text-decoration: none; color: var(--text-link);
background: none;
border: none;
&:hover {
text-decoration: underline;
cursor: pointer;
}
`; `;
const LoginButton = styled(Button)` const LoginButton = styled(Button)`
@ -123,13 +131,20 @@ const RegisterLabel = styled.label`
font-size: 14px; font-size: 14px;
`; `;
const RegisterLink = styled.a` const RegisterLink = styled.button`
font-size: 14px; font-size: 14px;
text-decoration: none; background: none;
border: none;
color: var(--text-link);
@media (max-width: 480px) { @media (max-width: 480px) {
display: inline-block; display: inline-block;
} }
&:hover {
text-decoration: underline;
cursor: pointer;
}
`; `;
const Divider = styled.span` const Divider = styled.span`
@ -143,6 +158,7 @@ type LoginFormValues = {
function LoginPage() { function LoginPage() {
const app = useAppStore(); const app = useAppStore();
const navigate = useNavigate();
const { const {
register, register,
@ -224,14 +240,23 @@ function LoginPage() {
</InputWrapper> </InputWrapper>
</InputContainer> </InputContainer>
<PasswordResetLink href="#">Forgot your password?</PasswordResetLink> <PasswordResetLink onClick={() => {}} type="button">
Forgot your password?
</PasswordResetLink>
<LoginButton variant="primary" type="submit"> <LoginButton variant="primary" type="submit">
Log In Log In
</LoginButton> </LoginButton>
<RegisterContainer> <RegisterContainer>
<RegisterLabel>Don't have an account?&nbsp;</RegisterLabel> <RegisterLabel>Don't have an account?&nbsp;</RegisterLabel>
<RegisterLink href="#">Sign Up</RegisterLink> <RegisterLink
onClick={() => {
navigate("/register");
}}
type="button"
>
Sign Up
</RegisterLink>
</RegisterContainer> </RegisterContainer>
</FormContainer> </FormContainer>
</LoginBox> </LoginBox>

View File

@ -1,7 +1,260 @@
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import Button from "../components/Button";
import Container from "../components/Container"; import Container from "../components/Container";
import { useAppStore } from "../stores/AppStore";
const Wrapper = styled(Container)`
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: var(--secondary);
`;
const LoginBox = styled(Container)`
background-color: var(--primary-alt);
padding: 32px;
font-size: 18px;
color: var(--text-muted);
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
@media (max-width: 480px) {
width: 100%;
height: 100%;
}
@media (min-width: 480px) {
width: 480px;
border-radius: 18px;
}
`;
const HeaderContainer = styled.div`
width: 100%;
`;
const Header = styled.h1`
font-weight: 600;
margin-bottom: 8px;
font-size: 24px;
color: var(--text);
`;
const SubHeader = styled.h2`
color: var(--text-muted);
font-weight: 400;
font-size: 16px;
`;
const FormContainer = styled.form`
width: 100%;
`;
const InputContainer = styled.h1<{ marginBottom: boolean }>`
margin-bottom: ${(props) => (props.marginBottom ? "20px" : "0")};
display: flex;
flex-direction: column;
align-items: flex-start;
`;
const LabelWrapper = styled.div<{ error?: boolean }>`
display: flex;
flex-direction: row;
margin-bottom: 8px;
color: ${(props) => (props.error ? "var(--error)" : "#b1b5bc")};
`;
const InputErrorText = styled.label`
font-size: 14px;
font-weight: 400;
font-style: italic;
`;
const InputLabel = styled.label`
font-size: 14px;
font-weight: 700;
`;
const InputWrapper = styled.div`
width: 100%;
display: flex;
`;
const Input = styled.input<{ error?: boolean }>`
outline: none;
background: var(--secondary);
padding: 10px;
font-size: 16px;
flex: 1;
border-radius: 12px;
color: var(--text);
margin: 0;
border: none;
aria-invalid: ${(props) => (props.error ? "true" : "false")};
`;
const LoginButton = styled(Button)`
margin-top: 20px;
margin-bottom: 8px;
width: 100%;
min-width: 130px;
min-height: 44px;
`;
const LoginLink = styled.button`
margin-top: 4px;
float: left;
font-size: 14px;
background: none;
border: none;
color: var(--text-link);
@media (max-width: 480px) {
display: inline-block;
}
&:hover {
text-decoration: underline;
cursor: pointer;
}
`;
const Divider = styled.span`
padding: 0 4px;
`;
type RegisterFormValues = {
email: string;
username: string;
password: string;
date_of_birth: string;
};
function RegistrationPage() { function RegistrationPage() {
return <Container>RegistrationPage</Container>; const app = useAppStore();
const navigate = useNavigate();
const {
register,
handleSubmit,
watch,
formState: { errors },
setError,
} = useForm<RegisterFormValues>();
const onSubmit = handleSubmit((data) => {
// TODO:
// app.api.login(data).catch((e) => {
// if (e instanceof MFAError) {
// console.log("MFA Required", e);
// } else if (e instanceof CaptchaError) {
// console.log("Captcha Required", e);
// } else if (e instanceof APIError) {
// console.log("APIError", e.message, e.code, e.fieldErrors);
// e.fieldErrors.forEach((fieldError) => {
// setError(fieldError.field as any, {
// type: "manual",
// message: fieldError.error,
// });
// });
// } else {
// console.log("General Error", e);
// }
// });
});
return (
<Wrapper>
<LoginBox>
<HeaderContainer>
<Header>Create an account</Header>
{/* <SubHeader>We're so excited to see you again!</SubHeader> */}
</HeaderContainer>
<FormContainer onSubmit={onSubmit}>
<InputContainer marginBottom={true} style={{ marginTop: 0 }}>
<LabelWrapper error={!!errors.email}>
<InputLabel>Email</InputLabel>
{errors.email && (
<InputErrorText>
<>
<Divider>-</Divider>
{errors.email.message}
</>
</InputErrorText>
)}
</LabelWrapper>
<InputWrapper>
<Input
type="email"
autoFocus
{...register("email", { required: true })}
error={!!errors.email}
/>
</InputWrapper>
</InputContainer>
<InputContainer marginBottom={true} style={{ marginTop: 0 }}>
<LabelWrapper error={!!errors.username}>
<InputLabel>Username</InputLabel>
{errors.username && (
<InputErrorText>
<>
<Divider>-</Divider>
{errors.username.message}
</>
</InputErrorText>
)}
</LabelWrapper>
<InputWrapper>
<Input
{...register("username", { required: true })}
error={!!errors.username}
/>
</InputWrapper>
</InputContainer>
<InputContainer marginBottom={false}>
<LabelWrapper error={!!errors.password}>
<InputLabel>Password</InputLabel>
{errors.password && (
<InputErrorText>
<>
<Divider>-</Divider>
{errors.password.message}
</>
</InputErrorText>
)}
</LabelWrapper>
<InputWrapper>
<Input
type="password"
{...register("password", { required: true })}
error={!!errors.password}
/>
</InputWrapper>
</InputContainer>
<LoginButton variant="primary" type="submit">
Log In
</LoginButton>
<LoginLink
onClick={() => {
navigate("/login", { replace: true });
}}
type="button"
>
Already have an account?
</LoginLink>
</FormContainer>
</LoginBox>
</Wrapper>
);
} }
export default RegistrationPage; export default RegistrationPage;