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:
parent
d0c96288a0
commit
5606f8e075
15
src/App.tsx
15
src/App.tsx
@ -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");
|
||||||
|
@ -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
|
||||||
|
@ -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? </RegisterLabel>
|
<RegisterLabel>Don't have an account? </RegisterLabel>
|
||||||
<RegisterLink href="#">Sign Up</RegisterLink>
|
<RegisterLink
|
||||||
|
onClick={() => {
|
||||||
|
navigate("/register");
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</RegisterLink>
|
||||||
</RegisterContainer>
|
</RegisterContainer>
|
||||||
</FormContainer>
|
</FormContainer>
|
||||||
</LoginBox>
|
</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 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;
|
||||||
|
Loading…
Reference in New Issue
Block a user