mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-14 21:10:34 +01:00
Website edit functionality.
This commit is contained in:
parent
30b87bc4c4
commit
00e232fee8
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import Page from './Page';
|
import Page from './layout/Page';
|
||||||
import styles from './Account.module.css';
|
import styles from './Account.module.css';
|
||||||
|
|
||||||
export default function Account() {
|
export default function Account() {
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default function Footer() {
|
|
||||||
return <footer className="container mt-5 mb-5">umami - deliciously simple web stats</footer>;
|
|
||||||
}
|
|
@ -1,30 +1,36 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import Page from './Page';
|
import Page from './layout/Page';
|
||||||
import Table from './Table';
|
import Table from './common/Table';
|
||||||
import Button from './Button';
|
import Button from './interface/Button';
|
||||||
import Icon from './Icon';
|
import PageHeader from './layout/PageHeader';
|
||||||
import PageHeader from './PageHeader';
|
|
||||||
import Pen from 'assets/pen.svg';
|
import Pen from 'assets/pen.svg';
|
||||||
import Trash from 'assets/trash.svg';
|
import Trash from 'assets/trash.svg';
|
||||||
import Plus from 'assets/plus.svg';
|
import Plus from 'assets/plus.svg';
|
||||||
import { get } from 'lib/web';
|
import { get } from 'lib/web';
|
||||||
|
import Modal from './common/Modal';
|
||||||
|
import WebsiteForm from './forms/WebsiteForm';
|
||||||
|
import styles from './Settings.module.css';
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
const [data, setData] = useState();
|
const [data, setData] = useState();
|
||||||
|
const [edit, setEdit] = useState();
|
||||||
|
const [del, setDelete] = useState();
|
||||||
|
const [saved, setSaved] = useState(0);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'name', label: 'Name' },
|
{ key: 'name', label: 'Name' },
|
||||||
{ key: 'domain', label: 'Domain' },
|
{ key: 'domain', label: 'Domain' },
|
||||||
{
|
{
|
||||||
key: 'action',
|
key: 'action',
|
||||||
label: '',
|
cell: {
|
||||||
style: { flex: 0 },
|
className: styles.buttons,
|
||||||
render: ({ website_id }) => (
|
},
|
||||||
|
render: row => (
|
||||||
<>
|
<>
|
||||||
<Button icon={<Pen />} size="S">
|
<Button icon={<Pen />} size="S" onClick={() => setEdit(row)}>
|
||||||
<div>Edit</div>
|
<div>Edit</div>
|
||||||
</Button>
|
</Button>
|
||||||
<Button icon={<Trash />} size="S">
|
<Button icon={<Trash />} size="S" onClick={() => setDelete(row)}>
|
||||||
<div>Delete</div>
|
<div>Delete</div>
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
@ -32,13 +38,23 @@ export default function Settings() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function handleSave() {
|
||||||
|
setSaved(state => state + 1);
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
setEdit(null);
|
||||||
|
setDelete(null);
|
||||||
|
}
|
||||||
|
|
||||||
async function loadData() {
|
async function loadData() {
|
||||||
setData(await get(`/api/website`));
|
setData(await get(`/api/website`));
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadData();
|
loadData();
|
||||||
}, []);
|
}, [saved]);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return null;
|
return null;
|
||||||
@ -47,13 +63,17 @@ export default function Settings() {
|
|||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<div>Settings</div>
|
<div>Websites</div>
|
||||||
<Button size="S">
|
<Button icon={<Plus />} size="S">
|
||||||
<Icon icon={<Plus />} size="S" />
|
|
||||||
<div>Add website</div>
|
<div>Add website</div>
|
||||||
</Button>
|
</Button>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<Table columns={columns} rows={data.websites} />
|
<Table columns={columns} rows={data} />
|
||||||
|
{edit && (
|
||||||
|
<Modal title="Edit website">
|
||||||
|
<WebsiteForm initialValues={edit} onSave={handleSave} onClose={handleClose} />
|
||||||
|
</Modal>
|
||||||
|
)}
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
4
components/Settings.module.css
Normal file
4
components/Settings.module.css
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
.buttons {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
@ -1,14 +1,14 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import WebsiteChart from './WebsiteChart';
|
import WebsiteChart from './charts/WebsiteChart';
|
||||||
import RankingsChart from './RankingsChart';
|
import RankingsChart from './charts/RankingsChart';
|
||||||
import WorldMap from './WorldMap';
|
import WorldMap from './common/WorldMap';
|
||||||
import Page from './Page';
|
import Page from './layout/Page';
|
||||||
import { getDateRange } from 'lib/date';
|
import { getDateRange } from 'lib/date';
|
||||||
import { get } from 'lib/web';
|
import { get } from 'lib/web';
|
||||||
import { browserFilter, urlFilter, refFilter, deviceFilter, countryFilter } from 'lib/filters';
|
import { browserFilter, urlFilter, refFilter, deviceFilter, countryFilter } from 'lib/filters';
|
||||||
import styles from './WebsiteDetails.module.css';
|
import styles from './WebsiteDetails.module.css';
|
||||||
import PageHeader from './PageHeader';
|
import PageHeader from './layout/PageHeader';
|
||||||
|
|
||||||
const pageviewClasses = 'col-md-12 col-lg-6';
|
const pageviewClasses = 'col-md-12 col-lg-6';
|
||||||
const sessionClasses = 'col-12 col-lg-4';
|
const sessionClasses = 'col-12 col-lg-4';
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { get } from 'lib/web';
|
import { get } from 'lib/web';
|
||||||
import Link from './Link';
|
import Link from './interface/Link';
|
||||||
import WebsiteChart from './WebsiteChart';
|
import WebsiteChart from './charts/WebsiteChart';
|
||||||
import Page from './Page';
|
import Page from './layout/Page';
|
||||||
import Icon from './Icon';
|
import Icon from './interface/Icon';
|
||||||
import Button from './Button';
|
import Button from './interface/Button';
|
||||||
import PageHeader from './PageHeader';
|
import PageHeader from './layout/PageHeader';
|
||||||
import Arrow from 'assets/arrow-right.svg';
|
import Arrow from 'assets/arrow-right.svg';
|
||||||
import styles from './WebsiteList.module.css';
|
import styles from './WebsiteList.module.css';
|
||||||
|
|
||||||
@ -24,32 +24,31 @@ export default function WebsiteList() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
{data &&
|
{data?.map(({ website_id, name }) => (
|
||||||
data.websites.map(({ website_id, name }) => (
|
<div key={website_id} className={styles.website}>
|
||||||
<div key={website_id} className={styles.website}>
|
<PageHeader>
|
||||||
<PageHeader>
|
<Link
|
||||||
<Link
|
href="/website/[...id]"
|
||||||
href="/website/[...id]"
|
as={`/website/${website_id}/${name}`}
|
||||||
as={`/website/${website_id}/${name}`}
|
className={styles.title}
|
||||||
className={styles.title}
|
>
|
||||||
>
|
{name}
|
||||||
{name}
|
</Link>
|
||||||
</Link>
|
<Button
|
||||||
<Button
|
icon={<Arrow />}
|
||||||
icon={<Arrow />}
|
onClick={() =>
|
||||||
onClick={() =>
|
router.push('/website/[...id]', `/website/${website_id}/${name}`, {
|
||||||
router.push('/website/[...id]', `/website/${website_id}/${name}`, {
|
shallow: true,
|
||||||
shallow: true,
|
})
|
||||||
})
|
}
|
||||||
}
|
size="S"
|
||||||
size="S"
|
>
|
||||||
>
|
<div>View details</div>
|
||||||
<div>View details</div>
|
</Button>
|
||||||
</Button>
|
</PageHeader>
|
||||||
</PageHeader>
|
<WebsiteChart key={website_id} title={name} websiteId={website_id} />
|
||||||
<WebsiteChart key={website_id} title={name} websiteId={website_id} />
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Button from './Button';
|
import Button from '../interface/Button';
|
||||||
import { getDateRange } from 'lib/date';
|
import { getDateRange } from 'lib/date';
|
||||||
import styles from './QuickButtons.module.css';
|
import styles from './QuickButtons.module.css';
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useState, useEffect, useMemo } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import { useSpring, animated, config } from 'react-spring';
|
import { useSpring, animated, config } from 'react-spring';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import CheckVisible from './CheckVisible';
|
import CheckVisible from '../helpers/CheckVisible';
|
||||||
import { get } from 'lib/web';
|
import { get } from 'lib/web';
|
||||||
import { percentFilter } from 'lib/filters';
|
import { percentFilter } from 'lib/filters';
|
||||||
import styles from './RankingsChart.module.css';
|
import styles from './RankingsChart.module.css';
|
@ -1,11 +1,11 @@
|
|||||||
import React, { useState, useEffect, useMemo, useRef } from 'react';
|
import React, { useState, useEffect, useMemo, useRef } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import PageviewsChart from './PageviewsChart';
|
import PageviewsChart from './PageviewsChart';
|
||||||
import CheckVisible from './CheckVisible';
|
import CheckVisible from '../helpers/CheckVisible';
|
||||||
import MetricsBar from './MetricsBar';
|
import MetricsBar from './MetricsBar';
|
||||||
import QuickButtons from './QuickButtons';
|
import QuickButtons from './QuickButtons';
|
||||||
import DateFilter from './DateFilter';
|
import DateFilter from '../common/DateFilter';
|
||||||
import StickyHeader from './StickyHeader';
|
import StickyHeader from '../helpers/StickyHeader';
|
||||||
import { get } from 'lib/web';
|
import { get } from 'lib/web';
|
||||||
import { getDateArray, getDateRange, getTimezone } from 'lib/date';
|
import { getDateArray, getDateRange, getTimezone } from 'lib/date';
|
||||||
import styles from './WebsiteChart.module.css';
|
import styles from './WebsiteChart.module.css';
|
@ -1,10 +1,10 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Menu from './Menu';
|
import Menu from '../interface/Menu';
|
||||||
import useDocumentClick from 'hooks/useDocumentClick';
|
import useDocumentClick from 'hooks/useDocumentClick';
|
||||||
import Chevron from 'assets/chevron-down.svg';
|
import Chevron from 'assets/chevron-down.svg';
|
||||||
import styles from './Dropdown.module.css';
|
import styles from './Dropdown.module.css';
|
||||||
import Icon from './Icon';
|
import Icon from '../interface/Icon';
|
||||||
|
|
||||||
export default function DropDown({
|
export default function DropDown({
|
||||||
value,
|
value,
|
16
components/common/Modal.js
Normal file
16
components/common/Modal.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useSpring, animated } from 'react-spring';
|
||||||
|
import styles from './Modal.module.css';
|
||||||
|
|
||||||
|
export default function Modal({ title, children }) {
|
||||||
|
const props = useSpring({ opacity: 1, from: { opacity: 0 } });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<animated.div className={styles.modal} style={props}>
|
||||||
|
<div className={styles.content}>
|
||||||
|
{title && <div className={styles.header}>{title}</div>}
|
||||||
|
<div className={styles.body}>{children}</div>
|
||||||
|
</div>
|
||||||
|
</animated.div>
|
||||||
|
);
|
||||||
|
}
|
45
components/common/Modal.module.css
Normal file
45
components/common/Modal.module.css
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
.modal {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal:before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
background: var(--gray900);
|
||||||
|
opacity: 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background: var(--gray50);
|
||||||
|
min-width: 200px;
|
||||||
|
min-height: 100px;
|
||||||
|
z-index: 1;
|
||||||
|
border: 1px solid var(--gray300);
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
@ -6,8 +6,12 @@ export default function Table({ columns, rows }) {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.table}>
|
<div className={styles.table}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
{columns.map(({ key, label }) => (
|
{columns.map(({ key, label, header }) => (
|
||||||
<div key={key} className={styles.head}>
|
<div
|
||||||
|
key={key}
|
||||||
|
className={classNames(styles.head, header?.className)}
|
||||||
|
style={header?.style}
|
||||||
|
>
|
||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -15,11 +19,11 @@ export default function Table({ columns, rows }) {
|
|||||||
<div className={styles.body}>
|
<div className={styles.body}>
|
||||||
{rows.map((row, rowIndex) => (
|
{rows.map((row, rowIndex) => (
|
||||||
<div className={styles.row} key={rowIndex}>
|
<div className={styles.row} key={rowIndex}>
|
||||||
{columns.map(({ key, render, className, style }) => (
|
{columns.map(({ key, render, cell }) => (
|
||||||
<div
|
<div
|
||||||
key={`${rowIndex}${key}`}
|
key={`${rowIndex}${key}`}
|
||||||
className={classNames(styles.cell, className)}
|
className={classNames(styles.cell, cell?.className)}
|
||||||
style={style}
|
style={cell?.style}
|
||||||
>
|
>
|
||||||
{render ? render(row) : row[key]}
|
{render ? render(row) : row[key]}
|
||||||
</div>
|
</div>
|
@ -14,6 +14,11 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
border-bottom: 1px solid var(--gray300);
|
border-bottom: 1px solid var(--gray300);
|
@ -2,9 +2,9 @@ import React, { useState } from 'react';
|
|||||||
import { Formik, Form, Field } 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 '../interface/Button';
|
||||||
import FormLayout, { FormButtons, FormError, FormMessage, FormRow } from './FormLayout';
|
import FormLayout, { FormButtons, FormError, FormMessage, FormRow } from '../layout/FormLayout';
|
||||||
import styles from './Login.module.css';
|
import styles from './LoginForm.module.css';
|
||||||
|
|
||||||
const validate = ({ username, password }) => {
|
const validate = ({ username, password }) => {
|
||||||
const errors = {};
|
const errors = {};
|
||||||
@ -19,7 +19,7 @@ const validate = ({ username, password }) => {
|
|||||||
return errors;
|
return errors;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Login() {
|
export default function LoginForm() {
|
||||||
const [message, setMessage] = useState();
|
const [message, setMessage] = useState();
|
||||||
|
|
||||||
const handleSubmit = async ({ username, password }) => {
|
const handleSubmit = async ({ username, password }) => {
|
64
components/forms/WebsiteForm.js
Normal file
64
components/forms/WebsiteForm.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Formik, Form, Field } from 'formik';
|
||||||
|
import Router from 'next/router';
|
||||||
|
import { post } from 'lib/web';
|
||||||
|
import Button from 'components/interface/Button';
|
||||||
|
import FormLayout, {
|
||||||
|
FormButtons,
|
||||||
|
FormError,
|
||||||
|
FormMessage,
|
||||||
|
FormRow,
|
||||||
|
} from 'components/layout/FormLayout';
|
||||||
|
|
||||||
|
const validate = ({ name, domain }) => {
|
||||||
|
const errors = {};
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
errors.name = 'Required';
|
||||||
|
}
|
||||||
|
if (!domain) {
|
||||||
|
errors.domain = 'Required';
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function WebsiteForm({ initialValues, onSave, onClose }) {
|
||||||
|
const [message, setMessage] = useState();
|
||||||
|
|
||||||
|
const handleSubmit = async values => {
|
||||||
|
const response = await post(`/api/website`, values);
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
onSave();
|
||||||
|
} else {
|
||||||
|
setMessage('Something went wrong.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormLayout>
|
||||||
|
<Formik initialValues={initialValues} validate={validate} onSubmit={handleSubmit}>
|
||||||
|
{() => (
|
||||||
|
<Form>
|
||||||
|
<FormRow>
|
||||||
|
<label htmlFor="name">Name</label>
|
||||||
|
<Field name="name" type="text" />
|
||||||
|
<FormError name="name" />
|
||||||
|
</FormRow>
|
||||||
|
<FormRow>
|
||||||
|
<label htmlFor="domain">Domain</label>
|
||||||
|
<Field name="domain" type="text" />
|
||||||
|
<FormError name="domain" />
|
||||||
|
</FormRow>
|
||||||
|
<FormButtons>
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
<Button onClick={onClose}>Cancel</Button>
|
||||||
|
</FormButtons>
|
||||||
|
<FormMessage>{message}</FormMessage>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</FormLayout>
|
||||||
|
);
|
||||||
|
}
|
11
components/layout/Footer.js
Normal file
11
components/layout/Footer.js
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
3
components/layout/Footer.module.css
Normal file
3
components/layout/Footer.module.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.footer {
|
||||||
|
font-size: var(--font-size-small);
|
||||||
|
}
|
@ -33,11 +33,24 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 100%;
|
left: 100%;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
margin-left: 10px;
|
margin-left: 16px;
|
||||||
padding: 4px 8px;
|
padding: 4px 10px;
|
||||||
border-radius: 4px;
|
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 {
|
.message {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 20px 0;
|
margin: 20px 0;
|
@ -1,9 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Link from 'components/Link';
|
import Link from 'components/interface/Link';
|
||||||
import UserButton from './UserButton';
|
import UserButton from '../interface/UserButton';
|
||||||
import Icon from './Icon';
|
import Icon from '../interface/Icon';
|
||||||
import Logo from 'assets/logo.svg';
|
import Logo from 'assets/logo.svg';
|
||||||
import styles from './Header.module.css';
|
import styles from './Header.module.css';
|
||||||
|
|
@ -1,8 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Header from 'components/Header';
|
import Header from 'components/layout/Header';
|
||||||
import Footer from 'components/Footer';
|
import Footer from 'components/layout/Footer';
|
||||||
import styles from './Layout.module.css';
|
import styles from './Layout.module.css';
|
||||||
|
|
||||||
export default function Layout({
|
export default function Layout({
|
14
lib/db.js
14
lib/db.js
@ -56,6 +56,20 @@ export async function getWebsites(user_id) {
|
|||||||
where: {
|
where: {
|
||||||
user_id,
|
user_id,
|
||||||
},
|
},
|
||||||
|
orderBy: {
|
||||||
|
name: 'asc',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateWebsite(website_id, data) {
|
||||||
|
return runQuery(
|
||||||
|
prisma.website.update({
|
||||||
|
where: {
|
||||||
|
website_id,
|
||||||
|
},
|
||||||
|
data,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Layout from 'components/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
|
|
||||||
export default function Custom404() {
|
export default function Custom404() {
|
||||||
return (
|
return (
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Layout from 'components/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
import Account from 'components/Account';
|
import Account from 'components/Account';
|
||||||
import useRequireLogin from 'hooks/useRequireLogin';
|
import useRequireLogin from 'hooks/useRequireLogin';
|
||||||
|
|
||||||
|
@ -1,12 +1,26 @@
|
|||||||
import { getWebsites } from 'lib/db';
|
import { getWebsites, updateWebsite } from 'lib/db';
|
||||||
import { useAuth } from 'lib/middleware';
|
import { useAuth } from 'lib/middleware';
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
await useAuth(req, res);
|
await useAuth(req, res);
|
||||||
|
|
||||||
const { user_id } = req.auth;
|
const { user_id } = req.auth;
|
||||||
|
const { website_id } = req.body;
|
||||||
|
|
||||||
const websites = await getWebsites(user_id);
|
if (req.method === 'GET') {
|
||||||
|
const websites = await getWebsites(user_id);
|
||||||
|
|
||||||
return res.status(200).json({ websites });
|
return res.status(200).json(websites);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.method === 'POST') {
|
||||||
|
if (website_id) {
|
||||||
|
const { name, domain } = req.body;
|
||||||
|
const website = await updateWebsite(website_id, { name, domain });
|
||||||
|
|
||||||
|
return res.status(200).json(website);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.status(405).end();
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Layout from 'components/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
import WebsiteList from 'components/WebsiteList';
|
import WebsiteList from 'components/WebsiteList';
|
||||||
import useRequireLogin from 'hooks/useRequireLogin';
|
import useRequireLogin from 'hooks/useRequireLogin';
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Layout from 'components/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
import Login from 'components/Login';
|
import LoginForm from 'components/forms/LoginForm';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/interface/Icon';
|
||||||
import Logo from 'assets/logo.svg';
|
import Logo from 'assets/logo.svg';
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
return (
|
return (
|
||||||
<Layout title="login" header={false} footer={false} center middle>
|
<Layout title="login" header={false} footer={false} center middle>
|
||||||
<Icon icon={<Logo />} size="XL" />
|
<Icon icon={<Logo />} size="XL" />
|
||||||
<Login />
|
<LoginForm />
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Layout from 'components/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
import Settings from 'components/Settings';
|
import Settings from 'components/Settings';
|
||||||
import useRequireLogin from 'hooks/useRequireLogin';
|
import useRequireLogin from 'hooks/useRequireLogin';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Layout from 'components/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
|
|
||||||
export default function Test({ websiteId }) {
|
export default function Test({ websiteId }) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import Layout from 'components/Layout';
|
import Layout from 'components/layout/Layout';
|
||||||
import WebsiteDetails from 'components/WebsiteDetails';
|
import WebsiteDetails from 'components/WebsiteDetails';
|
||||||
import useRequireLogin from 'hooks/useRequireLogin';
|
import useRequireLogin from 'hooks/useRequireLogin';
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user