Merge branch 'dev' of https://github.com/umami-software/umami into dev
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { Column } from '@umami/react-zen';
|
import { Column } from '@umami/react-zen';
|
||||||
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
|
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
|
||||||
import { useDateRange } from '@/components/hooks';
|
import { useDateRange, useTimezone } from '@/components/hooks';
|
||||||
import { Revenue } from './Revenue';
|
import { Revenue } from './Revenue';
|
||||||
|
|
||||||
export function RevenuePage({ websiteId }: { websiteId: string }) {
|
export function RevenuePage({ websiteId }: { websiteId: string }) {
|
||||||
|
const { timezone } = useTimezone();
|
||||||
const {
|
const {
|
||||||
dateRange: { startDate, endDate, unit },
|
dateRange: { startDate, endDate, unit },
|
||||||
} = useDateRange();
|
} = useDateRange({ timezone });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column gap>
|
<Column gap>
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ export interface SessionModalProps extends ModalProps {
|
|||||||
export function SessionModal({ websiteId, ...props }: SessionModalProps) {
|
export function SessionModal({ websiteId, ...props }: SessionModalProps) {
|
||||||
const {
|
const {
|
||||||
router,
|
router,
|
||||||
|
pathname,
|
||||||
query: { session },
|
query: { session },
|
||||||
updateParams,
|
updateParams,
|
||||||
} = useNavigation();
|
} = useNavigation();
|
||||||
|
const isSharePage = pathname.includes('/share/');
|
||||||
const handleOpenChange = (isOpen: boolean) => {
|
const handleOpenChange = (isOpen: boolean) => {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
router.push(updateParams({ session: undefined }));
|
router.push(updateParams({ session: undefined }));
|
||||||
@@ -32,7 +34,7 @@ export function SessionModal({ websiteId, ...props }: SessionModalProps) {
|
|||||||
<Dialog variant="sheet" className="rounded-lg">
|
<Dialog variant="sheet" className="rounded-lg">
|
||||||
{({ close }) => (
|
{({ close }) => (
|
||||||
<Column padding="10">
|
<Column padding="10">
|
||||||
<SessionProfile websiteId={websiteId} sessionId={session} onClose={() => close()} />
|
<SessionProfile websiteId={websiteId} sessionId={session} showReplays={!isSharePage} onClose={() => close()} />
|
||||||
</Column>
|
</Column>
|
||||||
)}
|
)}
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|||||||
@@ -23,10 +23,12 @@ import { SessionStats } from './SessionStats';
|
|||||||
export function SessionProfile({
|
export function SessionProfile({
|
||||||
websiteId,
|
websiteId,
|
||||||
sessionId,
|
sessionId,
|
||||||
|
showReplays = true,
|
||||||
onClose,
|
onClose,
|
||||||
}: {
|
}: {
|
||||||
websiteId: string;
|
websiteId: string;
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
|
showReplays?: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { data, isLoading, error } = useWebsiteSessionQuery(websiteId, sessionId);
|
const { data, isLoading, error } = useWebsiteSessionQuery(websiteId, sessionId);
|
||||||
@@ -65,7 +67,7 @@ export function SessionProfile({
|
|||||||
<TabList>
|
<TabList>
|
||||||
<Tab id="activity">{t(labels.activity)}</Tab>
|
<Tab id="activity">{t(labels.activity)}</Tab>
|
||||||
<Tab id="properties">{t(labels.properties)}</Tab>
|
<Tab id="properties">{t(labels.properties)}</Tab>
|
||||||
<Tab id="replays">{t(labels.replays)}</Tab>
|
{showReplays && <Tab id="replays">{t(labels.replays)}</Tab>}
|
||||||
</TabList>
|
</TabList>
|
||||||
<TabPanel id="activity">
|
<TabPanel id="activity">
|
||||||
<SessionActivity
|
<SessionActivity
|
||||||
@@ -78,9 +80,11 @@ export function SessionProfile({
|
|||||||
<TabPanel id="properties">
|
<TabPanel id="properties">
|
||||||
<SessionData sessionId={sessionId} websiteId={websiteId} />
|
<SessionData sessionId={sessionId} websiteId={websiteId} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel id="replays">
|
{showReplays && (
|
||||||
<SessionReplaysDataTable websiteId={websiteId} sessionId={sessionId} />
|
<TabPanel id="replays">
|
||||||
</TabPanel>
|
<SessionReplaysDataTable websiteId={websiteId} sessionId={sessionId} />
|
||||||
|
</TabPanel>
|
||||||
|
)}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Column>
|
</Column>
|
||||||
</Column>
|
</Column>
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
import { DataGrid } from '@/components/common/DataGrid';
|
import { DataGrid } from '@/components/common/DataGrid';
|
||||||
import { useWebsiteSessionsQuery } from '@/components/hooks';
|
import { useNavigation, useWebsiteSessionsQuery } from '@/components/hooks';
|
||||||
import { SessionsTable } from './SessionsTable';
|
import { SessionsTable } from './SessionsTable';
|
||||||
|
|
||||||
export function SessionsDataTable({ websiteId }: { websiteId: string }) {
|
export function SessionsDataTable({ websiteId }: { websiteId: string }) {
|
||||||
const queryResult = useWebsiteSessionsQuery(websiteId);
|
const queryResult = useWebsiteSessionsQuery(websiteId);
|
||||||
|
const { updateParams } = useNavigation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataGrid query={queryResult} allowPaging allowSearch>
|
<DataGrid query={queryResult} allowPaging allowSearch>
|
||||||
{({ data }) => {
|
{({ data }) => (
|
||||||
return <SessionsTable data={data} websiteId={websiteId} />;
|
<SessionsTable
|
||||||
}}
|
data={data}
|
||||||
|
websiteId={websiteId}
|
||||||
|
getSessionHref={row => updateParams({ session: row.id })}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteContro
|
|||||||
import { Panel } from '@/components/common/Panel';
|
import { Panel } from '@/components/common/Panel';
|
||||||
import { useMessages } from '@/components/hooks';
|
import { useMessages } from '@/components/hooks';
|
||||||
import { getItem, setItem } from '@/lib/storage';
|
import { getItem, setItem } from '@/lib/storage';
|
||||||
|
import { SessionModal } from './SessionModal';
|
||||||
import { SessionProperties } from './SessionProperties';
|
import { SessionProperties } from './SessionProperties';
|
||||||
import { SessionsDataTable } from './SessionsDataTable';
|
import { SessionsDataTable } from './SessionsDataTable';
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ export function SessionsPage({ websiteId }) {
|
|||||||
return (
|
return (
|
||||||
<Column gap="3">
|
<Column gap="3">
|
||||||
<WebsiteControls websiteId={websiteId} />
|
<WebsiteControls websiteId={websiteId} />
|
||||||
|
<SessionModal websiteId={websiteId} />
|
||||||
<Panel>
|
<Panel>
|
||||||
<Tabs selectedKey={tab} onSelectionChange={handleSelect}>
|
<Tabs selectedKey={tab} onSelectionChange={handleSelect}>
|
||||||
<TabList>
|
<TabList>
|
||||||
|
|||||||
@@ -17,11 +17,7 @@ export function SessionsTable({
|
|||||||
<DataTable {...props}>
|
<DataTable {...props}>
|
||||||
<DataColumn id="id" label={t(labels.session)} width="100px">
|
<DataColumn id="id" label={t(labels.session)} width="100px">
|
||||||
{(row: any) => (
|
{(row: any) => (
|
||||||
<Link
|
<Link href={getSessionHref ? getSessionHref(row) : `/websites/${websiteId}/sessions/${row.id}`}>
|
||||||
href={
|
|
||||||
getSessionHref ? getSessionHref(row) : `/websites/${websiteId}/sessions/${row.id}`
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Avatar seed={row.id} size={32} />
|
<Avatar seed={row.id} size={32} />
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const ALL_SECTION_IDS = [
|
|||||||
'events',
|
'events',
|
||||||
'sessions',
|
'sessions',
|
||||||
'realtime',
|
'realtime',
|
||||||
|
'performance',
|
||||||
'compare',
|
'compare',
|
||||||
'breakdown',
|
'breakdown',
|
||||||
'goals',
|
'goals',
|
||||||
@@ -59,7 +60,7 @@ export function ShareProvider({ slug, children }: { slug: string; children: Reac
|
|||||||
const isWebsiteShare = share?.shareType === ENTITY_TYPE.website;
|
const isWebsiteShare = share?.shareType === ENTITY_TYPE.website;
|
||||||
|
|
||||||
const allowedSections = isWebsiteShare && share?.parameters
|
const allowedSections = isWebsiteShare && share?.parameters
|
||||||
? ALL_SECTION_IDS.filter(id => share.parameters[id] !== false)
|
? ALL_SECTION_IDS.filter(id => share.parameters[id] === true)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const shouldRedirect = isWebsiteShare &&
|
const shouldRedirect = isWebsiteShare &&
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { useShare } from '@/components/hooks';
|
|||||||
import { MobileMenuButton } from '@/components/input/MobileMenuButton';
|
import { MobileMenuButton } from '@/components/input/MobileMenuButton';
|
||||||
import { ENTITY_TYPE } from '@/lib/constants';
|
import { ENTITY_TYPE } from '@/lib/constants';
|
||||||
import { Column, Grid, Row, useTheme } from '@umami/react-zen';
|
import { Column, Grid, Row, useTheme } from '@umami/react-zen';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname, useRouter } from 'next/navigation';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { ShareFooter } from './ShareFooter';
|
import { ShareFooter } from './ShareFooter';
|
||||||
import { ShareNav } from './ShareNav';
|
import { ShareNav } from './ShareNav';
|
||||||
@@ -69,9 +69,10 @@ export function SharePage() {
|
|||||||
};
|
};
|
||||||
const share = useShare();
|
const share = useShare();
|
||||||
const { setTheme } = useTheme();
|
const { setTheme } = useTheme();
|
||||||
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const path = getSharePath(pathname);
|
const path = getSharePath(pathname);
|
||||||
const { websiteId, boardId, pixelId, linkId, parameters = {}, shareType } = share;
|
const { slug, websiteId, boardId, pixelId, linkId, parameters = {}, shareType } = share;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const url = new URL(window?.location?.href);
|
const url = new URL(window?.location?.href);
|
||||||
@@ -102,9 +103,10 @@ export function SharePage() {
|
|||||||
|
|
||||||
// Check if the requested path is allowed
|
// Check if the requested path is allowed
|
||||||
const pageKey = path || '';
|
const pageKey = path || '';
|
||||||
const isAllowed = pageKey === '' || pageKey === 'overview' || parameters[pageKey] !== false;
|
const isAllowed = pageKey === '' || parameters[pageKey] === true;
|
||||||
|
|
||||||
if (!isAllowed) {
|
if (!isAllowed) {
|
||||||
|
router.replace(`/share/${slug}`);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +127,7 @@ export function SharePage() {
|
|||||||
<PageBody gap>
|
<PageBody gap>
|
||||||
<WebsiteProvider websiteId={websiteId}>
|
<WebsiteProvider websiteId={websiteId}>
|
||||||
<Column>
|
<Column>
|
||||||
<WebsiteHeader showActions={false} />
|
<WebsiteHeader showActions={false} allowLink={false} />
|
||||||
<PageComponent websiteId={websiteId} />
|
<PageComponent websiteId={websiteId} />
|
||||||
</Column>
|
</Column>
|
||||||
</WebsiteProvider>
|
</WebsiteProvider>
|
||||||
|
|||||||
Reference in New Issue
Block a user