Form components. New login page.

This commit is contained in:
Mike Cao 2020-08-06 22:03:02 -07:00
parent 9d09d89aef
commit a09867f28c
14 changed files with 165 additions and 45 deletions

1
assets/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 428 389.11"><defs><style>.cls-1{fill:#fff;stroke:#000;stroke-miterlimit:10;stroke-width:20px;}</style></defs><title>Asset 2</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_3" data-name="Layer 3"><circle class="cls-1" cx="214.15" cy="181" r="171"/><path d="M0,175.11c0,118.19,95.81,214,214,214s214-95.81,214-214a215.65,215.65,0,0,0-3-36H3A215.65,215.65,0,0,0,0,175.11Z"/><rect x="0.29" y="134.11" width="427.71" height="60" rx="15"/></g></g></svg>

After

Width:  |  Height:  |  Size: 507 B

20
components/FormLayout.js Normal file
View File

@ -0,0 +1,20 @@
import React from 'react';
import classNames from 'classnames';
import { ErrorMessage } from 'formik';
import styles from './FormLayout.module.css';
export default function FormLayout({ className, children }) {
return <div className={classNames(styles.form, className)}>{children}</div>;
}
export const FormButtons = ({ className, children }) => (
<div className={classNames(styles.buttons, className)}>{children}</div>
);
export const FormError = ({ name }) => (
<ErrorMessage name={name}>{msg => <div className={styles.error}>{msg}</div>}</ErrorMessage>
);
export const FormRow = ({ children }) => <div className={styles.row}>{children}</div>;
export const FormMessage = ({ children }) => <div className={styles.message}>{children}</div>;

View File

@ -0,0 +1,48 @@
.form {
display: flex;
flex-direction: column;
justify-content: center;
}
.form label {
display: inline-block;
min-width: 100px;
}
.row {
display: flex;
align-items: center;
position: relative;
margin-bottom: 20px;
line-height: 1.8;
}
.buttons {
display: flex;
justify-content: center;
}
.error {
color: var(--gray50);
background: var(--color-error);
font-size: var(--font-size-small);
position: absolute;
display: flex;
justify-content: center;
align-items: center;
top: 0;
left: 100%;
bottom: 0;
margin-left: 10px;
padding: 4px 8px;
border-radius: 4px;
}
.message {
text-align: center;
margin: 20px 0;
padding: 4px 8px;
border-radius: 4px;
color: var(--gray50);
background: var(--gray800);
}

View File

@ -3,6 +3,8 @@ import { useSelector } from 'react-redux';
import classNames from 'classnames';
import Link from 'components/Link';
import UserButton from './UserButton';
import Icon from './Icon';
import Logo from 'assets/logo.svg';
import styles from './Header.module.css';
export default function Header() {
@ -12,7 +14,10 @@ export default function Header() {
<header className={classNames(styles.header, 'container')}>
<div className="row align-items-center">
<div className="col">
<div className={styles.title}>{user ? <Link href="/">umami</Link> : 'umami'}</div>
<div className={styles.title}>
<Icon icon={<Logo />} size="L" className={styles.logo} />
{user ? <Link href="/">umami</Link> : 'umami'}
</div>
</div>
{user && (
<div className="col">

View File

@ -23,3 +23,7 @@
font-weight: 600;
margin-left: 40px;
}
.logo {
margin-right: 12px;
}

View File

@ -6,6 +6,7 @@ export default function Icon({ icon, className, size = 'M' }) {
return (
<div
className={classNames(styles.icon, className, {
[styles.xl]: size === 'XL',
[styles.large]: size === 'L',
[styles.medium]: size === 'M',
[styles.small]: size === 'S',

View File

@ -9,6 +9,11 @@
fill: currentColor;
}
.xl > svg {
width: 48px;
height: 48px;
}
.large > svg {
width: 24px;
height: 24px;

View File

@ -1,9 +1,18 @@
import React from 'react';
import classNames from 'classnames';
import Head from 'next/head';
import Header from 'components/Header';
import Footer from 'components/Footer';
import styles from './Layout.module.css';
export default function Layout({ title, children }) {
export default function Layout({
title,
children,
header = true,
footer = true,
center = false,
middle = false,
}) {
return (
<>
<Head>
@ -14,9 +23,16 @@ export default function Layout({ title, children }) {
rel="stylesheet"
/>
</Head>
<Header />
<main className="container">{children}</main>
<Footer />
{header && <Header />}
<main
className={classNames(styles.layout, 'container', {
[styles.center]: center,
[styles.middle]: middle,
})}
>
{children}
</main>
{footer && <Footer />}
</>
);
}

View File

@ -0,0 +1,12 @@
.layout {
display: flex;
flex-direction: column;
}
.center {
align-items: center;
}
.middle {
justify-content: center;
}

View File

@ -1,8 +1,9 @@
import React, { useState } from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';
import { Formik, Form, Field } from 'formik';
import Router from 'next/router';
import { post } from 'lib/web';
import Button from './Button';
import FormLayout, { FormButtons, FormError, FormMessage, FormRow } from './FormLayout';
import styles from './Login.module.css';
const validate = ({ username, password }) => {
@ -32,30 +33,35 @@ export default function Login() {
};
return (
<Formik
initialValues={{
username: '',
password: '',
}}
validate={validate}
onSubmit={handleSubmit}
>
{() => (
<Form className={styles.form}>
<h3>{message}</h3>
<div>
<label htmlFor="username">Username</label>
<Field name="username" type="text" />
<ErrorMessage name="username" />
</div>
<div>
<label htmlFor="password">Password</label>
<Field name="password" type="password" />
<ErrorMessage name="password" />
</div>
<Button type="submit">Submit</Button>
</Form>
)}
</Formik>
<FormLayout>
<Formik
initialValues={{
username: '',
password: '',
}}
validate={validate}
onSubmit={handleSubmit}
>
{() => (
<Form>
<h1 className={styles.title}>umami</h1>
<FormRow>
<label htmlFor="username">Username</label>
<Field name="username" type="text" />
<FormError name="username" />
</FormRow>
<FormRow>
<label htmlFor="password">Password</label>
<Field name="password" type="password" />
<FormError name="password" />
</FormRow>
<FormButtons>
<Button type="submit">Login</Button>
</FormButtons>
<FormMessage>{message}</FormMessage>
</Form>
)}
</Formik>
</FormLayout>
);
}

View File

@ -1,5 +1,4 @@
.form {
position: absolute;
left: 50%;
transform: translateX(-50%);
.title {
font-size: var(--font-size-xlarge);
text-align: center;
}

View File

@ -1,10 +1,13 @@
import React from 'react';
import Layout from 'components/Layout';
import Login from 'components/Login';
import Icon from 'components/Icon';
import Logo from 'assets/logo.svg';
export default function LoginPage() {
return (
<Layout title="login">
<Layout title="login" header={false} footer={false} center middle>
<Icon icon={<Logo />} size="XL" />
<Login />
</Layout>
);

View File

@ -19,7 +19,12 @@ body {
box-sizing: inherit;
}
h2 {
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 400;
}
@ -42,16 +47,9 @@ a:visited {
color: var(--primary400);
}
form label {
display: inline-block;
min-width: 100px;
}
input,
textarea {
padding: 4px 8px;
margin-right: 10px;
margin-bottom: 20px;
font-size: var(--font-size-normal);
line-height: 1.8;
border: 1px solid var(--gray500);

View File

@ -7,7 +7,7 @@
--gray400: #cacaca;
--gray500: #b3b3b3;
--gray600: #8e8e8e;
--gray6700: #6e6e6e;
--gray700: #6e6e6e;
--gray800: #4b4b4b;
--gray900: #2c2c2c;
@ -26,4 +26,6 @@
--grid-size-medium: 768px;
--grid-size-large: 992px;
--grid-size-xlarge: 1140px;
--color-error: #e34850;
}