mirror of
https://github.com/spacebarchat/client.git
synced 2024-11-22 10:22:30 +01:00
refine login page, add register page
This commit is contained in:
parent
d0c96288a0
commit
5606f8e075
15
src/App.tsx
15
src/App.tsx
@ -31,13 +31,14 @@ function App() {
|
||||
|
||||
// handles token changes
|
||||
React.useEffect(() => {
|
||||
if (!app.api.token && !isLoading) {
|
||||
console.log("TOKEN REMOVED");
|
||||
// remove token
|
||||
secureLocalStorage.removeItem("token");
|
||||
// navigate to login page if token is removed
|
||||
navigate("/login", { replace: true });
|
||||
}
|
||||
// FIXME: this triggers on load and causes a redirect to login no matter what page we actually want to go to
|
||||
// if (!app.api.token && !isLoading) {
|
||||
// console.log("TOKEN REMOVED");
|
||||
// // remove token
|
||||
// secureLocalStorage.removeItem("token");
|
||||
// // navigate to login page if token is removed
|
||||
// navigate("/login", { replace: true });
|
||||
// }
|
||||
|
||||
if (app.api.token) {
|
||||
console.log("TOKEN ADDED");
|
||||
|
@ -11,6 +11,7 @@ export type ThemeVariables =
|
||||
| "tertiary"
|
||||
| "text"
|
||||
| "textMuted"
|
||||
| "textLink"
|
||||
| "inputBackground"
|
||||
| "error"
|
||||
| "buttonPrimary"
|
||||
@ -47,6 +48,7 @@ export const ThemePresets: Record<string, Theme> = {
|
||||
tertiary: "#e9e2e1",
|
||||
text: "#000000",
|
||||
textMuted: "#232120",
|
||||
textLink: "#00a8fc",
|
||||
inputBackground: "#757575",
|
||||
error: "#e83f36",
|
||||
buttonPrimary: "",
|
||||
@ -74,6 +76,7 @@ export const ThemePresets: Record<string, Theme> = {
|
||||
tertiary: "#141212",
|
||||
text: "#e9e2e1",
|
||||
textMuted: "#85898f",
|
||||
textLink: "#00a8fc",
|
||||
inputBackground: "#121212",
|
||||
error: "#e83f36",
|
||||
// buttons
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { APIError, CaptchaError, MFAError } from "@puyodead1/fosscord-ts";
|
||||
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";
|
||||
@ -98,13 +99,20 @@ const Input = styled.input<{ error?: boolean }>`
|
||||
aria-invalid: ${(props) => (props.error ? "true" : "false")};
|
||||
`;
|
||||
|
||||
const PasswordResetLink = styled.a`
|
||||
const PasswordResetLink = styled.button`
|
||||
margin-bottom: 20px;
|
||||
margin-top: 4px;
|
||||
padding: 2px 0;
|
||||
font-size: 14px;
|
||||
display: flex;
|
||||
text-decoration: none;
|
||||
color: var(--text-link);
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
const LoginButton = styled(Button)`
|
||||
@ -123,13 +131,20 @@ const RegisterLabel = styled.label`
|
||||
font-size: 14px;
|
||||
`;
|
||||
|
||||
const RegisterLink = styled.a`
|
||||
const RegisterLink = styled.button`
|
||||
font-size: 14px;
|
||||
text-decoration: none;
|
||||
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`
|
||||
@ -143,6 +158,7 @@ type LoginFormValues = {
|
||||
|
||||
function LoginPage() {
|
||||
const app = useAppStore();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const {
|
||||
register,
|
||||
@ -224,14 +240,23 @@ function LoginPage() {
|
||||
</InputWrapper>
|
||||
</InputContainer>
|
||||
|
||||
<PasswordResetLink href="#">Forgot your password?</PasswordResetLink>
|
||||
<PasswordResetLink onClick={() => {}} type="button">
|
||||
Forgot your password?
|
||||
</PasswordResetLink>
|
||||
<LoginButton variant="primary" type="submit">
|
||||
Log In
|
||||
</LoginButton>
|
||||
|
||||
<RegisterContainer>
|
||||
<RegisterLabel>Don't have an account? </RegisterLabel>
|
||||
<RegisterLink href="#">Sign Up</RegisterLink>
|
||||
<RegisterLink
|
||||
onClick={() => {
|
||||
navigate("/register");
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
Sign Up
|
||||
</RegisterLink>
|
||||
</RegisterContainer>
|
||||
</FormContainer>
|
||||
</LoginBox>
|
||||
|
@ -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 { 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() {
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user