apply white-labeling to all share pages
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
import { getBoardWebsiteIds } from '@/lib/boards';
|
||||
import { ENTITY_TYPE, ROLES } from '@/lib/constants';
|
||||
import { secret } from '@/lib/crypto';
|
||||
import { createToken } from '@/lib/jwt';
|
||||
import { getBoardWebsiteIds } from '@/lib/boards';
|
||||
import prisma from '@/lib/prisma';
|
||||
import redis from '@/lib/redis';
|
||||
import { json, notFound } from '@/lib/response';
|
||||
import type { WhiteLabel } from '@/lib/types';
|
||||
import type { BoardParameters, WhiteLabel } from '@/lib/types';
|
||||
import { getBoard, getLink, getPixel, getShareByCode, getWebsite } from '@/queries/prisma';
|
||||
|
||||
async function getAccountId(entity: { userId?: string; teamId?: string }): Promise<string | null> {
|
||||
@@ -53,57 +53,35 @@ export async function GET(_request: Request, { params }: { params: Promise<{ slu
|
||||
return notFound();
|
||||
}
|
||||
|
||||
if (share.shareType === ENTITY_TYPE.board) {
|
||||
const board = await getBoard(share.entityId);
|
||||
|
||||
if (!board) {
|
||||
return notFound();
|
||||
}
|
||||
|
||||
const data: Record<string, any> = {
|
||||
shareId: share.id,
|
||||
shareType: share.shareType,
|
||||
boardId: share.entityId,
|
||||
parameters: share.parameters,
|
||||
websiteIds: getBoardWebsiteIds(board),
|
||||
};
|
||||
|
||||
data.token = createToken(data, secret());
|
||||
|
||||
return json(data);
|
||||
}
|
||||
|
||||
let entity: { userId?: string; teamId?: string } | null = null;
|
||||
const data: Record<string, any> = {
|
||||
shareId: share.id,
|
||||
shareType: share.shareType,
|
||||
parameters: share.parameters,
|
||||
};
|
||||
|
||||
if (share.shareType === ENTITY_TYPE.website) {
|
||||
let entity: { userId?: string; teamId?: string } | null = null;
|
||||
|
||||
if (share.shareType === ENTITY_TYPE.board) {
|
||||
const board = await getBoard(share.entityId);
|
||||
if (!board) return notFound();
|
||||
entity = board;
|
||||
data.boardId = share.entityId;
|
||||
data.websiteIds = getBoardWebsiteIds({
|
||||
type: board.type,
|
||||
parameters: share.parameters as BoardParameters,
|
||||
});
|
||||
} else if (share.shareType === ENTITY_TYPE.website) {
|
||||
entity = await getWebsite(share.entityId);
|
||||
|
||||
if (!entity) {
|
||||
return notFound();
|
||||
}
|
||||
|
||||
if (!entity) return notFound();
|
||||
data.websiteId = share.entityId;
|
||||
} else if (share.shareType === ENTITY_TYPE.pixel) {
|
||||
entity = await getPixel(share.entityId);
|
||||
|
||||
if (!entity) {
|
||||
return notFound();
|
||||
}
|
||||
|
||||
if (!entity) return notFound();
|
||||
data.websiteId = share.entityId;
|
||||
data.pixelId = share.entityId;
|
||||
} else if (share.shareType === ENTITY_TYPE.link) {
|
||||
entity = await getLink(share.entityId);
|
||||
|
||||
if (!entity) {
|
||||
return notFound();
|
||||
}
|
||||
|
||||
if (!entity) return notFound();
|
||||
data.websiteId = share.entityId;
|
||||
data.linkId = share.entityId;
|
||||
} else {
|
||||
@@ -116,7 +94,6 @@ export async function GET(_request: Request, { params }: { params: Promise<{ slu
|
||||
|
||||
if (accountId) {
|
||||
const whiteLabel = await getWhiteLabel(accountId);
|
||||
|
||||
if (whiteLabel) {
|
||||
data.whiteLabel = whiteLabel;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
'use client';
|
||||
import { useShare } from '@/components/hooks';
|
||||
import { Logo } from '@/components/svg';
|
||||
import { Icon, Row, Text } from '@umami/react-zen';
|
||||
|
||||
const LOGO_SIZE = { sm: 24, md: 32, lg: 40 };
|
||||
const TEXT_SIZE = { sm: 'sm', md: 'base', lg: 'lg' } as const;
|
||||
|
||||
export function ShareBranding({ size = 'md' }: { size?: 'sm' | 'md' | 'lg' }) {
|
||||
const share = useShare();
|
||||
const logoDomain = share?.whiteLabel?.domainName || 'https://umami.is';
|
||||
const logoName = share?.whiteLabel?.displayName || 'umami';
|
||||
const logoImage = share?.whiteLabel?.logoUrl;
|
||||
const height = LOGO_SIZE[size];
|
||||
|
||||
return (
|
||||
<a href={logoDomain} target="_blank" rel="noopener" style={{ marginLeft: 12 }}>
|
||||
<Row alignItems="center" gap>
|
||||
{logoImage ? (
|
||||
<img src={logoImage} alt={logoName} style={{ height }} />
|
||||
) : (
|
||||
<Icon>
|
||||
<Logo />
|
||||
</Icon>
|
||||
)}
|
||||
<Text size={TEXT_SIZE[size]} weight="bold">
|
||||
{logoName}
|
||||
</Text>
|
||||
</Row>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
'use client';
|
||||
import { useShare } from '@/components/hooks';
|
||||
import { Column, Row } from '@umami/react-zen';
|
||||
import { ShareBranding } from './ShareBranding';
|
||||
|
||||
export function ShareFooter() {
|
||||
const share = useShare();
|
||||
|
||||
if (!share?.whiteLabel) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Column
|
||||
paddingX={{ base: '3', md: '6' }}
|
||||
width="100%"
|
||||
maxWidth="1320px"
|
||||
style={{ margin: '0 auto' }}
|
||||
>
|
||||
<Row border="top" justifyContent="flex-end" paddingY="4">
|
||||
<ShareBranding size="sm" />
|
||||
</Row>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Button, Column, Icon, Row, Text, ThemeButton } from '@umami/react-zen';
|
||||
import { Button, Column, Icon, Row, ThemeButton } from '@umami/react-zen';
|
||||
import { NavMenu } from '@/components/common/NavMenu';
|
||||
import { useMessages, useNavigation, useShare } from '@/components/hooks';
|
||||
import { AlignEndHorizontal, Clock, Eye, PanelLeft, Sheet, Tag, User } from '@/components/icons';
|
||||
import { LanguageButton } from '@/components/input/LanguageButton';
|
||||
import { PreferencesButton } from '@/components/input/PreferencesButton';
|
||||
import { Funnel, Lightning, Logo, Magnet, Money, Network, Path, Target } from '@/components/svg';
|
||||
import { Funnel, Lightning, Magnet, Money, Network, Path, Target } from '@/components/svg';
|
||||
import { ShareBranding } from './ShareBranding';
|
||||
|
||||
export function ShareNav({
|
||||
collapsed,
|
||||
@@ -18,11 +19,7 @@ export function ShareNav({
|
||||
const share = useShare();
|
||||
const { t, labels } = useMessages();
|
||||
const { pathname } = useNavigation();
|
||||
const { slug, parameters, whiteLabel } = share;
|
||||
|
||||
const logoDomain = whiteLabel?.domainName || 'https://umami.is';
|
||||
const logoName = whiteLabel?.displayName || 'umami';
|
||||
const logoImage = whiteLabel?.logoUrl;
|
||||
const { slug, parameters } = share;
|
||||
|
||||
const renderPath = (path: string) => `/share/${slug}${path}`;
|
||||
|
||||
@@ -149,20 +146,7 @@ export function ShareNav({
|
||||
border={isMobile ? undefined : 'right'}
|
||||
>
|
||||
<Row as="header" gap alignItems="center" justifyContent="space-between">
|
||||
{!collapsed && (
|
||||
<a href={logoDomain} target="_blank" rel="noopener" style={{ marginLeft: 12 }}>
|
||||
<Row alignItems="center" gap>
|
||||
{logoImage ? (
|
||||
<img src={logoImage} alt={logoName} style={{ height: 24 }} />
|
||||
) : (
|
||||
<Icon>
|
||||
<Logo />
|
||||
</Icon>
|
||||
)}
|
||||
<Text weight="bold">{logoName}</Text>
|
||||
</Row>
|
||||
</a>
|
||||
)}
|
||||
{!collapsed && <ShareBranding size="md" />}
|
||||
{!onItemClick && (
|
||||
<Button variant="quiet" onPress={() => onCollapse?.(!collapsed)}>
|
||||
<Icon color="muted">
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use client';
|
||||
import { Column, Grid, Row, useTheme } from '@umami/react-zen';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { BoardViewPage } from '@/app/(main)/boards/[boardId]/BoardViewPage';
|
||||
import { LinkPage } from '@/app/(main)/links/[linkId]/LinkPage';
|
||||
import { PixelPage } from '@/app/(main)/pixels/[pixelId]/PixelPage';
|
||||
import { AttributionPage } from '@/app/(main)/websites/[websiteId]/(reports)/attribution/AttributionPage';
|
||||
@@ -19,11 +17,14 @@ import { SessionsPage } from '@/app/(main)/websites/[websiteId]/sessions/Session
|
||||
import { WebsiteHeader } from '@/app/(main)/websites/[websiteId]/WebsiteHeader';
|
||||
import { WebsitePage } from '@/app/(main)/websites/[websiteId]/WebsitePage';
|
||||
import { WebsiteProvider } from '@/app/(main)/websites/WebsiteProvider';
|
||||
import { BoardViewPage } from '@/app/(main)/boards/[boardId]/BoardViewPage';
|
||||
import { PageBody } from '@/components/common/PageBody';
|
||||
import { useShare } from '@/components/hooks';
|
||||
import { MobileMenuButton } from '@/components/input/MobileMenuButton';
|
||||
import { ENTITY_TYPE } from '@/lib/constants';
|
||||
import { Column, Grid, Row, useTheme } from '@umami/react-zen';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ShareFooter } from './ShareFooter';
|
||||
import { ShareNav } from './ShareNav';
|
||||
|
||||
const PAGE_COMPONENTS: Record<string, React.ComponentType<{ websiteId: string }>> = {
|
||||
@@ -72,25 +73,24 @@ export function SharePage() {
|
||||
}
|
||||
}, [setTheme]);
|
||||
|
||||
if (shareType === ENTITY_TYPE.board && boardId) {
|
||||
const entityPage =
|
||||
shareType === ENTITY_TYPE.board && boardId ? (
|
||||
<BoardViewPage boardId={boardId} showActions={false} />
|
||||
) : shareType === ENTITY_TYPE.pixel && pixelId ? (
|
||||
<PixelPage pixelId={pixelId} showHeaderActions={false} />
|
||||
) : shareType === ENTITY_TYPE.link && linkId ? (
|
||||
<LinkPage linkId={linkId} showHeaderActions={false} />
|
||||
) : null;
|
||||
|
||||
if (entityPage) {
|
||||
return (
|
||||
<BoardViewPage
|
||||
boardId={boardId}
|
||||
showActions={false}
|
||||
showControls={false}
|
||||
showEntityBadges={false}
|
||||
/>
|
||||
<Column>
|
||||
{entityPage}
|
||||
<ShareFooter />
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
if (shareType === ENTITY_TYPE.pixel && pixelId) {
|
||||
return <PixelPage pixelId={pixelId} showHeaderActions={false} />;
|
||||
}
|
||||
|
||||
if (shareType === ENTITY_TYPE.link && linkId) {
|
||||
return <LinkPage linkId={linkId} showHeaderActions={false} />;
|
||||
}
|
||||
|
||||
// Check if the requested path is allowed
|
||||
const pageKey = path || '';
|
||||
const isAllowed = pageKey === '' || pageKey === 'overview' || parameters[pageKey] !== false;
|
||||
|
||||
Reference in New Issue
Block a user