mirror of
https://github.com/umami-software/umami.git
synced 2026-02-04 04:37:11 +01:00
Redesigned share page.
This commit is contained in:
parent
9d3f5ad0fd
commit
78d467b478
9 changed files with 130 additions and 88 deletions
29
src/app/share/ShareProvider.tsx
Normal file
29
src/app/share/ShareProvider.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
'use client';
|
||||
import { Loading } from '@umami/react-zen';
|
||||
import { createContext, type ReactNode } from 'react';
|
||||
import { useShareTokenQuery } from '@/components/hooks';
|
||||
import type { WhiteLabel } from '@/lib/types';
|
||||
|
||||
export interface ShareData {
|
||||
shareId: string;
|
||||
websiteId: string;
|
||||
parameters: any;
|
||||
token: string;
|
||||
whiteLabel?: WhiteLabel;
|
||||
}
|
||||
|
||||
export const ShareContext = createContext<ShareData>(null);
|
||||
|
||||
export function ShareProvider({ shareId, children }: { shareId: string; children: ReactNode }) {
|
||||
const { share, isLoading, isFetching } = useShareTokenQuery(shareId);
|
||||
|
||||
if (isFetching && isLoading) {
|
||||
return <Loading placement="absolute" />;
|
||||
}
|
||||
|
||||
if (!share) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <ShareContext.Provider value={share}>{children}</ShareContext.Provider>;
|
||||
}
|
||||
|
|
@ -1,21 +1,20 @@
|
|||
'use client';
|
||||
import { Column } from '@umami/react-zen';
|
||||
import { Column, Icon, Row, Text, ThemeButton } from '@umami/react-zen';
|
||||
import { SideMenu } from '@/components/common/SideMenu';
|
||||
import { useMessages, useNavigation } from '@/components/hooks';
|
||||
import { useMessages, useNavigation, useShare } from '@/components/hooks';
|
||||
import { AlignEndHorizontal, Clock, Eye, Sheet, Tag, User } from '@/components/icons';
|
||||
import { Funnel, Lightning, Magnet, Money, Network, Path, Target } from '@/components/svg';
|
||||
import { LanguageButton } from '@/components/input/LanguageButton';
|
||||
import { PreferencesButton } from '@/components/input/PreferencesButton';
|
||||
import { Funnel, Lightning, Logo, Magnet, Money, Network, Path, Target } from '@/components/svg';
|
||||
|
||||
export function ShareNav({
|
||||
shareId,
|
||||
parameters,
|
||||
onItemClick,
|
||||
}: {
|
||||
shareId: string;
|
||||
parameters: Record<string, boolean>;
|
||||
onItemClick?: () => void;
|
||||
}) {
|
||||
export function ShareNav({ onItemClick }: { onItemClick?: () => void }) {
|
||||
const share = useShare();
|
||||
const { formatMessage, labels } = useMessages();
|
||||
const { pathname } = useNavigation();
|
||||
const { shareId, parameters, whiteLabel } = share;
|
||||
|
||||
const logoUrl = whiteLabel?.url || 'https://umami.is';
|
||||
const logoName = whiteLabel?.name || 'umami';
|
||||
const logoImage = whiteLabel?.image;
|
||||
|
||||
const renderPath = (path: string) => `/share/${shareId}${path}`;
|
||||
|
||||
|
|
@ -131,13 +130,36 @@ export function ShareNav({
|
|||
.find(({ path }) => path && pathname.endsWith(path.split('?')[0]))?.id;
|
||||
|
||||
return (
|
||||
<Column padding="3" position="sticky" top="0" gap>
|
||||
<SideMenu
|
||||
items={items}
|
||||
selectedKey={selectedKey}
|
||||
allowMinimize={false}
|
||||
onItemClick={onItemClick}
|
||||
/>
|
||||
<Column position="fixed" padding="3" width="240px" maxHeight="100vh" height="100vh">
|
||||
<Row as="header" gap alignItems="center" paddingY="3" marginLeft="3">
|
||||
<a href={logoUrl} target="_blank" rel="noopener">
|
||||
<Row alignItems="center" gap>
|
||||
{logoImage ? (
|
||||
<img src={logoImage} alt={logoName} style={{ height: 24 }} />
|
||||
) : (
|
||||
<Icon>
|
||||
<Logo />
|
||||
</Icon>
|
||||
)}
|
||||
<Text weight="bold">{logoName}</Text>
|
||||
</Row>
|
||||
</a>
|
||||
</Row>
|
||||
<Column>
|
||||
<SideMenu
|
||||
items={items}
|
||||
selectedKey={selectedKey}
|
||||
allowMinimize={false}
|
||||
onItemClick={onItemClick}
|
||||
/>
|
||||
</Column>
|
||||
<Column flexGrow={1} justifyContent="flex-end">
|
||||
<Row>
|
||||
<ThemeButton />
|
||||
<LanguageButton />
|
||||
<PreferencesButton />
|
||||
</Row>
|
||||
</Column>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
import { Column, Grid, Row, useTheme } from '@umami/react-zen';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { AttributionPage } from '@/app/(main)/websites/[websiteId]/(reports)/attribution/AttributionPage';
|
||||
import { BreakdownPage } from '@/app/(main)/websites/[websiteId]/(reports)/breakdown/BreakdownPage';
|
||||
|
|
@ -18,10 +18,8 @@ import { WebsiteHeader } from '@/app/(main)/websites/[websiteId]/WebsiteHeader';
|
|||
import { WebsitePage } from '@/app/(main)/websites/[websiteId]/WebsitePage';
|
||||
import { WebsiteProvider } from '@/app/(main)/websites/WebsiteProvider';
|
||||
import { PageBody } from '@/components/common/PageBody';
|
||||
import { useShareTokenQuery } from '@/components/hooks';
|
||||
import { useShare } from '@/components/hooks';
|
||||
import { MobileMenuButton } from '@/components/input/MobileMenuButton';
|
||||
import { ShareFooter } from './ShareFooter';
|
||||
import { ShareHeader } from './ShareHeader';
|
||||
import { ShareNav } from './ShareNav';
|
||||
|
||||
const PAGE_COMPONENTS: Record<string, React.ComponentType<{ websiteId: string }>> = {
|
||||
|
|
@ -58,17 +56,20 @@ const ALL_SECTION_IDS = [
|
|||
'attribution',
|
||||
];
|
||||
|
||||
export function SharePage({ shareId, path = '' }: { shareId: string; path?: string }) {
|
||||
const { shareToken, isLoading } = useShareTokenQuery(shareId);
|
||||
export function SharePage({ shareId }: { shareId: string }) {
|
||||
const share = useShare();
|
||||
const { setTheme } = useTheme();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const path = pathname.split('/')[3];
|
||||
const { websiteId, parameters = {} } = share;
|
||||
|
||||
// Calculate allowed sections
|
||||
const allowedSections = useMemo(() => {
|
||||
if (!shareToken?.parameters) return [];
|
||||
const params = shareToken.parameters;
|
||||
if (!share?.parameters) return [];
|
||||
const params = share.parameters;
|
||||
return ALL_SECTION_IDS.filter(id => params[id] !== false);
|
||||
}, [shareToken?.parameters]);
|
||||
}, [share?.parameters]);
|
||||
|
||||
useEffect(() => {
|
||||
const url = new URL(window?.location?.href);
|
||||
|
|
@ -90,12 +91,6 @@ export function SharePage({ shareId, path = '' }: { shareId: string; path?: stri
|
|||
}
|
||||
}, [allowedSections, shareId, path, router]);
|
||||
|
||||
if (isLoading || !shareToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { websiteId, parameters = {}, whiteLabel } = shareToken;
|
||||
|
||||
// Redirect to only allowed section - return null while redirecting
|
||||
if (
|
||||
allowedSections.length === 1 &&
|
||||
|
|
@ -116,40 +111,25 @@ export function SharePage({ shareId, path = '' }: { shareId: string; path?: stri
|
|||
const PageComponent = PAGE_COMPONENTS[pageKey] || WebsitePage;
|
||||
|
||||
return (
|
||||
<Column backgroundColor="2">
|
||||
<Grid columns={{ xs: '1fr', lg: 'auto 1fr' }} width="100%" height="100%">
|
||||
<Row display={{ xs: 'flex', lg: 'none' }} alignItems="center" gap padding="3">
|
||||
<Grid columns="auto 1fr" flexGrow={1} backgroundColor="3" borderRadius>
|
||||
<MobileMenuButton>
|
||||
{({ close }) => {
|
||||
return <ShareNav shareId={shareId} parameters={parameters} onItemClick={close} />;
|
||||
}}
|
||||
</MobileMenuButton>
|
||||
</Grid>
|
||||
</Row>
|
||||
<Column
|
||||
display={{ xs: 'none', lg: 'flex' }}
|
||||
width="240px"
|
||||
height="100%"
|
||||
border="right"
|
||||
backgroundColor
|
||||
marginRight="2"
|
||||
>
|
||||
<Column display={{ xs: 'none', lg: 'flex' }}>
|
||||
<ShareNav shareId={shareId} parameters={parameters} />
|
||||
<Grid columns={{ xs: '1fr', lg: '240px 1fr' }} width="100%">
|
||||
<Row display={{ xs: 'flex', lg: 'none' }} alignItems="center" gap padding="3">
|
||||
<MobileMenuButton>
|
||||
{({ close }) => {
|
||||
return <ShareNav onItemClick={close} />;
|
||||
}}
|
||||
</MobileMenuButton>
|
||||
</Row>
|
||||
<Column display={{ xs: 'none', lg: 'flex' }} marginRight="2">
|
||||
<ShareNav />
|
||||
</Column>
|
||||
<PageBody gap>
|
||||
<WebsiteProvider websiteId={websiteId}>
|
||||
<Column>
|
||||
<WebsiteHeader showActions={false} />
|
||||
<PageComponent websiteId={websiteId} />
|
||||
</Column>
|
||||
</Column>
|
||||
<PageBody gap>
|
||||
<WebsiteProvider websiteId={websiteId}>
|
||||
<ShareHeader whiteLabel={whiteLabel} />
|
||||
<Column>
|
||||
<WebsiteHeader showActions={false} />
|
||||
<PageComponent websiteId={websiteId} />
|
||||
</Column>
|
||||
<ShareFooter whiteLabel={whiteLabel} />
|
||||
</WebsiteProvider>
|
||||
</PageBody>
|
||||
</Grid>
|
||||
</Column>
|
||||
</WebsiteProvider>
|
||||
</PageBody>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import { ShareProvider } from '@/app/share/ShareProvider';
|
||||
import { SharePage } from './SharePage';
|
||||
|
||||
export default async function ({ params }: { params: Promise<{ shareId: string[] }> }) {
|
||||
const { shareId } = await params;
|
||||
const [slug, ...path] = shareId;
|
||||
const [slug] = shareId;
|
||||
|
||||
return <SharePage shareId={slug} path={path.join('/')} />;
|
||||
return (
|
||||
<ShareProvider shareId={slug}>
|
||||
<SharePage shareId={slug} />
|
||||
</ShareProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue