Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Brian Cao 2023-01-12 00:02:17 -08:00
commit fad38dc180
18 changed files with 103 additions and 106 deletions

View File

@ -3,8 +3,10 @@ import Head from 'next/head';
import Header from 'components/layout/Header'; import Header from 'components/layout/Header';
import Footer from 'components/layout/Footer'; import Footer from 'components/layout/Footer';
import useLocale from 'hooks/useLocale'; import useLocale from 'hooks/useLocale';
import useRequireLogin from 'hooks/useRequireLogin';
export default function Layout({ title, children, header = true, footer = true }) { export default function AppLayout({ title, children }) {
useRequireLogin();
const { dir } = useLocale(); const { dir } = useLocale();
return ( return (
@ -12,9 +14,9 @@ export default function Layout({ title, children, header = true, footer = true }
<Head> <Head>
<title>{title ? `${title} | umami` : 'umami'}</title> <title>{title ? `${title} | umami` : 'umami'}</title>
</Head> </Head>
{header && <Header />} <Header />
<main>{children}</main> <main>{children}</main>
{footer && <Footer />} <Footer />
</Container> </Container>
); );
} }

View File

@ -5,11 +5,11 @@ import Page from 'components/layout/Page';
import PageHeader from 'components/layout/PageHeader'; import PageHeader from 'components/layout/PageHeader';
import WebsiteChartList from 'components/pages/websites/WebsiteChartList'; import WebsiteChartList from 'components/pages/websites/WebsiteChartList';
import DashboardSettingsButton from 'components/settings/DashboardSettingsButton'; import DashboardSettingsButton from 'components/settings/DashboardSettingsButton';
import DashboardEdit from 'components/pages/dashboard/DashboardEdit';
import styles from 'components/pages/websites/WebsiteList.module.css';
import useUser from 'hooks/useUser';
import useApi from 'hooks/useApi'; import useApi from 'hooks/useApi';
import useRequireLogin from 'hooks/useRequireLogin';
import useDashboard from 'store/dashboard'; import useDashboard from 'store/dashboard';
import DashboardEdit from './DashboardEdit';
import styles from '../websites/WebsiteList.module.css';
const messages = defineMessages({ const messages = defineMessages({
dashboard: { id: 'label.dashboard', defaultMessage: 'Dashboard' }, dashboard: { id: 'label.dashboard', defaultMessage: 'Dashboard' },
@ -17,7 +17,7 @@ const messages = defineMessages({
}); });
export default function Dashboard({ userId }) { export default function Dashboard({ userId }) {
const { user } = useRequireLogin(); const { user } = useUser();
const dashboard = useDashboard(); const dashboard = useDashboard();
const { showCharts, limit, editing } = dashboard; const { showCharts, limit, editing } = dashboard;
const [max, setMax] = useState(limit); const [max, setMax] = useState(limit);

View File

@ -14,6 +14,7 @@ import useApi from 'hooks/useApi';
import { setUser } from 'store/app'; import { setUser } from 'store/app';
import { setClientAuthToken } from 'lib/client'; import { setClientAuthToken } from 'lib/client';
import Logo from 'assets/logo.svg'; import Logo from 'assets/logo.svg';
import styles from './LoginForm.module.css';
export default function LoginForm() { export default function LoginForm() {
const router = useRouter(); const router = useRouter();
@ -26,20 +27,18 @@ export default function LoginForm() {
setClientAuthToken(token); setClientAuthToken(token);
setUser(user); setUser(user);
await router.push('/settings/websites'); await router.push('/dashboard');
}, },
}); });
}; };
return ( return (
<> <div className={styles.login}>
<div> <Icon className={styles.icon} size="xl">
<Icon size="xl"> <Logo />
<Logo /> </Icon>
</Icon> <div className={styles.title}>umami</div>
<p>umami</p> <Form className={styles.form} onSubmit={handleSubmit} error={error}>
</div>
<Form onSubmit={handleSubmit} error={error}>
<FormRow label="Username"> <FormRow label="Username">
<FormInput name="username" rules={{ required: 'Required' }}> <FormInput name="username" rules={{ required: 'Required' }}>
<TextField autoComplete="off" /> <TextField autoComplete="off" />
@ -51,11 +50,11 @@ export default function LoginForm() {
</FormInput> </FormInput>
</FormRow> </FormRow>
<FormButtons> <FormButtons>
<SubmitButton variant="primary" disabled={isLoading}> <SubmitButton className={styles.button} variant="primary" disabled={isLoading}>
Log in Log in
</SubmitButton> </SubmitButton>
</FormButtons> </FormButtons>
</Form> </Form>
</> </div>
); );
} }

View File

@ -1,23 +1,31 @@
.login { .login {
display: flex; width: 300px;
flex-direction: column; margin: auto;
margin-top: 80px; transform: translateY(-25%);
} }
.login form { .form {
margin: 0 auto; display: flex;
flex-direction: column;
}
.title {
font-size: 24px;
font-weight: 700;
text-align: center;
margin: 30px 0;
} }
.icon { .icon {
display: flex; width: 100%;
}
.icon svg {
width: 32px;
height: 32px;
}
.button {
flex: 1;
justify-content: center; justify-content: center;
margin: 0 auto;
}
.header {
margin-bottom: 30px;
}
.header h1 {
margin: 12px 0;
} }

View File

@ -0,0 +1,16 @@
import Head from 'next/head';
import useLocale from 'hooks/useLocale';
import styles from './LoginLayout.module.css';
export default function LoginLayout({ children }) {
const { dir } = useLocale();
return (
<div className={styles.layout} dir={dir}>
<Head>
<title>{`Login | umami`}</title>
</Head>
{children}
</div>
);
}

View File

@ -0,0 +1,7 @@
.layout {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}

View File

@ -1,23 +1,23 @@
import Layout from 'components/layout/Layout'; import AppLayout from 'components/layout/AppLayout';
import Menu from 'components/nav/Nav'; import Menu from 'components/nav/Nav';
import useRequireLogin from 'hooks/useRequireLogin';
import styles from './SettingsLayout.module.css'; import styles from './SettingsLayout.module.css';
import useConfig from 'hooks/useConfig';
export default function SettingsLayout({ children }) { export default function SettingsLayout({ children }) {
const { user } = useRequireLogin(); const { adminDisabled } = useConfig();
if (!user) { if (adminDisabled) {
return null; return null;
} }
return ( return (
<Layout> <AppLayout>
<div className={styles.dashboard}> <div className={styles.dashboard}>
<div className={styles.nav}> <div className={styles.nav}>
<Menu /> <Menu />
</div> </div>
<div className={styles.content}>{children}</div> <div className={styles.content}>{children}</div>
</div> </div>
</Layout> </AppLayout>
); );
} }

View File

@ -12,8 +12,10 @@ export default function WebsitesList() {
const [edit, setEdit] = useState(false); const [edit, setEdit] = useState(false);
const { get, useQuery } = useApi(); const { get, useQuery } = useApi();
const { user } = useUser(); const { user } = useUser();
const { data, isLoading, error, refetch } = useQuery(['websites', user.id], () => const { data, isLoading, error, refetch } = useQuery(
get(`/users/${user.id}/websites`), ['websites', user?.id],
() => get(`/users/${user?.id}/websites`),
{ enabled: !!user },
); );
const hasData = data && data.length !== 0; const hasData = data && data.length !== 0;
const { toast, showToast } = useToast(); const { toast, showToast } = useToast();

View File

@ -1,11 +1,10 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { setItem } from 'next-basics'; import { get, setItem } from 'next-basics';
import { LOCALE_CONFIG } from 'lib/constants'; import { LOCALE_CONFIG } from 'lib/constants';
import { getDateLocale, getTextDirection } from 'lib/lang'; import { getDateLocale, getTextDirection } from 'lib/lang';
import useStore, { setLocale } from 'store/app'; import useStore, { setLocale } from 'store/app';
import useForceUpdate from 'hooks/useForceUpdate'; import useForceUpdate from 'hooks/useForceUpdate';
import useApi from 'hooks/useApi';
import enUS from 'public/intl/messages/en-US.json'; import enUS from 'public/intl/messages/en-US.json';
const messages = { const messages = {
@ -20,12 +19,11 @@ export default function useLocale() {
const forceUpdate = useForceUpdate(); const forceUpdate = useForceUpdate();
const dir = getTextDirection(locale); const dir = getTextDirection(locale);
const dateLocale = getDateLocale(locale); const dateLocale = getDateLocale(locale);
const { get } = useApi();
async function loadMessages(locale) { async function loadMessages(locale) {
const data = await get(`${basePath}/intl/messages/${locale}.json`); const { ok, data } = await get(`${basePath}/intl/messages/${locale}.json`);
if (data) { if (ok) {
messages[locale] = data; messages[locale] = data;
} }
} }

View File

@ -1,4 +1,4 @@
import Layout from 'components/layout/Layout'; import AppLayout from 'components/layout/AppLayout';
import { useIntl, defineMessages } from 'react-intl'; import { useIntl, defineMessages } from 'react-intl';
const messages = defineMessages({ const messages = defineMessages({
@ -9,10 +9,10 @@ export default function Custom404() {
const { formatMessage } = useIntl(); const { formatMessage } = useIntl();
return ( return (
<Layout> <AppLayout>
<div className="row justify-content-center"> <div className="row justify-content-center">
<h1 style={{ textAlign: 'center' }}>{formatMessage(messages.notFound)}</h1> <h1 style={{ textAlign: 'center' }}>{formatMessage(messages.notFound)}</h1>
</div> </div>
</Layout> </AppLayout>
); );
} }

View File

@ -1,18 +1,18 @@
import Layout from 'components/layout/Layout'; import AppLayout from 'components/layout/AppLayout';
import TestConsole from 'components/pages/console/TestConsole'; import TestConsole from 'components/pages/console/TestConsole';
import useRequireLogin from 'hooks/useRequireLogin'; import useUser from 'hooks/useUser';
export default function ConsolePage({ pageDisabled }) { export default function ConsolePage({ pageDisabled }) {
const { user } = useRequireLogin(); const { user } = useUser();
if (pageDisabled || !user || !user.isAdmin) { if (pageDisabled || !user || !user.isAdmin) {
return null; return null;
} }
return ( return (
<Layout> <AppLayout>
<TestConsole /> <TestConsole />
</Layout> </AppLayout>
); );
} }

View File

@ -1,27 +1,10 @@
import { useRouter } from 'next/router'; import AppLayout from 'components/layout/AppLayout';
import Layout from 'components/layout/Layout';
import Dashboard from 'components/pages/dashboard/Dashboard'; import Dashboard from 'components/pages/dashboard/Dashboard';
import useConfig from 'hooks/useConfig';
import useRequireLogin from 'hooks/useRequireLogin';
export default function DashboardPage() { export default function DashboardPage() {
const {
query: { id },
isReady,
asPath,
} = useRouter();
const { user } = useRequireLogin();
const { adminDisabled } = useConfig();
if (adminDisabled || !user || !isReady) {
return null;
}
const userId = id?.[0];
return ( return (
<Layout> <AppLayout>
<Dashboard key={asPath} userId={user.id || userId} /> <Dashboard />
</Layout> </AppLayout>
); );
} }

View File

@ -1,4 +1,4 @@
import Layout from 'components/layout/Layout'; import LoginLayout from 'components/pages/login/LoginLayout';
import LoginForm from 'components/pages/login/LoginForm'; import LoginForm from 'components/pages/login/LoginForm';
export default function LoginPage({ pageDisabled }) { export default function LoginPage({ pageDisabled }) {
@ -7,9 +7,9 @@ export default function LoginPage({ pageDisabled }) {
} }
return ( return (
<Layout title="login" header={false} footer={false} center> <LoginLayout title="login">
<LoginForm /> <LoginForm />
</Layout> </LoginLayout>
); );
} }

View File

@ -1,17 +1,10 @@
import Layout from 'components/layout/Layout'; import AppLayout from 'components/layout/AppLayout';
import RealtimeDashboard from 'components/pages/realtime/RealtimeDashboard'; import RealtimeDashboard from 'components/pages/realtime/RealtimeDashboard';
import useRequireLogin from 'hooks/useRequireLogin';
export default function RealtimePage() { export default function RealtimePage() {
const { user } = useRequireLogin();
if (!user) {
return null;
}
return ( return (
<Layout> <AppLayout>
<RealtimeDashboard /> <RealtimeDashboard />
</Layout> </AppLayout>
); );
} }

View File

@ -1,16 +1,7 @@
import SettingsLayout from 'components/pages/settings/SettingsLayout'; import SettingsLayout from 'components/pages/settings/SettingsLayout';
import useConfig from 'hooks/useConfig';
import useRequireLogin from 'hooks/useRequireLogin';
import WebsitesList from 'components/pages/settings/websites/WebsitesList'; import WebsitesList from 'components/pages/settings/websites/WebsitesList';
export default function WebsitesPage() { export default function WebsitesPage() {
const { user } = useRequireLogin();
const { adminDisabled } = useConfig();
if (adminDisabled || !user) {
return null;
}
return ( return (
<SettingsLayout> <SettingsLayout>
<WebsitesList /> <WebsitesList />

View File

@ -1,5 +1,5 @@
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import Layout from 'components/layout/Layout'; import AppLayout from 'components/layout/AppLayout';
import WebsiteDetails from 'components/pages/websites/WebsiteDetails'; import WebsiteDetails from 'components/pages/websites/WebsiteDetails';
import useShareToken from 'hooks/useShareToken'; import useShareToken from 'hooks/useShareToken';
@ -14,8 +14,8 @@ export default function SharePage() {
} }
return ( return (
<Layout> <AppLayout>
<WebsiteDetails websiteId={shareToken.websiteId} /> <WebsiteDetails websiteId={shareToken.websiteId} />
</Layout> </AppLayout>
); );
} }

View File

@ -1,20 +1,18 @@
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import Layout from 'components/layout/Layout'; import AppLayout from 'components/layout/AppLayout';
import WebsiteDetails from 'components/pages/websites/WebsiteDetails'; import WebsiteDetails from 'components/pages/websites/WebsiteDetails';
import useRequireLogin from 'hooks/useRequireLogin';
export default function DetailsPage() { export default function DetailsPage() {
const { user } = useRequireLogin();
const router = useRouter(); const router = useRouter();
const { id } = router.query; const { id } = router.query;
if (!id || !user) { if (!id) {
return null; return null;
} }
return ( return (
<Layout> <AppLayout>
<WebsiteDetails websiteId={id} /> <WebsiteDetails websiteId={id} />
</Layout> </AppLayout>
); );
} }