mirror of
https://github.com/kremalicious/umami.git
synced 2024-11-22 18:00:17 +01:00
Form components. New login page.
This commit is contained in:
parent
9d09d89aef
commit
a09867f28c
1
assets/logo.svg
Normal file
1
assets/logo.svg
Normal 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
20
components/FormLayout.js
Normal 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>;
|
48
components/FormLayout.module.css
Normal file
48
components/FormLayout.module.css
Normal 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);
|
||||||
|
}
|
@ -3,6 +3,8 @@ import { useSelector } from 'react-redux';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Link from 'components/Link';
|
import Link from 'components/Link';
|
||||||
import UserButton from './UserButton';
|
import UserButton from './UserButton';
|
||||||
|
import Icon from './Icon';
|
||||||
|
import Logo from 'assets/logo.svg';
|
||||||
import styles from './Header.module.css';
|
import styles from './Header.module.css';
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
@ -12,7 +14,10 @@ export default function Header() {
|
|||||||
<header className={classNames(styles.header, 'container')}>
|
<header className={classNames(styles.header, 'container')}>
|
||||||
<div className="row align-items-center">
|
<div className="row align-items-center">
|
||||||
<div className="col">
|
<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>
|
</div>
|
||||||
{user && (
|
{user && (
|
||||||
<div className="col">
|
<div className="col">
|
||||||
|
@ -23,3 +23,7 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-left: 40px;
|
margin-left: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
@ -6,6 +6,7 @@ export default function Icon({ icon, className, size = 'M' }) {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(styles.icon, className, {
|
className={classNames(styles.icon, className, {
|
||||||
|
[styles.xl]: size === 'XL',
|
||||||
[styles.large]: size === 'L',
|
[styles.large]: size === 'L',
|
||||||
[styles.medium]: size === 'M',
|
[styles.medium]: size === 'M',
|
||||||
[styles.small]: size === 'S',
|
[styles.small]: size === 'S',
|
||||||
|
@ -9,6 +9,11 @@
|
|||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.xl > svg {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
.large > svg {
|
.large > svg {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Header from 'components/Header';
|
import Header from 'components/Header';
|
||||||
import Footer from 'components/Footer';
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
@ -14,9 +23,16 @@ export default function Layout({ title, children }) {
|
|||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
</Head>
|
</Head>
|
||||||
<Header />
|
{header && <Header />}
|
||||||
<main className="container">{children}</main>
|
<main
|
||||||
<Footer />
|
className={classNames(styles.layout, 'container', {
|
||||||
|
[styles.center]: center,
|
||||||
|
[styles.middle]: middle,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
{footer && <Footer />}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
12
components/Layout.module.css
Normal file
12
components/Layout.module.css
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.layout {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.middle {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Formik, Form, Field, ErrorMessage } from 'formik';
|
import { Formik, Form, Field } from 'formik';
|
||||||
import Router from 'next/router';
|
import Router from 'next/router';
|
||||||
import { post } from 'lib/web';
|
import { post } from 'lib/web';
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
|
import FormLayout, { FormButtons, FormError, FormMessage, FormRow } from './FormLayout';
|
||||||
import styles from './Login.module.css';
|
import styles from './Login.module.css';
|
||||||
|
|
||||||
const validate = ({ username, password }) => {
|
const validate = ({ username, password }) => {
|
||||||
@ -32,6 +33,7 @@ export default function Login() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<FormLayout>
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
username: '',
|
username: '',
|
||||||
@ -41,21 +43,25 @@ export default function Login() {
|
|||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
{() => (
|
{() => (
|
||||||
<Form className={styles.form}>
|
<Form>
|
||||||
<h3>{message}</h3>
|
<h1 className={styles.title}>umami</h1>
|
||||||
<div>
|
<FormRow>
|
||||||
<label htmlFor="username">Username</label>
|
<label htmlFor="username">Username</label>
|
||||||
<Field name="username" type="text" />
|
<Field name="username" type="text" />
|
||||||
<ErrorMessage name="username" />
|
<FormError name="username" />
|
||||||
</div>
|
</FormRow>
|
||||||
<div>
|
<FormRow>
|
||||||
<label htmlFor="password">Password</label>
|
<label htmlFor="password">Password</label>
|
||||||
<Field name="password" type="password" />
|
<Field name="password" type="password" />
|
||||||
<ErrorMessage name="password" />
|
<FormError name="password" />
|
||||||
</div>
|
</FormRow>
|
||||||
<Button type="submit">Submit</Button>
|
<FormButtons>
|
||||||
|
<Button type="submit">Login</Button>
|
||||||
|
</FormButtons>
|
||||||
|
<FormMessage>{message}</FormMessage>
|
||||||
</Form>
|
</Form>
|
||||||
)}
|
)}
|
||||||
</Formik>
|
</Formik>
|
||||||
|
</FormLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
.form {
|
.title {
|
||||||
position: absolute;
|
font-size: var(--font-size-xlarge);
|
||||||
left: 50%;
|
text-align: center;
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Layout from 'components/Layout';
|
import Layout from 'components/Layout';
|
||||||
import Login from 'components/Login';
|
import Login from 'components/Login';
|
||||||
|
import Icon from 'components/Icon';
|
||||||
|
import Logo from 'assets/logo.svg';
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
return (
|
return (
|
||||||
<Layout title="login">
|
<Layout title="login" header={false} footer={false} center middle>
|
||||||
|
<Icon icon={<Logo />} size="XL" />
|
||||||
<Login />
|
<Login />
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
|
@ -19,7 +19,12 @@ body {
|
|||||||
box-sizing: inherit;
|
box-sizing: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,16 +47,9 @@ a:visited {
|
|||||||
color: var(--primary400);
|
color: var(--primary400);
|
||||||
}
|
}
|
||||||
|
|
||||||
form label {
|
|
||||||
display: inline-block;
|
|
||||||
min-width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
input,
|
input,
|
||||||
textarea {
|
textarea {
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
margin-right: 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
font-size: var(--font-size-normal);
|
font-size: var(--font-size-normal);
|
||||||
line-height: 1.8;
|
line-height: 1.8;
|
||||||
border: 1px solid var(--gray500);
|
border: 1px solid var(--gray500);
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
--gray400: #cacaca;
|
--gray400: #cacaca;
|
||||||
--gray500: #b3b3b3;
|
--gray500: #b3b3b3;
|
||||||
--gray600: #8e8e8e;
|
--gray600: #8e8e8e;
|
||||||
--gray6700: #6e6e6e;
|
--gray700: #6e6e6e;
|
||||||
--gray800: #4b4b4b;
|
--gray800: #4b4b4b;
|
||||||
--gray900: #2c2c2c;
|
--gray900: #2c2c2c;
|
||||||
|
|
||||||
@ -26,4 +26,6 @@
|
|||||||
--grid-size-medium: 768px;
|
--grid-size-medium: 768px;
|
||||||
--grid-size-large: 992px;
|
--grid-size-large: 992px;
|
||||||
--grid-size-xlarge: 1140px;
|
--grid-size-xlarge: 1140px;
|
||||||
|
|
||||||
|
--color-error: #e34850;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user