This commit is contained in:
Brian Cao 2024-02-06 12:29:48 -08:00
commit 10f1b906ec
236 changed files with 505 additions and 594 deletions

View File

@ -67,7 +67,7 @@
"@prisma/extension-read-replicas": "^0.3.0", "@prisma/extension-read-replicas": "^0.3.0",
"@react-spring/web": "^9.7.3", "@react-spring/web": "^9.7.3",
"@tanstack/react-query": "^5.12.2", "@tanstack/react-query": "^5.12.2",
"@umami/prisma-client": "^0.13.0", "@umami/prisma-client": "^0.14.0",
"@umami/redis-client": "^0.18.0", "@umami/redis-client": "^0.18.0",
"chalk": "^4.1.1", "chalk": "^4.1.1",
"chart.js": "^4.2.1", "chart.js": "^4.2.1",

View File

@ -1,4 +1,3 @@
'use client';
import { Loading } from 'react-basics'; import { Loading } from 'react-basics';
import Script from 'next/script'; import Script from 'next/script';
import { usePathname } from 'next/navigation'; import { usePathname } from 'next/navigation';

View File

@ -1,4 +1,3 @@
'use client';
import { Icon, Text } from 'react-basics'; import { Icon, Text } from 'react-basics';
import Link from 'next/link'; import Link from 'next/link';
import classNames from 'classnames'; import classNames from 'classnames';

View File

@ -1,4 +1,3 @@
'use client';
import { useEffect, useCallback, useState } from 'react'; import { useEffect, useCallback, useState } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { Button } from 'react-basics'; import { Button } from 'react-basics';

View File

@ -0,0 +1,6 @@
'use client';
import TestConsole from './TestConsole';
export default function ConsolePage({ websiteId }) {
return <TestConsole websiteId={websiteId} />;
}

View File

@ -1,6 +1,7 @@
.container { .container {
display: grid; display: grid;
gap: 30px; gap: 30px;
padding-bottom: 40px;
} }
.actions { .actions {

View File

@ -1,4 +1,3 @@
'use client';
import { Button } from 'react-basics'; import { Button } from 'react-basics';
import Head from 'next/head'; import Head from 'next/head';
import Link from 'next/link'; import Link from 'next/link';

View File

@ -1,5 +1,5 @@
import TestConsole from '../TestConsole';
import { Metadata } from 'next'; import { Metadata } from 'next';
import ConsolePage from '../ConsolePage';
async function getEnabled() { async function getEnabled() {
return !!process.env.ENABLE_TEST_CONSOLE; return !!process.env.ENABLE_TEST_CONSOLE;
@ -12,7 +12,7 @@ export default async function ({ params: { websiteId } }) {
return null; return null;
} }
return <TestConsole websiteId={websiteId?.[0]} />; return <ConsolePage websiteId={websiteId?.[0]} />;
} }
export const metadata: Metadata = { export const metadata: Metadata = {

View File

@ -1,4 +1,3 @@
'use client';
import { useState, useMemo } from 'react'; import { useState, useMemo } from 'react';
import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd';
import classNames from 'classnames'; import classNames from 'classnames';

View File

@ -10,7 +10,7 @@ import { useMessages, useLocale, useTeamUrl, useWebsites } from 'components/hook
import useDashboard from 'store/dashboard'; import useDashboard from 'store/dashboard';
import LinkButton from 'components/common/LinkButton'; import LinkButton from 'components/common/LinkButton';
export function Dashboard() { export function DashboardPage() {
const { formatMessage, labels, messages } = useMessages(); const { formatMessage, labels, messages } = useMessages();
const { teamId, renderTeamUrl } = useTeamUrl(); const { teamId, renderTeamUrl } = useTeamUrl();
const { showCharts, editing } = useDashboard(); const { showCharts, editing } = useDashboard();
@ -68,4 +68,4 @@ export function Dashboard() {
); );
} }
export default Dashboard; export default DashboardPage;

View File

@ -1,4 +1,3 @@
'use client';
import { TooltipPopup, Icon, Text, Flexbox, Button } from 'react-basics'; import { TooltipPopup, Icon, Text, Flexbox, Button } from 'react-basics';
import Icons from 'components/icons'; import Icons from 'components/icons';
import { saveDashboard } from 'store/dashboard'; import { saveDashboard } from 'store/dashboard';

View File

@ -1,8 +1,8 @@
import Dashboard from 'app/(main)/dashboard/Dashboard'; import DashboardPage from './DashboardPage';
import { Metadata } from 'next'; import { Metadata } from 'next';
export default function () { export default function () {
return <Dashboard />; return <DashboardPage />;
} }
export const metadata: Metadata = { export const metadata: Metadata = {

View File

@ -19,3 +19,15 @@
height: calc(100vh - 60px); height: calc(100vh - 60px);
overflow-y: auto; overflow-y: auto;
} }
.content {
flex: 1;
display: flex;
flex-direction: column;
position: relative;
width: 100%;
max-width: 1320px;
margin: 0 auto;
padding: 0 20px;
min-height: calc(100vh - 60px);
}

View File

@ -1,3 +1,4 @@
'use client';
import App from './App'; import App from './App';
import NavBar from './NavBar'; import NavBar from './NavBar';
import Page from 'components/layout/Page'; import Page from 'components/layout/Page';

View File

@ -1,4 +1,3 @@
'use client';
import DateFilter from 'components/input/DateFilter'; import DateFilter from 'components/input/DateFilter';
import { Button, Flexbox } from 'react-basics'; import { Button, Flexbox } from 'react-basics';
import { useDateRange, useMessages } from 'components/hooks'; import { useDateRange, useMessages } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import { useState } from 'react'; import { useState } from 'react';
import { Button, Dropdown, Item, Flexbox } from 'react-basics'; import { Button, Dropdown, Item, Flexbox } from 'react-basics';
import { useLocale, useMessages } from 'components/hooks'; import { useLocale, useMessages } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import { Button, Icon, Text, useToasts, ModalTrigger, Modal } from 'react-basics'; import { Button, Icon, Text, useToasts, ModalTrigger, Modal } from 'react-basics';
import PasswordEditForm from 'app/(main)/profile/PasswordEditForm'; import PasswordEditForm from 'app/(main)/profile/PasswordEditForm';
import Icons from 'components/icons'; import Icons from 'components/icons';

View File

@ -1,4 +1,3 @@
'use client';
import { useRef } from 'react'; import { useRef } from 'react';
import { Form, FormRow, FormInput, FormButtons, PasswordField, Button } from 'react-basics'; import { Form, FormRow, FormInput, FormButtons, PasswordField, Button } from 'react-basics';
import { useApi, useMessages } from 'components/hooks'; import { useApi, useMessages } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import PageHeader from 'components/layout/PageHeader'; import PageHeader from 'components/layout/PageHeader';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';

View File

@ -0,0 +1,13 @@
'use client';
import ProfileHeader from './ProfileHeader';
import ProfileSettings from './ProfileSettings';
import styles from './ProfilePage.module.css';
export default function () {
return (
<div className={styles.container}>
<ProfileHeader />
<ProfileSettings />
</div>
);
}

View File

@ -1,4 +1,3 @@
'use client';
import { Form, FormRow } from 'react-basics'; import { Form, FormRow } from 'react-basics';
import TimezoneSetting from 'app/(main)/profile/TimezoneSetting'; import TimezoneSetting from 'app/(main)/profile/TimezoneSetting';
import DateRangeSetting from 'app/(main)/profile/DateRangeSetting'; import DateRangeSetting from 'app/(main)/profile/DateRangeSetting';
@ -11,7 +10,7 @@ import { ROLES } from 'lib/constants';
export function ProfileSettings() { export function ProfileSettings() {
const { user } = useLogin(); const { user } = useLogin();
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const cloudMode = Boolean(process.env.cloudMode); const cloudMode = !!process.env.cloudMode;
if (!user) { if (!user) {
return null; return null;
@ -24,7 +23,7 @@ export function ProfileSettings() {
return formatMessage(labels.user); return formatMessage(labels.user);
} }
if (value === ROLES.admin) { if (value === ROLES.admin) {
return formatMessage(labels.admin); return formatMessage(labels.administrator);
} }
if (value === ROLES.viewOnly) { if (value === ROLES.viewOnly) {
return formatMessage(labels.viewOnly); return formatMessage(labels.viewOnly);

View File

@ -1,4 +1,3 @@
'use client';
import classNames from 'classnames'; import classNames from 'classnames';
import { Button, Icon } from 'react-basics'; import { Button, Icon } from 'react-basics';
import { useTheme } from 'components/hooks'; import { useTheme } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import { useState } from 'react'; import { useState } from 'react';
import { Dropdown, Item, Button, Flexbox } from 'react-basics'; import { Dropdown, Item, Button, Flexbox } from 'react-basics';
import { listTimeZones } from 'timezone-support'; import { listTimeZones } from 'timezone-support';

View File

@ -1,15 +1,8 @@
import ProfileHeader from './ProfileHeader';
import ProfileSettings from './ProfileSettings';
import { Metadata } from 'next'; import { Metadata } from 'next';
import styles from './page.module.css'; import ProfilePage from './ProfilePage';
export default function () { export default function () {
return ( return <ProfilePage />;
<div className={styles.container}>
<ProfileHeader />
<ProfileSettings />
</div>
);
} }
export const metadata: Metadata = { export const metadata: Metadata = {

View File

@ -1,4 +1,3 @@
'use client';
import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics'; import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
import { useApi, useMessages } from 'components/hooks'; import { useApi, useMessages } from 'components/hooks';
import { touch } from 'store/modified'; import { touch } from 'store/modified';

View File

@ -1,4 +1,3 @@
'use client';
import { useReports } from 'components/hooks'; import { useReports } from 'components/hooks';
import ReportsTable from './ReportsTable'; import ReportsTable from './ReportsTable';
import DataTable from 'components/common/DataTable'; import DataTable from 'components/common/DataTable';

View File

@ -1,4 +1,3 @@
'use client';
import PageHeader from 'components/layout/PageHeader'; import PageHeader from 'components/layout/PageHeader';
import { Icon, Icons, Text } from 'react-basics'; import { Icon, Icons, Text } from 'react-basics';
import { useMessages, useTeamUrl } from 'components/hooks'; import { useMessages, useTeamUrl } from 'components/hooks';

View File

@ -0,0 +1,15 @@
'use client';
import ReportsHeader from './ReportsHeader';
import ReportsDataTable from './ReportsDataTable';
export default function ReportsPage({ teamId }: { teamId: string }) {
return (
<>
<ReportsHeader />
<ReportsDataTable teamId={teamId} />
</>
);
}
export const metadata = {
title: 'Reports | Umami',
};

View File

@ -1,4 +1,3 @@
'use client';
import { GridColumn, GridTable, Icon, Icons, Text, useBreakpoint } from 'react-basics'; import { GridColumn, GridTable, Icon, Icons, Text, useBreakpoint } from 'react-basics';
import LinkButton from 'components/common/LinkButton'; import LinkButton from 'components/common/LinkButton';
import { useMessages, useLogin, useTeamUrl } from 'components/hooks'; import { useMessages, useLogin, useTeamUrl } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import { useContext } from 'react'; import { useContext } from 'react';
import { FormRow } from 'react-basics'; import { FormRow } from 'react-basics';
import { parseDateRange } from 'lib/date'; import { parseDateRange } from 'lib/date';

View File

@ -1,4 +1,3 @@
'use client';
import { useState } from 'react'; import { useState } from 'react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { REPORT_PARAMETERS } from 'lib/constants'; import { REPORT_PARAMETERS } from 'lib/constants';

View File

@ -1,4 +1,3 @@
'use client';
import { Form, FormRow, Menu, Item } from 'react-basics'; import { Form, FormRow, Menu, Item } from 'react-basics';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import { useState, useMemo } from 'react'; import { useState, useMemo } from 'react';
import { Form, FormRow, Item, Flexbox, Dropdown, Button } from 'react-basics'; import { Form, FormRow, Item, Flexbox, Dropdown, Button } from 'react-basics';
import { useMessages, useFilters, useFormat, useLocale } from 'components/hooks'; import { useMessages, useFilters, useFormat, useLocale } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import { Menu, Item, Form, FormRow } from 'react-basics'; import { Menu, Item, Form, FormRow } from 'react-basics';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';
import styles from './FieldSelectForm.module.css'; import styles from './FieldSelectForm.module.css';

View File

@ -1,4 +1,3 @@
'use client';
import { useState } from 'react'; import { useState } from 'react';
import { Loading } from 'react-basics'; import { Loading } from 'react-basics';
import { subDays } from 'date-fns'; import { subDays } from 'date-fns';

View File

@ -1,4 +1,3 @@
'use client';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { Icon, TooltipPopup } from 'react-basics'; import { Icon, TooltipPopup } from 'react-basics';
import Icons from 'components/icons'; import Icons from 'components/icons';

View File

@ -1,4 +1,3 @@
'use client';
import { CSSProperties, ReactNode } from 'react'; import { CSSProperties, ReactNode } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import styles from './PopupForm.module.css'; import styles from './PopupForm.module.css';

View File

@ -1,20 +1,22 @@
'use client';
import { createContext, ReactNode } from 'react'; import { createContext, ReactNode } from 'react';
import { Loading } from 'react-basics'; import { Loading } from 'react-basics';
import classNames from 'classnames';
import { useReport } from 'components/hooks'; import { useReport } from 'components/hooks';
import styles from './Report.module.css'; import styles from './Report.module.css';
import classNames from 'classnames';
export const ReportContext = createContext(null); export const ReportContext = createContext(null);
export interface ReportProps { export function Report({
reportId,
defaultParameters,
children,
className,
}: {
reportId: string; reportId: string;
defaultParameters: { [key: string]: any }; defaultParameters: { [key: string]: any };
children: ReactNode; children: ReactNode;
className?: string; className?: string;
} }) {
export function Report({ reportId, defaultParameters, children, className }: ReportProps) {
const report = useReport(reportId, defaultParameters); const report = useReport(reportId, defaultParameters);
if (!report) { if (!report) {

View File

@ -1,4 +1,3 @@
'use client';
import styles from './ReportBody.module.css'; import styles from './ReportBody.module.css';
import { useContext } from 'react'; import { useContext } from 'react';
import { ReportContext } from './Report'; import { ReportContext } from './Report';

View File

@ -1,4 +1,3 @@
'use client';
import FunnelReport from '../funnel/FunnelReport'; import FunnelReport from '../funnel/FunnelReport';
import EventDataReport from '../event-data/EventDataReport'; import EventDataReport from '../event-data/EventDataReport';
import InsightsReport from '../insights/InsightsReport'; import InsightsReport from '../insights/InsightsReport';

View File

@ -1,4 +1,3 @@
'use client';
import { useContext } from 'react'; import { useContext } from 'react';
import { Icon, LoadingButton, InlineEditField, useToasts } from 'react-basics'; import { Icon, LoadingButton, InlineEditField, useToasts } from 'react-basics';
import { useMessages, useApi, useNavigation, useTeamUrl } from 'components/hooks'; import { useMessages, useApi, useNavigation, useTeamUrl } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import styles from './ReportMenu.module.css'; import styles from './ReportMenu.module.css';
import { useContext } from 'react'; import { useContext } from 'react';
import { ReportContext } from './Report'; import { ReportContext } from './Report';

View File

@ -0,0 +1,6 @@
'use client';
import ReportDetails from './ReportDetails';
export default function ReportPage({ reportId }) {
return <ReportDetails reportId={reportId} />;
}

View File

@ -1,12 +1,8 @@
import ReportDetails from './ReportDetails';
import { Metadata } from 'next'; import { Metadata } from 'next';
import ReportPage from './ReportPage';
export default function ReportDetailsPage({ params: { reportId } }) { export default function ({ params: { reportId } }) {
if (!reportId) { return <ReportPage reportId={reportId} />;
return null;
}
return <ReportDetails reportId={reportId} />;
} }
export const metadata: Metadata = { export const metadata: Metadata = {

View File

@ -0,0 +1,6 @@
'use client';
import ReportTemplates from './ReportTemplates';
export default function ReportCreatePage() {
return <ReportTemplates />;
}

View File

@ -1,4 +1,3 @@
'use client';
import Link from 'next/link'; import Link from 'next/link';
import { Button, Icons, Text, Icon } from 'react-basics'; import { Button, Icons, Text, Icon } from 'react-basics';
import PageHeader from 'components/layout/PageHeader'; import PageHeader from 'components/layout/PageHeader';
@ -8,30 +7,6 @@ import Magnet from 'assets/magnet.svg';
import styles from './ReportTemplates.module.css'; import styles from './ReportTemplates.module.css';
import { useMessages, useTeamUrl } from 'components/hooks'; import { useMessages, useTeamUrl } from 'components/hooks';
function ReportItem({ title, description, url, icon }) {
const { formatMessage, labels } = useMessages();
return (
<div className={styles.report}>
<div className={styles.title}>
<Icon size="lg">{icon}</Icon>
<Text>{title}</Text>
</div>
<div className={styles.description}>{description}</div>
<div className={styles.buttons}>
<Link href={url}>
<Button variant="primary">
<Icon>
<Icons.Plus />
</Icon>
<Text>{formatMessage(labels.create)}</Text>
</Button>
</Link>
</div>
</div>
);
}
export function ReportTemplates({ showHeader = true }: { showHeader?: boolean }) { export function ReportTemplates({ showHeader = true }: { showHeader?: boolean }) {
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { renderTeamUrl } = useTeamUrl(); const { renderTeamUrl } = useTeamUrl();
@ -71,4 +46,28 @@ export function ReportTemplates({ showHeader = true }: { showHeader?: boolean })
); );
} }
function ReportItem({ title, description, url, icon }) {
const { formatMessage, labels } = useMessages();
return (
<div className={styles.report}>
<div className={styles.title}>
<Icon size="lg">{icon}</Icon>
<Text>{title}</Text>
</div>
<div className={styles.description}>{description}</div>
<div className={styles.buttons}>
<Link href={url}>
<Button variant="primary">
<Icon>
<Icons.Plus />
</Icon>
<Text>{formatMessage(labels.create)}</Text>
</Button>
</Link>
</div>
</div>
);
}
export default ReportTemplates; export default ReportTemplates;

View File

@ -1,8 +1,8 @@
import ReportTemplates from './ReportTemplates'; import ReportCreatePage from './ReportCreatePage';
import { Metadata } from 'next'; import { Metadata } from 'next';
export default function ReportsCreatePage() { export default function () {
return <ReportTemplates />; return <ReportCreatePage />;
} }
export const metadata: Metadata = { export const metadata: Metadata = {

View File

@ -1,4 +1,3 @@
'use client';
import { useContext } from 'react'; import { useContext } from 'react';
import { Form, FormRow, FormButtons, SubmitButton, PopupTrigger, Icon, Popup } from 'react-basics'; import { Form, FormRow, FormButtons, SubmitButton, PopupTrigger, Icon, Popup } from 'react-basics';
import Empty from 'components/common/Empty'; import Empty from 'components/common/Empty';

View File

@ -1,4 +1,3 @@
'use client';
import Report from '../[reportId]/Report'; import Report from '../[reportId]/Report';
import ReportHeader from '../[reportId]/ReportHeader'; import ReportHeader from '../[reportId]/ReportHeader';
import ReportMenu from '../[reportId]/ReportMenu'; import ReportMenu from '../[reportId]/ReportMenu';

View File

@ -0,0 +1,6 @@
'use client';
import EventDataReport from './EventDataReport';
export default function EventDataReportPage() {
return <EventDataReport />;
}

View File

@ -1,4 +1,3 @@
'use client';
import { useContext } from 'react'; import { useContext } from 'react';
import { GridTable, GridColumn } from 'react-basics'; import { GridTable, GridColumn } from 'react-basics';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';

View File

@ -1,8 +1,8 @@
import { Metadata } from 'next'; import { Metadata } from 'next';
import EventDataReport from './EventDataReport'; import EventDataReportPage from './EventDataReportPage';
export default function EventDataReportPage() { export default function () {
return <EventDataReport />; return <EventDataReportPage />;
} }
export const metadata: Metadata = { export const metadata: Metadata = {

View File

@ -1,4 +1,3 @@
'use client';
import { JSX, useCallback, useContext, useMemo } from 'react'; import { JSX, useCallback, useContext, useMemo } from 'react';
import { Loading, StatusLight } from 'react-basics'; import { Loading, StatusLight } from 'react-basics';
import { useMessages, useTheme } from 'components/hooks'; import { useMessages, useTheme } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import { useContext } from 'react'; import { useContext } from 'react';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';
import { import {

View File

@ -1,4 +1,3 @@
'use client';
import FunnelChart from './FunnelChart'; import FunnelChart from './FunnelChart';
import FunnelTable from './FunnelTable'; import FunnelTable from './FunnelTable';
import FunnelParameters from './FunnelParameters'; import FunnelParameters from './FunnelParameters';

View File

@ -0,0 +1,6 @@
'use client';
import FunnelReport from './FunnelReport';
export default function FunnelReportPage() {
return <FunnelReport />;
}

View File

@ -1,4 +1,3 @@
'use client';
import { useContext } from 'react'; import { useContext } from 'react';
import ListTable from 'components/metrics/ListTable'; import ListTable from 'components/metrics/ListTable';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import { useState } from 'react'; import { useState } from 'react';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';
import { Button, Form, FormRow, TextField, Flexbox } from 'react-basics'; import { Button, Form, FormRow, TextField, Flexbox } from 'react-basics';

View File

@ -1,8 +1,8 @@
import FunnelReport from './FunnelReport'; import FunnelReportPage from './FunnelReportPage';
import { Metadata } from 'next'; import { Metadata } from 'next';
export default function FunnelReportPage() { export default function () {
return <FunnelReport />; return <FunnelReportPage />;
} }
export const metadata: Metadata = { export const metadata: Metadata = {

View File

@ -1,4 +1,3 @@
'use client';
import { useContext } from 'react'; import { useContext } from 'react';
import { useFormat, useMessages, useFilters } from 'components/hooks'; import { useFormat, useMessages, useFilters } from 'components/hooks';
import { import {

View File

@ -1,4 +1,3 @@
'use client';
import Report from '../[reportId]/Report'; import Report from '../[reportId]/Report';
import ReportHeader from '../[reportId]/ReportHeader'; import ReportHeader from '../[reportId]/ReportHeader';
import ReportMenu from '../[reportId]/ReportMenu'; import ReportMenu from '../[reportId]/ReportMenu';

View File

@ -0,0 +1,6 @@
'use client';
import InsightsReport from './InsightsReport';
export default function InsightsReportPage() {
return <InsightsReport />;
}

View File

@ -1,4 +1,3 @@
'use client';
import { useContext, useEffect, useState } from 'react'; import { useContext, useEffect, useState } from 'react';
import { GridTable, GridColumn } from 'react-basics'; import { GridTable, GridColumn } from 'react-basics';
import { useFormat, useMessages } from 'components/hooks'; import { useFormat, useMessages } from 'components/hooks';

View File

@ -1,8 +1,8 @@
import InsightsReport from './InsightsReport'; import InsightsReportPage from './InsightsReportPage';
import { Metadata } from 'next'; import { Metadata } from 'next';
export default function InsightsReportPage() { export default function () {
return <InsightsReport />; return <InsightsReportPage />;
} }
export const metadata: Metadata = { export const metadata: Metadata = {

View File

@ -1,14 +1,10 @@
import ReportsHeader from './ReportsHeader'; import ReportsPage from './ReportsPage';
import ReportsDataTable from './ReportsDataTable'; import { Metadata } from 'next';
export default function ({ params: { teamId } }: { params: { teamId: string } }) { export default function ({ params: { teamId } }: { params: { teamId: string } }) {
return ( return <ReportsPage teamId={teamId} />;
<>
<ReportsHeader />
<ReportsDataTable teamId={teamId} />
</>
);
} }
export const metadata = {
export const metadata: Metadata = {
title: 'Reports | Umami', title: 'Reports | Umami',
}; };

View File

@ -1,4 +1,3 @@
'use client';
import { useContext } from 'react'; import { useContext } from 'react';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';
import { Form, FormButtons, FormRow, SubmitButton } from 'react-basics'; import { Form, FormButtons, FormRow, SubmitButton } from 'react-basics';

View File

@ -1,4 +1,3 @@
'use client';
import RetentionTable from './RetentionTable'; import RetentionTable from './RetentionTable';
import RetentionParameters from './RetentionParameters'; import RetentionParameters from './RetentionParameters';
import Report from '../[reportId]/Report'; import Report from '../[reportId]/Report';

View File

@ -0,0 +1,6 @@
'use client';
import RetentionReport from './RetentionReport';
export default function RetentionReportPage() {
return <RetentionReport />;
}

View File

@ -1,4 +1,3 @@
'use client';
import { useContext } from 'react'; import { useContext } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { ReportContext } from '../[reportId]/Report'; import { ReportContext } from '../[reportId]/Report';

View File

@ -1,8 +1,8 @@
import { Metadata } from 'next'; import { Metadata } from 'next';
import RetentionReport from './RetentionReport'; import RetentionReportPage from './RetentionReportPage';
export default function RetentionReportPage() { export default function () {
return <RetentionReport />; return <RetentionReportPage />;
} }
export const metadata: Metadata = { export const metadata: Metadata = {

View File

@ -1,9 +1,9 @@
'use client'; 'use client';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { useLogin, useMessages } from 'components/hooks'; import { useLogin, useMessages } from 'components/hooks';
import SettingsLayout from 'components/layout/SettingsLayout'; import MenuLayout from 'components/layout/MenuLayout';
export default function Settings({ children }: { children: ReactNode }) { export default function SettingsLayout({ children }: { children: ReactNode }) {
const { user } = useLogin(); const { user } = useLogin();
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
@ -21,5 +21,5 @@ export default function Settings({ children }: { children: ReactNode }) {
}, },
].filter(n => n); ].filter(n => n);
return <SettingsLayout items={items}>{children}</SettingsLayout>; return <MenuLayout items={items}>{children}</MenuLayout>;
} }

View File

@ -1,5 +1,5 @@
import Settings from './Settings'; import SettingsLayout from './SettingsLayout';
export default function ({ children }) { export default function ({ children }) {
return <Settings>{children}</Settings>; return <SettingsLayout>{children}</SettingsLayout>;
} }

View File

@ -1,4 +1,3 @@
'use client';
import { import {
Form, Form,
FormRow, FormRow,

View File

@ -1,4 +1,3 @@
'use client';
import { useRef } from 'react'; import { useRef } from 'react';
import { import {
Form, Form,

View File

@ -1,4 +1,3 @@
'use client';
import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics'; import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics';
import { useMessages, useLocale, useLogin } from 'components/hooks'; import { useMessages, useLocale, useLogin } from 'components/hooks';
import TeamDeleteForm from './TeamLeaveForm'; import TeamDeleteForm from './TeamLeaveForm';

View File

@ -1,4 +1,3 @@
'use client';
import { useApi, useMessages } from 'components/hooks'; import { useApi, useMessages } from 'components/hooks';
import { touch } from 'store/modified'; import { touch } from 'store/modified';
import ConfirmationForm from 'components/common/ConfirmationForm'; import ConfirmationForm from 'components/common/ConfirmationForm';

View File

@ -1,4 +1,3 @@
'use client';
import { Button, Icon, Modal, ModalTrigger, Text } from 'react-basics'; import { Button, Icon, Modal, ModalTrigger, Text } from 'react-basics';
import Icons from 'components/icons'; import Icons from 'components/icons';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import DataTable from 'components/common/DataTable'; import DataTable from 'components/common/DataTable';
import TeamsTable from 'app/(main)/settings/teams/TeamsTable'; import TeamsTable from 'app/(main)/settings/teams/TeamsTable';
import { useTeams } from 'components/hooks'; import { useTeams } from 'components/hooks';

View File

@ -1,4 +1,3 @@
'use client';
import { Flexbox } from 'react-basics'; import { Flexbox } from 'react-basics';
import PageHeader from 'components/layout/PageHeader'; import PageHeader from 'components/layout/PageHeader';
import { ROLES } from 'lib/constants'; import { ROLES } from 'lib/constants';

View File

@ -1,4 +1,3 @@
'use client';
import { Button, Icon, Modal, ModalTrigger, Text, useToasts } from 'react-basics'; import { Button, Icon, Modal, ModalTrigger, Text, useToasts } from 'react-basics';
import Icons from 'components/icons'; import Icons from 'components/icons';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';

View File

@ -0,0 +1,12 @@
'use client';
import TeamsDataTable from './TeamsDataTable';
import TeamsHeader from './TeamsHeader';
export default function TeamsPage() {
return (
<>
<TeamsHeader />
<TeamsDataTable />
</>
);
}

View File

@ -1,13 +1,11 @@
'use client';
import { GridColumn, GridTable, Icon, Text, useBreakpoint } from 'react-basics'; import { GridColumn, GridTable, Icon, Text, useBreakpoint } from 'react-basics';
import { useMessages, useLogin } from 'components/hooks'; import { useMessages } from 'components/hooks';
import Icons from 'components/icons'; import Icons from 'components/icons';
import { ROLES } from 'lib/constants'; import { ROLES } from 'lib/constants';
import LinkButton from 'components/common/LinkButton'; import LinkButton from 'components/common/LinkButton';
export function TeamsTable({ export function TeamsTable({
data = [], data = [],
allowEdit = false,
showActions = true, showActions = true,
}: { }: {
data: any[]; data: any[];
@ -15,7 +13,6 @@ export function TeamsTable({
showActions?: boolean; showActions?: boolean;
}) { }) {
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { user } = useLogin();
const breakpoint = useBreakpoint(); const breakpoint = useBreakpoint();
return ( return (
@ -30,34 +27,22 @@ export function TeamsTable({
<GridColumn name="members" label={formatMessage(labels.members)}> <GridColumn name="members" label={formatMessage(labels.members)}>
{row => row._count.teamUser} {row => row._count.teamUser}
</GridColumn> </GridColumn>
<GridColumn name="action" label=" " alignment="end"> {showActions && (
{row => { <GridColumn name="action" label=" " alignment="end">
const { id, teamUser } = row; {row => {
const owner = teamUser.find(({ role }) => role === ROLES.teamOwner); const { id } = row;
const isOwner = user.id === owner?.userId;
return ( return (
showActions && ( <LinkButton href={`/teams/${id}/settings`}>
<> <Icon>
{allowEdit && isOwner && ( <Icons.ArrowRight />
<LinkButton href={`/teams/${id}/settings`}> </Icon>
<Icon> <Text>{formatMessage(labels.view)}</Text>
<Icons.Edit /> </LinkButton>
</Icon> );
<Text>{formatMessage(labels.edit)}</Text> }}
</LinkButton> </GridColumn>
)} )}
<LinkButton href={`/teams/${id}`}>
<Icon>
<Icons.Change />
</Icon>
<Text>{formatMessage(labels.switch)}</Text>
</LinkButton>
</>
)
);
}}
</GridColumn>
</GridTable> </GridTable>
); );
} }

View File

@ -1,4 +1,3 @@
'use client';
import { Button, Icon, Icons, Text } from 'react-basics'; import { Button, Icon, Icons, Text } from 'react-basics';
import styles from './WebsiteTags.module.css'; import styles from './WebsiteTags.module.css';

View File

@ -1,4 +1,3 @@
'use client';
import { useApi, useMessages } from 'components/hooks'; import { useApi, useMessages } from 'components/hooks';
import { Icon, Icons, LoadingButton, Text } from 'react-basics'; import { Icon, Icons, LoadingButton, Text } from 'react-basics';
import { touch } from 'store/modified'; import { touch } from 'store/modified';

View File

@ -1,9 +1,14 @@
'use client';
import DataTable from 'components/common/DataTable'; import DataTable from 'components/common/DataTable';
import TeamMembersTable from './TeamMembersTable'; import TeamMembersTable from './TeamMembersTable';
import { useTeamMembers } from 'components/hooks'; import { useTeamMembers } from 'components/hooks';
export function TeamMembers({ teamId, allowEdit }: { teamId: string; allowEdit: boolean }) { export function TeamMembersDataTable({
teamId,
allowEdit = false,
}: {
teamId: string;
allowEdit?: boolean;
}) {
const queryResult = useTeamMembers(teamId); const queryResult = useTeamMembers(teamId);
return ( return (
@ -13,4 +18,4 @@ export function TeamMembers({ teamId, allowEdit }: { teamId: string; allowEdit:
); );
} }
export default TeamMembers; export default TeamMembersDataTable;

View File

@ -0,0 +1,18 @@
'use client';
import TeamProvider from 'app/(main)/teams/[teamId]/TeamProvider';
import TeamMembersDataTable from './TeamMembersDataTable';
import PageHeader from 'components/layout/PageHeader';
import { useMessages } from 'components/hooks';
export function TeamMembersPage({ teamId }: { teamId: string }) {
const { formatMessage, labels } = useMessages();
return (
<TeamProvider teamId={teamId}>
<PageHeader title={formatMessage(labels.members)} />
<TeamMembersDataTable teamId={teamId} allowEdit={true} />
</TeamProvider>
);
}
export default TeamMembersPage;

View File

@ -1,4 +1,3 @@
'use client';
import { GridColumn, GridTable, useBreakpoint } from 'react-basics'; import { GridColumn, GridTable, useBreakpoint } from 'react-basics';
import { useMessages, useLogin } from 'components/hooks'; import { useMessages, useLogin } from 'components/hooks';
import { ROLES } from 'lib/constants'; import { ROLES } from 'lib/constants';

View File

@ -0,0 +1,10 @@
import TeamMembersPage from './TeamMembersPage';
import { Metadata } from 'next';
export default function ({ params: { teamId } }) {
return <TeamMembersPage teamId={teamId} />;
}
export const metadata: Metadata = {
title: 'Team members - Umami',
};

View File

@ -1,10 +0,0 @@
import TeamSettings from './TeamSettings';
import TeamProvider from 'app/(main)/teams/[teamId]/TeamProvider';
export default function ({ params: { teamId } }) {
return (
<TeamProvider teamId={teamId}>
<TeamSettings teamId={teamId} />
</TeamProvider>
);
}

View File

@ -1,9 +1,8 @@
'use client';
import { ActionForm, Button, Modal, ModalTrigger } from 'react-basics'; import { ActionForm, Button, Modal, ModalTrigger } from 'react-basics';
import { useMessages } from 'components/hooks'; import { useMessages } from 'components/hooks';
import TeamDeleteForm from '../TeamDeleteForm'; import TeamDeleteForm from './TeamDeleteForm';
export function TeamData({ teamId }: { teamId: string }) { export function TeamAdmin({ teamId }: { teamId: string }) {
const { formatMessage, labels, messages } = useMessages(); const { formatMessage, labels, messages } = useMessages();
return ( return (
@ -21,4 +20,4 @@ export function TeamData({ teamId }: { teamId: string }) {
); );
} }
export default TeamData; export default TeamAdmin;

View File

@ -1,4 +1,3 @@
'use client';
import { useApi, useMessages } from 'components/hooks'; import { useApi, useMessages } from 'components/hooks';
import { touch } from 'store/modified'; import { touch } from 'store/modified';
import TypeConfirmationForm from 'components/common/TypeConfirmationForm'; import TypeConfirmationForm from 'components/common/TypeConfirmationForm';

View File

@ -1,4 +1,3 @@
'use client';
import { useContext, useState } from 'react'; import { useContext, useState } from 'react';
import { Item, Tabs, Flexbox, Text, Icon } from 'react-basics'; import { Item, Tabs, Flexbox, Text, Icon } from 'react-basics';
import PageHeader from 'components/layout/PageHeader'; import PageHeader from 'components/layout/PageHeader';
@ -6,13 +5,11 @@ import { ROLES } from 'lib/constants';
import Icons from 'components/icons'; import Icons from 'components/icons';
import { useLogin, useMessages } from 'components/hooks'; import { useLogin, useMessages } from 'components/hooks';
import TeamEditForm from './TeamEditForm'; import TeamEditForm from './TeamEditForm';
import TeamMembers from './TeamMembers'; import TeamAdmin from './TeamAdmin';
import TeamWebsites from './TeamWebsites';
import TeamData from './TeamData';
import LinkButton from 'components/common/LinkButton'; import LinkButton from 'components/common/LinkButton';
import { TeamContext } from 'app/(main)/teams/[teamId]/TeamProvider'; import { TeamContext } from 'app/(main)/teams/[teamId]/TeamProvider';
export function TeamSettings({ teamId }: { teamId: string }) { export function TeamDetails({ teamId }: { teamId: string }) {
const team = useContext(TeamContext); const team = useContext(TeamContext);
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const { user } = useLogin(); const { user } = useLogin();
@ -25,25 +22,23 @@ export function TeamSettings({ teamId }: { teamId: string }) {
return ( return (
<Flexbox direction="column"> <Flexbox direction="column">
<PageHeader title={team?.name} icon={<Icons.Users />}> <PageHeader title={team?.name} icon={<Icons.Users />}>
<LinkButton href={`/teams/${teamId}`} variant="primary"> {!canEdit && (
<Icon> <LinkButton href={`/teams/${teamId}`}>
<Icons.Change /> <Icon>
</Icon> <Icons.Logout />
<Text>{formatMessage(labels.view)}</Text> </Icon>
</LinkButton> <Text>{formatMessage(labels.leaveTeam)}</Text>
</LinkButton>
)}
</PageHeader> </PageHeader>
<Tabs selectedKey={tab} onSelect={(value: any) => setTab(value)} style={{ marginBottom: 30 }}> <Tabs selectedKey={tab} onSelect={(value: any) => setTab(value)} style={{ marginBottom: 30 }}>
<Item key="details">{formatMessage(labels.details)}</Item> <Item key="details">{formatMessage(labels.details)}</Item>
<Item key="members">{formatMessage(labels.members)}</Item> <Item key="admin">{formatMessage(labels.admin)}</Item>
<Item key="websites">{formatMessage(labels.websites)}</Item>
<Item key="data">{formatMessage(labels.data)}</Item>
</Tabs> </Tabs>
{tab === 'details' && <TeamEditForm teamId={teamId} allowEdit={canEdit} />} {tab === 'details' && <TeamEditForm teamId={teamId} allowEdit={canEdit} />}
{tab === 'members' && <TeamMembers teamId={teamId} allowEdit={canEdit} />} {canEdit && tab === 'admin' && <TeamAdmin teamId={teamId} />}
{tab === 'websites' && <TeamWebsites teamId={teamId} allowEdit={canEdit} />}
{canEdit && tab === 'data' && <TeamData teamId={teamId} />}
</Flexbox> </Flexbox>
); );
} }
export default TeamSettings; export default TeamDetails;

View File

@ -1,4 +1,3 @@
'use client';
import { import {
SubmitButton, SubmitButton,
Form, Form,

View File

@ -0,0 +1,13 @@
'use client';
import TeamProvider from 'app/(main)/teams/[teamId]/TeamProvider';
import TeamDetails from './TeamDetails';
export function TeamMembersPage({ teamId }: { teamId: string }) {
return (
<TeamProvider teamId={teamId}>
<TeamDetails teamId={teamId} />
</TeamProvider>
);
}
export default TeamMembersPage;

View File

@ -0,0 +1,10 @@
import { Metadata } from 'next';
import TeamPage from './TeamPage';
export default function ({ params: { teamId } }) {
return <TeamPage teamId={teamId} />;
}
export const metadata: Metadata = {
title: 'Teams Settings - Umami',
};

View File

@ -1,4 +1,3 @@
'use client';
import { useApi, useMessages } from 'components/hooks'; import { useApi, useMessages } from 'components/hooks';
import { Icon, Icons, LoadingButton, Text } from 'react-basics'; import { Icon, Icons, LoadingButton, Text } from 'react-basics';

View File

@ -1,9 +1,14 @@
'use client';
import DataTable from 'components/common/DataTable'; import DataTable from 'components/common/DataTable';
import { useTeamWebsites } from 'components/hooks'; import { useTeamWebsites } from 'components/hooks';
import TeamWebsitesTable from './TeamWebsitesTable'; import TeamWebsitesTable from './TeamWebsitesTable';
export function TeamWebsites({ teamId, allowEdit }: { teamId: string; allowEdit: boolean }) { export function TeamWebsitesDataTable({
teamId,
allowEdit = false,
}: {
teamId: string;
allowEdit?: boolean;
}) {
const queryResult = useTeamWebsites(teamId); const queryResult = useTeamWebsites(teamId);
return ( return (
@ -13,4 +18,4 @@ export function TeamWebsites({ teamId, allowEdit }: { teamId: string; allowEdit:
); );
} }
export default TeamWebsites; export default TeamWebsitesDataTable;

View File

@ -0,0 +1,18 @@
'use client';
import TeamProvider from 'app/(main)/teams/[teamId]/TeamProvider';
import TeamWebsitesDataTable from './TeamWebsitesDataTable';
import PageHeader from 'components/layout/PageHeader';
import { useMessages } from 'components/hooks';
export function TeamWebsitesPage({ teamId }: { teamId: string }) {
const { formatMessage, labels } = useMessages();
return (
<TeamProvider teamId={teamId}>
<PageHeader title={formatMessage(labels.websites)} />
<TeamWebsitesDataTable teamId={teamId} allowEdit={true} />
</TeamProvider>
);
}
export default TeamWebsitesPage;

View File

@ -1,4 +1,3 @@
'use client';
import { GridColumn, GridTable, Icon, Text } from 'react-basics'; import { GridColumn, GridTable, Icon, Text } from 'react-basics';
import { useLogin, useMessages } from 'components/hooks'; import { useLogin, useMessages } from 'components/hooks';
import Icons from 'components/icons'; import Icons from 'components/icons';
@ -35,7 +34,7 @@ export function TeamWebsitesTable({
)} )}
<LinkButton href={`/teams/${teamId}/websites/${websiteId}`}> <LinkButton href={`/teams/${teamId}/websites/${websiteId}`}>
<Icon> <Icon>
<Icons.Change /> <Icons.ArrowRight />
</Icon> </Icon>
<Text>{formatMessage(labels.view)}</Text> <Text>{formatMessage(labels.view)}</Text>
</LinkButton> </LinkButton>

Some files were not shown because too many files have changed in this diff Show More