diff --git a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx
index df476e79b..bbbb06147 100644
--- a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx
+++ b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx
@@ -1,5 +1,14 @@
import { Text } from '@umami/react-zen';
-import { Eye, User, Clock, Ungroup, Tag, ChartPie, UserPlus, GitCompare } from '@/components/icons';
+import {
+ Eye,
+ User,
+ Clock,
+ Sheet,
+ Tag,
+ ChartPie,
+ UserPlus,
+ AlignEndHorizontal,
+} from '@/components/icons';
import { Lightning, Path, Money, Target, Funnel, Magnet, Network } from '@/components/svg';
import { useMessages, useNavigation } from '@/components/hooks';
import { SideMenu } from '@/components/common/SideMenu';
@@ -47,13 +56,13 @@ export function WebsiteNav({ websiteId }: { websiteId: string }) {
{
id: 'compare',
label: formatMessage(labels.compare),
- icon: ,
+ icon: ,
path: renderPath('/compare'),
},
{
id: 'breakdown',
label: formatMessage(labels.breakdown),
- icon: ,
+ icon: ,
path: renderPath('/breakdown'),
},
],
diff --git a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx
index 48e8b0158..b918ec4df 100644
--- a/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx
+++ b/src/app/(main)/websites/[websiteId]/events/EventsPage.tsx
@@ -9,6 +9,7 @@ import { useMessages } from '@/components/hooks';
import { EventProperties } from './EventProperties';
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
import { getItem, setItem } from '@/lib/storage';
+import { SessionModal } from '@/app/(main)/websites/[websiteId]/sessions/SessionModal';
const KEY_NAME = 'umami.events.tab';
@@ -52,6 +53,7 @@ export function EventsPage({ websiteId }) {
+
);
}
diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx
index 31044982b..c36ee5e73 100644
--- a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx
+++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx
@@ -10,7 +10,7 @@ import { TypeIcon } from '@/components/common/TypeIcon';
export function EventsTable({ data = [] }) {
const { formatMessage, labels } = useMessages();
- const { renderUrl } = useNavigation();
+ const { updateParams } = useNavigation();
const { formatValue } = useFormat();
if (data.length === 0) {
@@ -23,7 +23,7 @@ export function EventsTable({ data = [] }) {
{(row: any) => {
return (
-
+
{row.eventName ? : }
diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionModal.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionModal.tsx
new file mode 100644
index 000000000..d2240d84e
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/sessions/SessionModal.tsx
@@ -0,0 +1,49 @@
+import { Dialog, Modal, ModalProps } from '@umami/react-zen';
+import { SessionProfile } from '@/app/(main)/websites/[websiteId]/sessions/SessionProfile';
+import { useNavigation } from '@/components/hooks';
+
+export interface SessionModalProps extends ModalProps {
+ websiteId: string;
+}
+
+export function SessionModal({ websiteId, ...props }: SessionModalProps) {
+ const {
+ router,
+ query: { session },
+ updateParams,
+ } = useNavigation();
+
+ const handleClose = (close: () => void) => {
+ router.push(updateParams({ session: undefined }));
+ close();
+ };
+
+ const handleOpenChange = (isOpen: boolean) => {
+ if (!isOpen) {
+ router.push(updateParams({ session: undefined }));
+ }
+ };
+
+ return (
+
+
+
+ );
+}
diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx
index ae0d41043..8a096aa78 100644
--- a/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx
+++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsPage.tsx
@@ -1,35 +1,19 @@
'use client';
import { Key, useState } from 'react';
-import { TabList, Tab, Tabs, TabPanel, Column, Modal, Dialog } from '@umami/react-zen';
+import { TabList, Tab, Tabs, TabPanel, Column } from '@umami/react-zen';
import { SessionsDataTable } from './SessionsDataTable';
import { SessionProperties } from './SessionProperties';
-import { useMessages, useNavigation } from '@/components/hooks';
+import { useMessages } from '@/components/hooks';
import { Panel } from '@/components/common/Panel';
import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls';
import { getItem, setItem } from '@/lib/storage';
-import { SessionProfile } from '@/app/(main)/websites/[websiteId]/sessions/SessionProfile';
+import { SessionModal } from '@/app/(main)/websites/[websiteId]/sessions/SessionModal';
const KEY_NAME = 'umami.sessions.tab';
export function SessionsPage({ websiteId }) {
const [tab, setTab] = useState(getItem(KEY_NAME) || 'activity');
const { formatMessage, labels } = useMessages();
- const {
- router,
- query: { session },
- updateParams,
- } = useNavigation();
-
- const handleClose = (close: () => void) => {
- router.push(updateParams({ session: undefined }));
- close();
- };
-
- const handleOpenChange = (isOpen: boolean) => {
- if (!isOpen) {
- router.push(updateParams({ session: undefined }));
- }
- };
const handleSelect = (value: Key) => {
setItem(KEY_NAME, value);
@@ -53,26 +37,7 @@ export function SessionsPage({ websiteId }) {
-
-
-
+
);
}
diff --git a/src/app/api/websites/route.ts b/src/app/api/websites/route.ts
index 776f23b0a..994e22bd7 100644
--- a/src/app/api/websites/route.ts
+++ b/src/app/api/websites/route.ts
@@ -1,4 +1,5 @@
import { z } from 'zod';
+import redis from '@/lib/redis';
import { canCreateTeamWebsite, canCreateWebsite } from '@/permissions';
import { json, unauthorized } from '@/lib/response';
import { uuid } from '@/lib/crypto';
@@ -50,11 +51,15 @@ export async function POST(request: Request) {
const { id, name, domain, shareId, teamId } = body;
- if (process.env.CLOUD_MODE && !teamId && !auth.user.hasSubscription) {
- const count = await getWebsiteCount(auth.user.id);
+ if (process.env.CLOUD_MODE && !teamId) {
+ const account = await redis.client.get(`account:${auth.user.id}`);
- if (count >= CLOUD_WEBSITE_LIMIT) {
- return unauthorized({ message: 'Website limit reached.' });
+ if (!account?.hasSubscription) {
+ const count = await getWebsiteCount(auth.user.id);
+
+ if (count >= CLOUD_WEBSITE_LIMIT) {
+ return unauthorized({ message: 'Website limit reached.' });
+ }
}
}
diff --git a/src/components/input/FilterBar.tsx b/src/components/input/FilterBar.tsx
index 96ad51b4f..b81f77e30 100644
--- a/src/components/input/FilterBar.tsx
+++ b/src/components/input/FilterBar.tsx
@@ -25,13 +25,14 @@ export function FilterBar({ websiteId }: { websiteId: string }) {
const { formatValue } = useFormat();
const {
router,
+ pathname,
updateParams,
replaceParams,
query: { segment, cohort },
} = useNavigation();
const { filters, operatorLabels } = useFilters();
const { data, isLoading } = useWebsiteSegmentQuery(websiteId, segment || cohort);
- const canSaveSegment = filters.length > 0 && !segment && !cohort;
+ const canSaveSegment = filters.length > 0 && !segment && !cohort && !pathname.includes('/share');
const handleCloseFilter = (param: string) => {
router.push(updateParams({ [param]: undefined }));
diff --git a/src/lib/request.ts b/src/lib/request.ts
index afd2b1300..da247e20b 100644
--- a/src/lib/request.ts
+++ b/src/lib/request.ts
@@ -1,12 +1,12 @@
-import { z } from 'zod';
import { checkAuth } from '@/lib/auth';
import { DEFAULT_PAGE_SIZE, FILTER_COLUMNS } from '@/lib/constants';
import { getAllowedUnits, getMinimumUnit, maxDate, parseDateRange } from '@/lib/date';
import { fetchWebsite } from '@/lib/load';
+import { filtersArrayToObject } from '@/lib/params';
import { badRequest, unauthorized } from '@/lib/response';
import { QueryFilters } from '@/lib/types';
import { getWebsiteSegment } from '@/queries/prisma';
-import { filtersArrayToObject } from '@/lib/params';
+import { z } from 'zod';
export async function parseRequest(
request: Request,
diff --git a/src/queries/prisma/website.ts b/src/queries/prisma/website.ts
index 315dca038..5f5404d5d 100644
--- a/src/queries/prisma/website.ts
+++ b/src/queries/prisma/website.ts
@@ -156,7 +156,10 @@ export async function resetWebsite(websiteId: string) {
}),
]).then(async data => {
if (cloudMode) {
- await redis.client.set(`website:${websiteId}`, data[3]);
+ await redis.client.set(
+ `website:${websiteId}`,
+ data.find(website => website.id),
+ );
}
return data;
@@ -208,6 +211,7 @@ export async function getWebsiteCount(userId: string) {
return prisma.client.website.count({
where: {
userId,
+ deletedAt: null,
},
});
}