Website edit functionality.

This commit is contained in:
Mike Cao 2020-08-07 02:27:12 -07:00
parent 30b87bc4c4
commit 00e232fee8
63 changed files with 301 additions and 94 deletions

View file

@ -0,0 +1,11 @@
import React from 'react';
import classNames from 'classnames';
import styles from './Footer.module.css';
export default function Footer() {
return (
<footer className={classNames(styles.footer, 'container mt-5 mb-5')}>
umami - deliciously simple web stats
</footer>
);
}

View file

@ -0,0 +1,3 @@
.footer {
font-size: var(--font-size-small);
}

View file

@ -0,0 +1,21 @@
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 }) =>
children ? <div className={styles.message}>{children}</div> : null;

View file

@ -0,0 +1,61 @@
.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: 16px;
padding: 4px 10px;
border-radius: 4px;
}
.error:after {
content: '';
position: absolute;
top: 0;
left: -5px;
bottom: 0;
margin: auto;
width: 10px;
height: 10px;
background: var(--color-error);
transform: rotate(45deg);
}
.message {
text-align: center;
margin: 20px 0;
padding: 4px 8px;
border-radius: 4px;
color: var(--gray50);
background: var(--gray800);
}

View file

@ -0,0 +1,34 @@
import React from 'react';
import { useSelector } from 'react-redux';
import classNames from 'classnames';
import Link from 'components/interface/Link';
import UserButton from '../interface/UserButton';
import Icon from '../interface/Icon';
import Logo from 'assets/logo.svg';
import styles from './Header.module.css';
export default function Header() {
const user = useSelector(state => state.user);
return (
<header className={classNames(styles.header, 'container')}>
<div className="row align-items-center">
<div className="col">
<div className={styles.title}>
<Icon icon={<Logo />} size="L" className={styles.logo} />
{user ? <Link href="/">umami</Link> : 'umami'}
</div>
</div>
{user && (
<div className="col">
<div className={styles.nav}>
<Link href="/dashboard">Dashboard</Link>
<Link href="/settings">Settings</Link>
<UserButton />
</div>
</div>
)}
</div>
</header>
);
}

View file

@ -0,0 +1,29 @@
.header {
display: flex;
height: 80px;
}
.header > div {
flex: 1;
}
.title {
font-size: var(--font-size-large);
}
.nav {
list-style: none;
display: flex;
justify-content: flex-end;
align-items: center;
}
.nav > * {
font-size: var(--font-size-normal);
font-weight: 600;
margin-left: 40px;
}
.logo {
margin-right: 12px;
}

View file

@ -0,0 +1,38 @@
import React from 'react';
import classNames from 'classnames';
import Head from 'next/head';
import Header from 'components/layout/Header';
import Footer from 'components/layout/Footer';
import styles from './Layout.module.css';
export default function Layout({
title,
children,
header = true,
footer = true,
center = false,
middle = false,
}) {
return (
<>
<Head>
<title>umami{title && ` - ${title}`}</title>
<link rel="icon" href="/favicon.ico" />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&display=swap"
rel="stylesheet"
/>
</Head>
{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

@ -0,0 +1,6 @@
import React from 'react';
import styles from './Page.module.css';
export default function Page({ children }) {
return <div className={styles.container}>{children}</div>;
}

View file

@ -0,0 +1,8 @@
.container {
flex: 1;
display: flex;
flex-direction: column;
padding: 0 30px;
background: var(--gray50);
height: 100%;
}

View file

@ -0,0 +1,12 @@
import React, { Children } from 'react';
import styles from './PageHeader.module.css';
export default function PageHeader({ children }) {
const [firstChild, ...otherChildren] = Children.toArray(children);
return (
<div className={styles.header}>
<div className={styles.title}> {firstChild}</div>
{otherChildren && <div>{otherChildren}</div>}
</div>
);
}

View file

@ -0,0 +1,10 @@
.header {
display: flex;
justify-content: space-between;
align-items: center;
line-height: 80px;
}
.title {
font-size: var(--font-size-large);
}