diff --git a/next.config.ts b/next.config.ts
index 77a0c1c6..278e7a6d 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -97,7 +97,12 @@ const headers = [
},
];
-const rewrites = [];
+const rewrites = [
+ {
+ source: '/teams/:id/settings/:path*',
+ destination: '/settings/:path*',
+ },
+];
if (trackerScriptURL) {
rewrites.push({
diff --git a/package.json b/package.json
index bf0bad76..c2c9b3cc 100644
--- a/package.json
+++ b/package.json
@@ -82,7 +82,7 @@
"@react-spring/web": "^9.7.3",
"@svgr/cli": "^8.1.0",
"@tanstack/react-query": "^5.83.0",
- "@umami/react-zen": "^0.154.0",
+ "@umami/react-zen": "^0.157.0",
"@umami/redis-client": "^0.27.0",
"bcryptjs": "^3.0.2",
"chalk": "^5.4.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cd379179..fce70ca0 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -45,8 +45,8 @@ importers:
specifier: ^5.83.0
version: 5.83.0(react@19.1.0)
'@umami/react-zen':
- specifier: ^0.154.0
- version: 0.154.0(@babel/core@7.27.1)(@types/react@19.1.8)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0))
+ specifier: ^0.157.0
+ version: 0.157.0(@babel/core@7.27.1)(@types/react@19.1.8)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0))
'@umami/redis-client':
specifier: ^0.27.0
version: 0.27.0
@@ -2544,8 +2544,8 @@ packages:
resolution: {integrity: sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@umami/react-zen@0.154.0':
- resolution: {integrity: sha512-waVMJNwGPSDereFWTnbtdJjQGQhJIi1n7Hs2EnuS9OSMUbj9THgA1A9722zyO3w46KmbIBMqmIxur4ykpHOYlg==}
+ '@umami/react-zen@0.157.0':
+ resolution: {integrity: sha512-QZqZcah1t6KniDvFTF+GxBDkaEYUuQOyj1olao5p5rtZBMW1/v4GaVcL+8UAzWyWIUnsrRI3e5HoytapJg6VfQ==}
'@umami/redis-client@0.27.0':
resolution: {integrity: sha512-SbHTpxhgeZyTBUSp2zdZM+XUtpsaSL4Tad8QXIEhEtjWhvvfoornyT5kLuyYCVtzSAT4daALeGmOO1z6EE1KcA==}
@@ -5984,8 +5984,8 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19
- react-hook-form@7.61.1:
- resolution: {integrity: sha512-2vbXUFDYgqEgM2RcXcAT2PwDW/80QARi+PKmHy5q2KhuKvOlG8iIYgf7eIlIANR5trW9fJbP4r5aub3a4egsew==}
+ react-hook-form@7.62.0:
+ resolution: {integrity: sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19
@@ -9751,7 +9751,7 @@ snapshots:
'@typescript-eslint/types': 8.38.0
eslint-visitor-keys: 4.2.1
- '@umami/react-zen@0.154.0(@babel/core@7.27.1)(@types/react@19.1.8)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0))':
+ '@umami/react-zen@0.157.0(@babel/core@7.27.1)(@types/react@19.1.8)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@9.0.21)(use-sync-external-store@1.5.0(react@19.1.0))':
dependencies:
'@fontsource/jetbrains-mono': 5.2.6
'@internationalized/date': 3.8.2
@@ -9765,7 +9765,7 @@ snapshots:
react: 19.1.0
react-aria-components: 1.9.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-dom: 19.1.0(react@19.1.0)
- react-hook-form: 7.61.1(react@19.1.0)
+ react-hook-form: 7.62.0(react@19.1.0)
react-icons: 5.5.0(react@19.1.0)
thenby: 1.3.4
zustand: 5.0.6(@types/react@19.1.8)(immer@9.0.21)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0))
@@ -13763,7 +13763,7 @@ snapshots:
dependencies:
react: 19.1.0
- react-hook-form@7.61.1(react@19.1.0):
+ react-hook-form@7.62.0(react@19.1.0):
dependencies:
react: 19.1.0
diff --git a/src/app/(main)/App.tsx b/src/app/(main)/App.tsx
index e3602d5a..bb33dc87 100644
--- a/src/app/(main)/App.tsx
+++ b/src/app/(main)/App.tsx
@@ -4,7 +4,6 @@ import Script from 'next/script';
import { usePathname } from 'next/navigation';
import { UpdateNotice } from './UpdateNotice';
import { SideNav } from '@/app/(main)/SideNav';
-import { TopNav } from '@/app/(main)/TopNav';
import { useLoginQuery, useConfig } from '@/components/hooks';
export function App({ children }) {
@@ -31,7 +30,6 @@ export function App({ children }) {
-
{children}
diff --git a/src/app/(main)/SideNav.tsx b/src/app/(main)/SideNav.tsx
index 25f5a4ab..06b0abc9 100644
--- a/src/app/(main)/SideNav.tsx
+++ b/src/app/(main)/SideNav.tsx
@@ -17,15 +17,20 @@ import {
LockKeyhole,
} from '@/components/icons';
import { useMessages, useNavigation, useGlobalState } from '@/components/hooks';
-import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav';
import { TeamsButton } from '@/components/input/TeamsButton';
import { PanelButton } from '@/components/input/PanelButton';
export function SideNav(props: SidebarProps) {
const { formatMessage, labels } = useMessages();
- const { pathname, renderUrl, websiteId, teamId } = useNavigation();
+ const { pathname, renderUrl, websiteId } = useNavigation();
const [isCollapsed] = useGlobalState('sidenav-collapsed');
- const isWebsite = websiteId && !pathname.includes('/settings');
+
+ const hasNav = !!(
+ websiteId ||
+ pathname.startsWith('/admin') ||
+ pathname.startsWith('/settings') ||
+ pathname.endsWith('/settings')
+ );
const links = [
{
@@ -71,12 +76,7 @@ export function SideNav(props: SidebarProps) {
return (
-
+
} />
@@ -90,23 +90,21 @@ export function SideNav(props: SidebarProps) {
})}
- {!teamId &&
- bottomLinks.map(({ id, path, label, icon }) => {
- return (
-
-
-
- );
- })}
+ {bottomLinks.map(({ id, path, label, icon }) => {
+ return (
+
+
+
+ );
+ })}
-
+
- {isWebsite && }
);
}
diff --git a/src/app/(main)/admin/AdminLayout.tsx b/src/app/(main)/admin/AdminLayout.tsx
index 9dfc118c..87abd77b 100644
--- a/src/app/(main)/admin/AdminLayout.tsx
+++ b/src/app/(main)/admin/AdminLayout.tsx
@@ -2,9 +2,8 @@
import { ReactNode } from 'react';
import { Grid, Column } from '@umami/react-zen';
import { useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
+import { User, Users, Globe } from '@/components/icons';
import { SideMenu } from '@/components/common/SideMenu';
-import { PageHeader } from '@/components/common/PageHeader';
-import { Panel } from '@/components/common/Panel';
import { PageBody } from '@/components/common/PageBody';
export function AdminLayout({ children }: { children: ReactNode }) {
@@ -18,37 +17,45 @@ export function AdminLayout({ children }: { children: ReactNode }) {
const items = [
{
- id: 'users',
- label: formatMessage(labels.users),
- url: '/admin/users',
- },
- {
- id: 'websites',
- label: formatMessage(labels.websites),
- url: '/admin/websites',
- },
- {
- id: 'teams',
- label: formatMessage(labels.teams),
- url: '/admin/teams',
+ label: formatMessage(labels.application),
+ items: [
+ {
+ id: 'users',
+ label: formatMessage(labels.users),
+ path: '/admin/users',
+ icon: ,
+ },
+ {
+ id: 'websites',
+ label: formatMessage(labels.websites),
+ path: '/admin/websites',
+ icon: ,
+ },
+ {
+ id: 'teams',
+ label: formatMessage(labels.teams),
+ path: '/admin/teams',
+ icon: ,
+ },
+ ],
},
];
- const value = items.find(({ url }) => pathname.includes(url))?.id;
+ const selectedKey = items
+ .flatMap(e => e.items)
+ ?.find(({ path }) => path && pathname.startsWith(path))?.id;
return (
-
-
-
-
-
-
-
-
- {children}
-
-
+
+
+
-
+ {children}
+
);
}
diff --git a/src/app/(main)/admin/teams/AdminTeamsPage.tsx b/src/app/(main)/admin/teams/AdminTeamsPage.tsx
index 37f43c94..1d4984e8 100644
--- a/src/app/(main)/admin/teams/AdminTeamsPage.tsx
+++ b/src/app/(main)/admin/teams/AdminTeamsPage.tsx
@@ -1,16 +1,19 @@
'use client';
import { AdminTeamsDataTable } from './AdminTeamsDataTable';
import { Column } from '@umami/react-zen';
-import { SectionHeader } from '@/components/common/SectionHeader';
import { useMessages } from '@/components/hooks';
+import { PageHeader } from '@/components/common/PageHeader';
+import { Panel } from '@/components/common/Panel';
export function AdminTeamsPage() {
const { formatMessage, labels } = useMessages();
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/src/app/(main)/admin/teams/AdminTeamsTable.tsx b/src/app/(main)/admin/teams/AdminTeamsTable.tsx
index da72955d..bacb7de8 100644
--- a/src/app/(main)/admin/teams/AdminTeamsTable.tsx
+++ b/src/app/(main)/admin/teams/AdminTeamsTable.tsx
@@ -32,7 +32,9 @@ export function AdminTeamsTable({
{(row: any) => (
- {row?.teamUser?.[0]?.user?.username}
+
+ {row?.teamUser?.[0]?.user?.username}
+
)}
diff --git a/src/app/(main)/admin/teams/[teamId]/AdminTeamPage.tsx b/src/app/(main)/admin/teams/[teamId]/AdminTeamPage.tsx
index 7f680a91..a7c5b0e2 100644
--- a/src/app/(main)/admin/teams/[teamId]/AdminTeamPage.tsx
+++ b/src/app/(main)/admin/teams/[teamId]/AdminTeamPage.tsx
@@ -1,6 +1,6 @@
'use client';
import { TeamDetails } from '@/app/(main)/settings/teams/[teamId]/TeamDetails';
-import { TeamProvider } from '@/app/(main)/teams/[teamId]/TeamProvider';
+import { TeamProvider } from '@/app/(main)/settings/teams/[teamId]/TeamProvider';
export function AdminTeamPage({ teamId }: { teamId: string }) {
return (
diff --git a/src/app/(main)/admin/users/UsersPage.tsx b/src/app/(main)/admin/users/UsersPage.tsx
index 5c8a1ae3..47ac5512 100644
--- a/src/app/(main)/admin/users/UsersPage.tsx
+++ b/src/app/(main)/admin/users/UsersPage.tsx
@@ -1,9 +1,10 @@
'use client';
import { UsersDataTable } from './UsersDataTable';
import { Column } from '@umami/react-zen';
-import { SectionHeader } from '@/components/common/SectionHeader';
import { useMessages } from '@/components/hooks';
import { UserAddButton } from './UserAddButton';
+import { PageHeader } from '@/components/common/PageHeader';
+import { Panel } from '@/components/common/Panel';
export function UsersPage() {
const { formatMessage, labels } = useMessages();
@@ -11,11 +12,13 @@ export function UsersPage() {
const handleSave = () => {};
return (
-
-
+
+
-
-
+
+
+
+
);
}
diff --git a/src/app/(main)/admin/users/[userId]/UserHeader.tsx b/src/app/(main)/admin/users/[userId]/UserHeader.tsx
new file mode 100644
index 00000000..f9fcdd64
--- /dev/null
+++ b/src/app/(main)/admin/users/[userId]/UserHeader.tsx
@@ -0,0 +1,10 @@
+import { useContext } from 'react';
+import { User } from '@/components/icons';
+import { PageHeader } from '@/components/common/PageHeader';
+import { UserContext } from '@/app/(main)/admin/users/[userId]/UserProvider';
+
+export function UserHeader() {
+ const user = useContext(UserContext);
+
+ return } />;
+}
diff --git a/src/app/(main)/admin/users/[userId]/UserPage.tsx b/src/app/(main)/admin/users/[userId]/UserPage.tsx
index 56812c0c..0ae65855 100644
--- a/src/app/(main)/admin/users/[userId]/UserPage.tsx
+++ b/src/app/(main)/admin/users/[userId]/UserPage.tsx
@@ -1,11 +1,19 @@
'use client';
+import { Column } from '@umami/react-zen';
import { UserSettings } from './UserSettings';
import { UserProvider } from './UserProvider';
+import { UserHeader } from '@/app/(main)/admin/users/[userId]/UserHeader';
+import { Panel } from '@/components/common/Panel';
export function UserPage({ userId }: { userId: string }) {
return (
-
+
+
+
+
+
+
);
}
diff --git a/src/app/(main)/admin/users/[userId]/UserSettings.tsx b/src/app/(main)/admin/users/[userId]/UserSettings.tsx
index 8369c3f4..3d699ebe 100644
--- a/src/app/(main)/admin/users/[userId]/UserSettings.tsx
+++ b/src/app/(main)/admin/users/[userId]/UserSettings.tsx
@@ -1,19 +1,13 @@
-import { useContext } from 'react';
-import { Tabs, Tab, TabList, TabPanel } from '@umami/react-zen';
-import { User } from '@/components/icons';
+import { Column, Tabs, Tab, TabList, TabPanel } from '@umami/react-zen';
import { UserEditForm } from './UserEditForm';
-import { SectionHeader } from '@/components/common/SectionHeader';
import { useMessages } from '@/components/hooks';
import { UserWebsites } from './UserWebsites';
-import { UserContext } from './UserProvider';
export function UserSettings({ userId }: { userId: string }) {
const { formatMessage, labels } = useMessages();
- const user = useContext(UserContext);
return (
- <>
- } />
+
{formatMessage(labels.details)}
@@ -26,6 +20,6 @@ export function UserSettings({ userId }: { userId: string }) {
- >
+
);
}
diff --git a/src/app/(main)/admin/websites/AdminWebsitesPage.tsx b/src/app/(main)/admin/websites/AdminWebsitesPage.tsx
index 07a73a40..3ca61f78 100644
--- a/src/app/(main)/admin/websites/AdminWebsitesPage.tsx
+++ b/src/app/(main)/admin/websites/AdminWebsitesPage.tsx
@@ -1,16 +1,19 @@
'use client';
import { AdminWebsitesDataTable } from './AdminWebsitesDataTable';
import { Column } from '@umami/react-zen';
-import { SectionHeader } from '@/components/common/SectionHeader';
import { useMessages } from '@/components/hooks';
+import { PageHeader } from '@/components/common/PageHeader';
+import { Panel } from '@/components/common/Panel';
export function AdminWebsitesPage() {
const { formatMessage, labels } = useMessages();
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/src/app/(main)/admin/websites/AdminWebsitesTable.tsx b/src/app/(main)/admin/websites/AdminWebsitesTable.tsx
index 7b970e7f..81a6aacf 100644
--- a/src/app/(main)/admin/websites/AdminWebsitesTable.tsx
+++ b/src/app/(main)/admin/websites/AdminWebsitesTable.tsx
@@ -55,7 +55,7 @@ export function AdminWebsitesTable({ data = [] }: { data: any[] }) {
return (
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/src/app/(main)/settings/preferences/PreferencesPage.tsx b/src/app/(main)/settings/preferences/PreferencesPage.tsx
index 62d7f882..99289956 100644
--- a/src/app/(main)/settings/preferences/PreferencesPage.tsx
+++ b/src/app/(main)/settings/preferences/PreferencesPage.tsx
@@ -1,16 +1,19 @@
'use client';
import { Column } from '@umami/react-zen';
import { useMessages } from '@/components/hooks';
-import { SectionHeader } from '@/components/common/SectionHeader';
+import { Panel } from '@/components/common/Panel';
import { PreferenceSettings } from './PreferenceSettings';
+import { PageHeader } from '@/components/common/PageHeader';
export function PreferencesPage() {
const { formatMessage, labels } = useMessages();
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/src/app/(main)/settings/profile/ProfilePage.tsx b/src/app/(main)/settings/profile/ProfilePage.tsx
index aaec3f58..2d1ce0d3 100644
--- a/src/app/(main)/settings/profile/ProfilePage.tsx
+++ b/src/app/(main)/settings/profile/ProfilePage.tsx
@@ -1,16 +1,19 @@
'use client';
import { ProfileSettings } from './ProfileSettings';
import { useMessages } from '@/components/hooks';
-import { SectionHeader } from '@/components/common/SectionHeader';
+import { Panel } from '@/components/common/Panel';
import { Column } from '@umami/react-zen';
+import { PageHeader } from '@/components/common/PageHeader';
export function ProfilePage() {
const { formatMessage, labels } = useMessages();
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/src/app/(main)/settings/profile/ProfileSettings.tsx b/src/app/(main)/settings/profile/ProfileSettings.tsx
index 72ae7d91..b580d686 100644
--- a/src/app/(main)/settings/profile/ProfileSettings.tsx
+++ b/src/app/(main)/settings/profile/ProfileSettings.tsx
@@ -34,12 +34,10 @@ export function ProfileSettings() {
{username}
-
{renderRole(role)}
-
{!cloudMode && (
diff --git a/src/app/(main)/settings/teams/TeamsHeader.tsx b/src/app/(main)/settings/teams/TeamsHeader.tsx
index 96774f5e..61244033 100644
--- a/src/app/(main)/settings/teams/TeamsHeader.tsx
+++ b/src/app/(main)/settings/teams/TeamsHeader.tsx
@@ -1,5 +1,5 @@
import { Row } from '@umami/react-zen';
-import { SectionHeader } from '@/components/common/SectionHeader';
+import { PageHeader } from '@/components/common/PageHeader';
import { ROLES } from '@/lib/constants';
import { useLoginQuery, useMessages } from '@/components/hooks';
import { TeamsJoinButton } from './TeamsJoinButton';
@@ -11,11 +11,11 @@ export function TeamsHeader({ allowCreate = true }: { allowCreate?: boolean }) {
const cloudMode = !!process.env.cloudMode;
return (
-
+
{!cloudMode && }
{allowCreate && user.role !== ROLES.viewOnly && }
-
+
);
}
diff --git a/src/app/(main)/settings/teams/TeamsSettingsPage.tsx b/src/app/(main)/settings/teams/TeamsSettingsPage.tsx
index e64461c0..57e4aad8 100644
--- a/src/app/(main)/settings/teams/TeamsSettingsPage.tsx
+++ b/src/app/(main)/settings/teams/TeamsSettingsPage.tsx
@@ -2,12 +2,15 @@
import { TeamsDataTable } from './TeamsDataTable';
import { TeamsHeader } from './TeamsHeader';
import { Column } from '@umami/react-zen';
+import { Panel } from '@/components/common/Panel';
export function TeamsSettingsPage() {
return (
-
+
-
+
+
+
);
}
diff --git a/src/app/(main)/settings/teams/TeamsTable.tsx b/src/app/(main)/settings/teams/TeamsTable.tsx
index bfe6b332..a2599251 100644
--- a/src/app/(main)/settings/teams/TeamsTable.tsx
+++ b/src/app/(main)/settings/teams/TeamsTable.tsx
@@ -7,7 +7,7 @@ import Link from 'next/link';
export function TeamsTable({
data = [],
- showActions = true,
+ showActions = false,
}: {
data: any[];
allowEdit?: boolean;
@@ -29,7 +29,7 @@ export function TeamsTable({
{(row: any) => row._count.teamUser}
- {showActions && (
+ {showActions ? (
{(row: any) => {
const { id } = row;
@@ -56,7 +56,7 @@ export function TeamsTable({
);
}}
- )}
+ ) : null}
);
}
diff --git a/src/app/(main)/settings/teams/[teamId]/TeamDetails.tsx b/src/app/(main)/settings/teams/[teamId]/TeamDetails.tsx
index 071c56a8..e4900004 100644
--- a/src/app/(main)/settings/teams/[teamId]/TeamDetails.tsx
+++ b/src/app/(main)/settings/teams/[teamId]/TeamDetails.tsx
@@ -1,8 +1,8 @@
import { useContext, useState } from 'react';
import { Column, Tabs, TabList, Tab, TabPanel } from '@umami/react-zen';
-import { TeamContext } from '@/app/(main)/teams/[teamId]/TeamProvider';
+import { TeamContext } from '@/app/(main)/settings/teams/[teamId]/TeamProvider';
import { useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
-import { SectionHeader } from '@/components/common/SectionHeader';
+
import { ROLES } from '@/lib/constants';
import { Users } from '@/components/icons';
import { TeamLeaveButton } from '@/app/(main)/settings/teams/TeamLeaveButton';
@@ -10,6 +10,8 @@ import { TeamManage } from './TeamManage';
import { TeamEditForm } from './TeamEditForm';
import { TeamWebsitesDataTable } from './TeamWebsitesDataTable';
import { TeamMembersDataTable } from './TeamMembersDataTable';
+import { PageHeader } from '@/components/common/PageHeader';
+import { Panel } from '@/components/common/Panel';
export function TeamDetails({ teamId }: { teamId: string }) {
const team = useContext(TeamContext);
@@ -33,30 +35,32 @@ export function TeamDetails({ teamId }: { teamId: string }) {
user.role !== ROLES.viewOnly);
return (
-
- }>
+
+ }>
{!isTeamOwner && !isAdmin && }
-
- setTab(value)}>
-
- {formatMessage(labels.details)}
- {formatMessage(labels.members)}
- {formatMessage(labels.websites)}
- {isTeamOwner && {formatMessage(labels.manage)}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ setTab(value)}>
+
+ {formatMessage(labels.details)}
+ {formatMessage(labels.members)}
+ {formatMessage(labels.websites)}
+ {isTeamOwner && {formatMessage(labels.manage)}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/app/(main)/settings/teams/[teamId]/TeamEditForm.tsx b/src/app/(main)/settings/teams/[teamId]/TeamEditForm.tsx
index 9c053ac8..ce206169 100644
--- a/src/app/(main)/settings/teams/[teamId]/TeamEditForm.tsx
+++ b/src/app/(main)/settings/teams/[teamId]/TeamEditForm.tsx
@@ -10,7 +10,7 @@ import {
import { getRandomChars } from '@/lib/crypto';
import { useContext } from 'react';
import { useApi, useMessages, useModified } from '@/components/hooks';
-import { TeamContext } from '@/app/(main)/teams/[teamId]/TeamProvider';
+import { TeamContext } from '@/app/(main)/settings/teams/[teamId]/TeamProvider';
const generateId = () => `team_${getRandomChars(16)}`;
diff --git a/src/app/(main)/settings/teams/[teamId]/TeamMemberEditButton.tsx b/src/app/(main)/settings/teams/[teamId]/TeamMemberEditButton.tsx
index c8056146..a9216439 100644
--- a/src/app/(main)/settings/teams/[teamId]/TeamMemberEditButton.tsx
+++ b/src/app/(main)/settings/teams/[teamId]/TeamMemberEditButton.tsx
@@ -1,6 +1,7 @@
import { useMessages, useModified } from '@/components/hooks';
-import { Row, Button, Icon, Modal, DialogTrigger, Dialog, useToast } from '@umami/react-zen';
+import { Dialog, useToast } from '@umami/react-zen';
import { TeamMemberEditForm } from './TeamMemberEditForm';
+import { ActionButton } from '@/components/input/ActionButton';
import { Edit } from '@/components/icons';
export function TeamMemberEditButton({
@@ -25,27 +26,18 @@ export function TeamMemberEditButton({
};
return (
-
-
-
-
-
-
+ }>
+
+
);
}
diff --git a/src/app/(main)/settings/teams/[teamId]/TeamMemberRemoveButton.tsx b/src/app/(main)/settings/teams/[teamId]/TeamMemberRemoveButton.tsx
index be1a11cc..5b1d323f 100644
--- a/src/app/(main)/settings/teams/[teamId]/TeamMemberRemoveButton.tsx
+++ b/src/app/(main)/settings/teams/[teamId]/TeamMemberRemoveButton.tsx
@@ -2,7 +2,8 @@ import { ConfirmationForm } from '@/components/common/ConfirmationForm';
import { useApi, useMessages, useModified } from '@/components/hooks';
import { messages } from '@/components/messages';
import { Trash } from '@/components/icons';
-import { Button, Icon, Modal, DialogTrigger, Dialog } from '@umami/react-zen';
+import { Dialog } from '@umami/react-zen';
+import { ActionButton } from '@/components/input/ActionButton';
export function TeamMemberRemoveButton({
teamId,
@@ -34,29 +35,22 @@ export function TeamMemberRemoveButton({
};
return (
-
-
-
-
-
-
+ }>
+
+
);
}
diff --git a/src/app/(main)/teams/[teamId]/TeamProvider.tsx b/src/app/(main)/settings/teams/[teamId]/TeamProvider.tsx
similarity index 100%
rename from src/app/(main)/teams/[teamId]/TeamProvider.tsx
rename to src/app/(main)/settings/teams/[teamId]/TeamProvider.tsx
diff --git a/src/app/(main)/settings/teams/[teamId]/TeamSettingsPage.tsx b/src/app/(main)/settings/teams/[teamId]/TeamSettingsPage.tsx
index 88ec9b40..d172c3aa 100644
--- a/src/app/(main)/settings/teams/[teamId]/TeamSettingsPage.tsx
+++ b/src/app/(main)/settings/teams/[teamId]/TeamSettingsPage.tsx
@@ -1,5 +1,5 @@
'use client';
-import { TeamProvider } from '@/app/(main)/teams/[teamId]/TeamProvider';
+import { TeamProvider } from '@/app/(main)/settings/teams/[teamId]/TeamProvider';
import { TeamDetails } from '@/app/(main)/settings/teams/[teamId]/TeamDetails';
export function TeamSettingsPage({ teamId }: { teamId: string }) {
diff --git a/src/app/(main)/settings/teams/[teamId]/TeamWebsitesTable.tsx b/src/app/(main)/settings/teams/[teamId]/TeamWebsitesTable.tsx
index f04f1f85..da97fc51 100644
--- a/src/app/(main)/settings/teams/[teamId]/TeamWebsitesTable.tsx
+++ b/src/app/(main)/settings/teams/[teamId]/TeamWebsitesTable.tsx
@@ -1,58 +1,19 @@
-import { DataColumn, DataTable, Icon, MenuItem, Text, Row } from '@umami/react-zen';
-import { useLoginQuery, useMessages } from '@/components/hooks';
-import { Eye, Edit } from '@/components/icons';
-import { MenuButton } from '@/components/input/MenuButton';
+import { DataColumn, DataTable } from '@umami/react-zen';
+import { useMessages } from '@/components/hooks';
import Link from 'next/link';
-export function TeamWebsitesTable({
- teamId,
- data = [],
- allowEdit = false,
-}: {
- teamId: string;
- data: any[];
- allowEdit?: boolean;
-}) {
- const { user } = useLoginQuery();
+export function TeamWebsitesTable({ teamId, data = [] }: { teamId: string; data: any[] }) {
const { formatMessage, labels } = useMessages();
return (
- {(row: any) => {row.name}}
+ {(row: any) => {row.name}}
{(row: any) => row?.createUser?.username}
-
- {(row: any) => {
- const { id: websiteId } = row;
-
- return (
-
-
-
-
-
-
- {formatMessage(labels.view)}
-
-
- {allowEdit && (teamId || user?.isAdmin) && (
-
-
-
-
-
- {formatMessage(labels.edit)}
-
-
- )}
-
- );
- }}
-
);
}
diff --git a/src/app/(main)/settings/websites/WebsitesTable.tsx b/src/app/(main)/settings/websites/WebsitesTable.tsx
index a7948781..2d285c75 100644
--- a/src/app/(main)/settings/websites/WebsitesTable.tsx
+++ b/src/app/(main)/settings/websites/WebsitesTable.tsx
@@ -57,7 +57,7 @@ export function WebsitesTable({
)}
{allowEdit && (
-
+
diff --git a/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx b/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx
index fce82f50..1c1f4a9e 100644
--- a/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx
+++ b/src/app/(main)/settings/websites/[websiteId]/WebsiteData.tsx
@@ -1,5 +1,4 @@
import { Button, Modal, DialogTrigger, Dialog, Column } from '@umami/react-zen';
-import { useRouter } from 'next/navigation';
import {
useLoginQuery,
useMessages,
@@ -17,9 +16,9 @@ export function WebsiteData({ websiteId, onSave }: { websiteId: string; onSave?:
const { formatMessage, labels, messages } = useMessages();
const { user } = useLoginQuery();
const { touch } = useModified();
- const { teamId, renderUrl } = useNavigation();
- const router = useRouter();
+ const { router, pathname, teamId, renderUrl } = useNavigation();
const { data: teams } = useUserTeamsQuery(user.id);
+ const isAdmin = pathname.startsWith('/admin');
const canTransferWebsite =
(
@@ -49,21 +48,23 @@ export function WebsiteData({ websiteId, onSave }: { websiteId: string; onSave?:
return (
-
-
-
-
-
-
-
-
+ {!isAdmin && (
+
+
+
+
+
+
+
+
+ )}
- }>
-
-
-
-
- {formatMessage(labels.view)}
-
-
-
-
- {formatMessage(labels.details)}
- {formatMessage(labels.trackingCode)}
- {formatMessage(labels.shareUrl)}
- {formatMessage(labels.manage)}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
+
+
+ {formatMessage(labels.details)}
+ {formatMessage(labels.trackingCode)}
+ {formatMessage(labels.shareUrl)}
+ {formatMessage(labels.manage)}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/app/(main)/settings/websites/[websiteId]/WebsiteSettingsHeader.tsx b/src/app/(main)/settings/websites/[websiteId]/WebsiteSettingsHeader.tsx
new file mode 100644
index 00000000..ee542f2f
--- /dev/null
+++ b/src/app/(main)/settings/websites/[websiteId]/WebsiteSettingsHeader.tsx
@@ -0,0 +1,10 @@
+import { useContext } from 'react';
+import { WebsiteContext } from '@/app/(main)/websites/[websiteId]/WebsiteProvider';
+import { PageHeader } from '@/components/common/PageHeader';
+import { Globe } from '@/components/icons';
+
+export function WebsiteSettingsHeader() {
+ const website = useContext(WebsiteContext);
+
+ return } />;
+}
diff --git a/src/app/(main)/settings/websites/[websiteId]/WebsiteSettingsPage.tsx b/src/app/(main)/settings/websites/[websiteId]/WebsiteSettingsPage.tsx
index 5df7a819..1aff4e7e 100644
--- a/src/app/(main)/settings/websites/[websiteId]/WebsiteSettingsPage.tsx
+++ b/src/app/(main)/settings/websites/[websiteId]/WebsiteSettingsPage.tsx
@@ -1,11 +1,19 @@
'use client';
+import { Column } from '@umami/react-zen';
import { WebsiteProvider } from '@/app/(main)/websites/[websiteId]/WebsiteProvider';
import { WebsiteSettings } from './WebsiteSettings';
+import { WebsiteSettingsHeader } from '@/app/(main)/settings/websites/[websiteId]/WebsiteSettingsHeader';
+import { Panel } from '@/components/common/Panel';
export function WebsiteSettingsPage({ websiteId }: { websiteId: string }) {
return (
-
+
+
+
+
+
+
);
}
diff --git a/src/app/(main)/teams/[teamId]/layout.tsx b/src/app/(main)/teams/[teamId]/layout.tsx
deleted file mode 100644
index 1dfdd243..00000000
--- a/src/app/(main)/teams/[teamId]/layout.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { TeamProvider } from './TeamProvider';
-import { Metadata } from 'next';
-
-export default async function ({
- children,
- params,
-}: {
- children: any;
- params: Promise<{ teamId: string }>;
-}) {
- const { teamId } = await params;
-
- return {children};
-}
-
-export const metadata: Metadata = {
- title: 'Teams',
-};
diff --git a/src/app/(main)/websites/[websiteId]/WebsiteFilterButton.tsx b/src/app/(main)/websites/[websiteId]/WebsiteFilterButton.tsx
index a3442d1c..f8617225 100644
--- a/src/app/(main)/websites/[websiteId]/WebsiteFilterButton.tsx
+++ b/src/app/(main)/websites/[websiteId]/WebsiteFilterButton.tsx
@@ -1,6 +1,6 @@
import { Button, Icon, DialogTrigger, Dialog, Modal, Text } from '@umami/react-zen';
import { ListFilter } from '@/components/icons';
-import { FilterEditForm } from '@/components/common/FilterEditForm';
+import { FilterEditForm } from '@/components/input/FilterEditForm';
import { useMessages, useNavigation, useFilters } from '@/components/hooks';
import { filtersArrayToObject } from '@/lib/params';
diff --git a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx
index ae2cf1bb..1935fe07 100644
--- a/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx
+++ b/src/app/(main)/websites/[websiteId]/WebsiteHeader.tsx
@@ -5,11 +5,17 @@ import { Share, Edit } from '@/components/icons';
import { Favicon } from '@/components/common/Favicon';
import { ActiveUsers } from '@/components/metrics/ActiveUsers';
import { WebsiteShareForm } from '@/app/(main)/settings/websites/[websiteId]/WebsiteShareForm';
-import { useMessages } from '@/components/hooks';
+import { useMessages, useNavigation } from '@/components/hooks';
import { LinkButton } from '@/components/common/LinkButton';
export function WebsiteHeader() {
const website = useWebsite();
+ const { renderUrl, pathname } = useNavigation();
+ const isSettings = pathname.endsWith('/settings');
+
+ if (isSettings) {
+ return null;
+ }
return (
} marginBottom="3">
@@ -17,7 +23,7 @@ export function WebsiteHeader() {
-
+
diff --git a/src/app/(main)/websites/[websiteId]/WebsiteLayout.tsx b/src/app/(main)/websites/[websiteId]/WebsiteLayout.tsx
index 14374904..b095afdb 100644
--- a/src/app/(main)/websites/[websiteId]/WebsiteLayout.tsx
+++ b/src/app/(main)/websites/[websiteId]/WebsiteLayout.tsx
@@ -1,17 +1,23 @@
'use client';
import { ReactNode } from 'react';
-import { Column } from '@umami/react-zen';
+import { Column, Grid } from '@umami/react-zen';
import { WebsiteProvider } from './WebsiteProvider';
import { PageBody } from '@/components/common/PageBody';
import { WebsiteHeader } from './WebsiteHeader';
+import { WebsiteNav } from '@/app/(main)/websites/[websiteId]/WebsiteNav';
export function WebsiteLayout({ websiteId, children }: { websiteId: string; children: ReactNode }) {
return (
-
-
- {children}
-
+
+
+
+
+
+
+ {children}
+
+
);
}
diff --git a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx
index 9ba78662..54cfe0bb 100644
--- a/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx
+++ b/src/app/(main)/websites/[websiteId]/WebsiteNav.tsx
@@ -1,4 +1,3 @@
-import { Icon, Text, Row, NavMenu, NavMenuItem, NavMenuGroup, Column } from '@umami/react-zen';
import {
Eye,
Lightning,
@@ -16,14 +15,16 @@ import {
ChartPie,
} from '@/components/icons';
import { useMessages, useNavigation } from '@/components/hooks';
-import Link from 'next/link';
+import { SideMenu } from '@/components/common/SideMenu';
import { WebsiteSelect } from '@/components/input/WebsiteSelect';
export function WebsiteNav({ websiteId }: { websiteId: string }) {
const { formatMessage, labels } = useMessages();
const { pathname, renderUrl, teamId } = useNavigation();
- const links = [
+ const renderPath = (path: string) => renderUrl(`/websites/${websiteId}${path}`);
+
+ const items = [
{
label: formatMessage(labels.traffic),
items: [
@@ -31,25 +32,31 @@ export function WebsiteNav({ websiteId }: { websiteId: string }) {
id: 'overview',
label: formatMessage(labels.overview),
icon: ,
- path: '',
+ path: renderPath(''),
},
{
id: 'events',
label: formatMessage(labels.events),
icon: ,
- path: '/events',
+ path: renderPath('/events'),
},
{
id: 'sessions',
label: formatMessage(labels.sessions),
icon: ,
- path: '/sessions',
+ path: renderPath('/sessions'),
},
{
id: 'realtime',
label: formatMessage(labels.realtime),
icon: ,
- path: '/realtime',
+ path: renderPath('/realtime'),
+ },
+ {
+ id: 'breakdown',
+ label: formatMessage(labels.breakdown),
+ icon: ,
+ path: renderPath('/breakdown'),
},
],
},
@@ -60,48 +67,42 @@ export function WebsiteNav({ websiteId }: { websiteId: string }) {
id: 'goals',
label: formatMessage(labels.goals),
icon: ,
- path: '/goals',
+ path: renderPath('/goals'),
},
{
id: 'funnel',
label: formatMessage(labels.funnels),
icon: ,
- path: '/funnels',
+ path: renderPath('/funnels'),
},
{
id: 'journeys',
label: formatMessage(labels.journeys),
icon: ,
- path: '/journeys',
+ path: renderPath('/journeys'),
},
{
id: 'retention',
label: formatMessage(labels.retention),
icon: ,
- path: '/retention',
+ path: renderPath('/retention'),
},
],
},
{
label: formatMessage(labels.segments),
items: [
- {
- id: 'breakdown',
- label: formatMessage(labels.breakdown),
- icon: ,
- path: '/breakdown',
- },
{
id: 'segments',
label: formatMessage(labels.segments),
icon: ,
- path: '/segments',
+ path: renderPath('/segments'),
},
{
id: 'cohorts',
label: formatMessage(labels.cohorts),
icon: ,
- path: '/cohorts',
+ path: renderPath('/cohorts'),
},
],
},
@@ -112,53 +113,31 @@ export function WebsiteNav({ websiteId }: { websiteId: string }) {
id: 'utm',
label: formatMessage(labels.utm),
icon: ,
- path: '/utm',
+ path: renderPath('/utm'),
},
{
id: 'revenue',
label: formatMessage(labels.revenue),
icon: ,
- path: '/revenue',
+ path: renderPath('/revenue'),
},
{
id: 'attribution',
label: formatMessage(labels.attribution),
icon: ,
- path: '/attribution',
+ path: renderPath('/attribution'),
},
],
},
];
- const selected =
- links.flatMap(e => e.items).find(({ path }) => path && pathname.endsWith(path))?.id ||
+ const selectedKey =
+ items.flatMap(e => e.items).find(({ path }) => path && pathname.endsWith(path))?.id ||
'overview';
return (
-
+
-
- {links.map(({ label, items }) => {
- return (
-
- {items.map(({ id, label, icon, path }) => {
- const isSelected = selected === id;
-
- return (
-
-
-
- {icon}
- {label}
-
-
-
- );
- })}
-
- );
- })}
-
-
+
);
}
diff --git a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx
index d93c7d8c..805f1252 100644
--- a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx
+++ b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx
@@ -3,7 +3,7 @@ import { useMessages, useWebsiteEventsQuery } from '@/components/hooks';
import { EventsTable } from './EventsTable';
import { DataGrid } from '@/components/common/DataGrid';
import { ReactNode } from 'react';
-import { FilterButtons } from '@/components/common/FilterButtons';
+import { FilterButtons } from '@/components/input/FilterButtons';
export function EventsDataTable({
websiteId,
diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx
index 8e965528..a706242a 100644
--- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx
+++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeLog.tsx
@@ -1,6 +1,6 @@
import { useFormat } from '@/components//hooks/useFormat';
import { Empty } from '@/components/common/Empty';
-import { FilterButtons } from '@/components/common/FilterButtons';
+import { FilterButtons } from '@/components/input/FilterButtons';
import { useCountryNames, useLocale, useMessages, useTimezone } from '@/components/hooks';
import { Eye, Visitor, Bolt } from '@/components/icons';
import { BROWSERS, OS_NAMES } from '@/lib/constants';
diff --git a/src/app/(main)/websites/[websiteId]/realtime/RealtimeUrls.tsx b/src/app/(main)/websites/[websiteId]/realtime/RealtimeUrls.tsx
index e6b493bf..15e53a6b 100644
--- a/src/app/(main)/websites/[websiteId]/realtime/RealtimeUrls.tsx
+++ b/src/app/(main)/websites/[websiteId]/realtime/RealtimeUrls.tsx
@@ -6,7 +6,7 @@ import { ListTable } from '@/components/metrics/ListTable';
import { useMessages } from '@/components/hooks';
import { RealtimeData } from '@/lib/types';
import { WebsiteContext } from '../WebsiteProvider';
-import { FilterButtons } from '@/components/common/FilterButtons';
+import { FilterButtons } from '@/components/input/FilterButtons';
const FILTER_REFERRERS = 'filter-referrers';
const FILTER_PAGES = 'filter-pages';
diff --git a/src/app/(main)/websites/[websiteId]/segments/SegmentAddButton.tsx b/src/app/(main)/websites/[websiteId]/segments/SegmentAddButton.tsx
index 3c198fd4..d8539406 100644
--- a/src/app/(main)/websites/[websiteId]/segments/SegmentAddButton.tsx
+++ b/src/app/(main)/websites/[websiteId]/segments/SegmentAddButton.tsx
@@ -1,7 +1,7 @@
import { Button, DialogTrigger, Modal, Text, Icon, Dialog } from '@umami/react-zen';
import { useMessages } from '@/components/hooks';
import { Plus } from '@/components/icons';
-import { SegmentEditForm } from '@/app/(main)/websites/[websiteId]/segments/SegmentEditForm';
+import { SegmentEditForm } from './SegmentEditForm';
export function SegmentAddButton({ websiteId }: { websiteId: string }) {
const { formatMessage, labels } = useMessages();
diff --git a/src/app/(main)/websites/[websiteId]/segments/SegmentDeleteButton.tsx b/src/app/(main)/websites/[websiteId]/segments/SegmentDeleteButton.tsx
new file mode 100644
index 00000000..fc86e78f
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/segments/SegmentDeleteButton.tsx
@@ -0,0 +1,55 @@
+import { Dialog } from '@umami/react-zen';
+import { ActionButton } from '@/components/input/ActionButton';
+import { Trash } from '@/components/icons';
+import { ConfirmationForm } from '@/components/common/ConfirmationForm';
+import { messages } from '@/components/messages';
+import { useApi, useMessages, useModified } from '@/components/hooks';
+
+export function SegmentDeleteButton({
+ segmentId,
+ websiteId,
+ name,
+ onSave,
+}: {
+ segmentId: string;
+ websiteId: string;
+ name: string;
+ onSave?: () => void;
+}) {
+ const { formatMessage, labels } = useMessages();
+ const { del, useMutation } = useApi();
+ const { mutate, isPending, error } = useMutation({
+ mutationFn: () => del(`/websites/${websiteId}/segments/${segmentId}`),
+ });
+ const { touch } = useModified();
+
+ const handleConfirm = (close: () => void) => {
+ mutate(null, {
+ onSuccess: () => {
+ touch('segments');
+ onSave?.();
+ close();
+ },
+ });
+ };
+
+ return (
+ }>
+
+
+ );
+}
diff --git a/src/app/(main)/websites/[websiteId]/segments/SegmentEditButton.tsx b/src/app/(main)/websites/[websiteId]/segments/SegmentEditButton.tsx
new file mode 100644
index 00000000..c7041f7a
--- /dev/null
+++ b/src/app/(main)/websites/[websiteId]/segments/SegmentEditButton.tsx
@@ -0,0 +1,34 @@
+import { ActionButton } from '@/components/input/ActionButton';
+import { Edit } from '@/components/icons';
+import { Dialog } from '@umami/react-zen';
+import { SegmentEditForm } from '@/app/(main)/websites/[websiteId]/segments/SegmentEditForm';
+import { useMessages } from '@/components/hooks';
+
+export function SegmentEditButton({
+ segmentId,
+ websiteId,
+ filters,
+}: {
+ segmentId: string;
+ websiteId: string;
+ filters: any[];
+}) {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+ }>
+
+
+ );
+}
diff --git a/src/app/(main)/websites/[websiteId]/segments/SegmentEditForm.tsx b/src/app/(main)/websites/[websiteId]/segments/SegmentEditForm.tsx
index 15133e8e..b19785a5 100644
--- a/src/app/(main)/websites/[websiteId]/segments/SegmentEditForm.tsx
+++ b/src/app/(main)/websites/[websiteId]/segments/SegmentEditForm.tsx
@@ -6,24 +6,30 @@ import {
FormSubmitButton,
TextField,
Label,
+ Loading,
} from '@umami/react-zen';
import { subMonths, endOfDay } from 'date-fns';
import { FieldFilters } from '@/components/input/FieldFilters';
import { useState } from 'react';
-import { useApi, useMessages, useModified } from '@/components/hooks';
+import { useApi, useMessages, useModified, useWebsiteSegmentQuery } from '@/components/hooks';
import { filtersArrayToObject } from '@/lib/params';
export function SegmentEditForm({
+ segmentId,
websiteId,
filters = [],
+ showFilters = true,
onSave,
onClose,
}: {
+ segmentId: string;
websiteId: string;
filters?: any[];
+ showFilters?: boolean;
onSave?: () => void;
onClose?: () => void;
}) {
+ const { data } = useWebsiteSegmentQuery(websiteId, segmentId);
const { formatMessage, labels } = useMessages();
const [currentFilters, setCurrentFilters] = useState(filters);
const { touch } = useModified();
@@ -33,7 +39,10 @@ export function SegmentEditForm({
const { post, useMutation } = useApi();
const { mutate, error, isPending } = useMutation({
mutationFn: (data: any) =>
- post(`/websites/${websiteId}/segments`, { ...data, type: 'segment' }),
+ post(`/websites/${websiteId}/segments${segmentId ? `/${segmentId}` : ''}`, {
+ ...data,
+ type: 'segment',
+ }),
});
const handleSubmit = async (data: any) => {
@@ -49,28 +58,40 @@ export function SegmentEditForm({
);
};
+ if (segmentId && !data) {
+ return ;
+ }
+
return (
-
-
-
-
- {formatMessage(labels.clearAll)}
-
-
+
+
+
+
+
+ {formatMessage(labels.saveSegment)}
+
+
+
+
+
+
+
+
+
+ {formatMessage(labels.clearAll)}
+
+
+
);
}
diff --git a/src/components/common/FilterButtons.tsx b/src/components/input/FilterButtons.tsx
similarity index 100%
rename from src/components/common/FilterButtons.tsx
rename to src/components/input/FilterButtons.tsx
diff --git a/src/components/common/FilterEditForm.tsx b/src/components/input/FilterEditForm.tsx
similarity index 96%
rename from src/components/common/FilterEditForm.tsx
rename to src/components/input/FilterEditForm.tsx
index b253b780..33846575 100644
--- a/src/components/common/FilterEditForm.tsx
+++ b/src/components/input/FilterEditForm.tsx
@@ -57,7 +57,7 @@ export function FilterEditForm({
onSave={setCurrentFilters}
/>
-
+
-
+
diff --git a/src/components/input/ProfileButton.tsx b/src/components/input/ProfileButton.tsx
index e30557f1..5b890095 100644
--- a/src/components/input/ProfileButton.tsx
+++ b/src/components/input/ProfileButton.tsx
@@ -9,6 +9,7 @@ import {
MenuSeparator,
MenuSection,
Text,
+ Row,
} from '@umami/react-zen';
import { useRouter } from 'next/navigation';
import { useMessages, useLoginQuery } from '@/components/hooks';
@@ -36,25 +37,31 @@ export function ProfileButton() {
-
-
-
- {formatMessage(labels.settings)}
+
+
+
+
+ {formatMessage(labels.settings)}
+
{user.isAdmin && (
-
-
-
- {formatMessage(labels.admin)}
+
+
+
+
+ {formatMessage(labels.admin)}
+
)}
{!cloudMode && (
-
-
-
- {formatMessage(labels.logout)}
+
+
+
+
+ {formatMessage(labels.logout)}
+
)}
diff --git a/src/components/input/SegmentFilters.tsx b/src/components/input/SegmentFilters.tsx
index 3cf8ce46..1406d7a3 100644
--- a/src/components/input/SegmentFilters.tsx
+++ b/src/components/input/SegmentFilters.tsx
@@ -1,4 +1,4 @@
-import { List, Column, ListItem } from '@umami/react-zen';
+import { List, ListItem } from '@umami/react-zen';
import { useWebsiteSegmentsQuery } from '@/components/hooks';
import { LoadingPanel } from '@/components/common/LoadingPanel';
@@ -9,25 +9,23 @@ export interface SegmentFiltersProps {
}
export function SegmentFilters({ websiteId, segmentId, onSave }: SegmentFiltersProps) {
- const { data, isLoading } = useWebsiteSegmentsQuery(websiteId, { type: 'segment' });
+ const { data, isLoading, isFetching } = useWebsiteSegmentsQuery(websiteId, { type: 'segment' });
const handleChange = (id: string) => {
onSave?.(id);
};
return (
-
-
- handleChange(id[0])}>
- {data?.data?.map(item => {
- return (
-
- {item.name}
-
- );
- })}
-
-
-
+
+ handleChange(id[0])}>
+ {data?.data?.map(item => {
+ return (
+
+ {item.name}
+
+ );
+ })}
+
+
);
}
diff --git a/src/components/input/SegmentSaveButton.tsx b/src/components/input/SegmentSaveButton.tsx
new file mode 100644
index 00000000..f6cee0e1
--- /dev/null
+++ b/src/components/input/SegmentSaveButton.tsx
@@ -0,0 +1,26 @@
+import { Button, DialogTrigger, Modal, Text, Icon, Dialog } from '@umami/react-zen';
+import { useMessages } from '@/components/hooks';
+import { Plus } from '@/components/icons';
+import { SegmentEditForm } from '@/app/(main)/websites/[websiteId]/segments/SegmentEditForm';
+
+export function SegmentSaveButton({ websiteId }: { websiteId: string }) {
+ const { formatMessage, labels } = useMessages();
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/input/TeamsButton.tsx b/src/components/input/TeamsButton.tsx
index d9fd5e08..6c84493a 100644
--- a/src/components/input/TeamsButton.tsx
+++ b/src/components/input/TeamsButton.tsx
@@ -15,7 +15,7 @@ import {
Pressable,
} from '@umami/react-zen';
import { useLoginQuery, useMessages, useUserTeamsQuery, useNavigation } from '@/components/hooks';
-import { Chevron, User, Users } from '@/components/icons';
+import { Chevron, User, Users, LogOut } from '@/components/icons';
export function TeamsButton({ showText = true }: { showText?: boolean }) {
const { user } = useLoginQuery();
@@ -81,6 +81,17 @@ export function TeamsButton({ showText = true }: { showText?: boolean }) {
))}
+
+
+
+
+
+
+
+ {formatMessage(labels.logout)}
+
+
+
diff --git a/src/components/input/WebsiteSelect.tsx b/src/components/input/WebsiteSelect.tsx
index 18a1e4a7..40e31a2f 100644
--- a/src/components/input/WebsiteSelect.tsx
+++ b/src/components/input/WebsiteSelect.tsx
@@ -33,13 +33,13 @@ export function WebsiteSelect({
items={data?.['data'] || []}
value={websiteId}
isLoading={isLoading}
- buttonProps={buttonProps}
+ buttonProps={{ ...buttonProps }}
allowSearch={true}
searchValue={search}
onSearch={handleSearch}
onChange={handleSelect}
renderValue={() => (
-
+
{website?.name}
)}
diff --git a/src/components/messages.ts b/src/components/messages.ts
index e0e8413e..c06f4a02 100644
--- a/src/components/messages.ts
+++ b/src/components/messages.ts
@@ -342,6 +342,10 @@ export const labels = defineMessages({
traffic: { id: 'label.traffic', defaultMessage: 'Traffic' },
behavior: { id: 'label.behavior', defaultMessage: 'Behavior' },
growth: { id: 'label.growth', defaultMessage: 'Growth' },
+ account: { id: 'label.account', defaultMessage: 'Account' },
+ application: { id: 'label.application', defaultMessage: 'Application' },
+ saveSegment: { id: 'label.save-segment', defaultMessage: 'Save segment' },
+ saveCohort: { id: 'label.save-cohort', defaultMessage: 'Save cohort' },
});
export const messages = defineMessages({
diff --git a/src/components/metrics/PagesTable.tsx b/src/components/metrics/PagesTable.tsx
index 5fd43fef..086d3dc7 100644
--- a/src/components/metrics/PagesTable.tsx
+++ b/src/components/metrics/PagesTable.tsx
@@ -1,5 +1,5 @@
import { WebsiteContext } from '@/app/(main)/websites/[websiteId]/WebsiteProvider';
-import { FilterButtons } from '@/components/common/FilterButtons';
+import { FilterButtons } from '@/components/input/FilterButtons';
import { FilterLink } from '@/components/common/FilterLink';
import { useMessages, useNavigation } from '@/components/hooks';
import { emptyFilter } from '@/lib/filters';
diff --git a/src/components/metrics/QueryParametersTable.tsx b/src/components/metrics/QueryParametersTable.tsx
index e6d85495..e98f9134 100644
--- a/src/components/metrics/QueryParametersTable.tsx
+++ b/src/components/metrics/QueryParametersTable.tsx
@@ -1,6 +1,6 @@
import { useState } from 'react';
import { Row, Text } from '@umami/react-zen';
-import { FilterButtons } from '@/components/common/FilterButtons';
+import { FilterButtons } from '@/components/input/FilterButtons';
import { emptyFilter, paramFilter } from '@/lib/filters';
import { MetricsTable, MetricsTableProps } from './MetricsTable';
import { useMessages } from '@/components/hooks';
diff --git a/src/components/metrics/ReferrersTable.tsx b/src/components/metrics/ReferrersTable.tsx
index 0b3f1ee3..8990db1a 100644
--- a/src/components/metrics/ReferrersTable.tsx
+++ b/src/components/metrics/ReferrersTable.tsx
@@ -1,7 +1,7 @@
import { Row } from '@umami/react-zen';
import { FilterLink } from '@/components/common/FilterLink';
import { Favicon } from '@/components/common/Favicon';
-import { FilterButtons } from '@/components/common/FilterButtons';
+import { FilterButtons } from '@/components/input/FilterButtons';
import { useMessages, useNavigation } from '@/components/hooks';
import { MetricsTable, MetricsTableProps } from './MetricsTable';
import thenby from 'thenby';
diff --git a/src/index.ts b/src/index.ts
index 243d5ea7..0f89b6fb 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -17,6 +17,7 @@ export * from '@/app/(main)/settings/teams/TeamAddForm';
export * from '@/app/(main)/settings/teams/TeamJoinForm';
export * from '@/app/(main)/settings/teams/TeamLeaveButton';
export * from '@/app/(main)/settings/teams/TeamLeaveForm';
+export * from '@/app/(main)/settings/teams/[teamId]/TeamProvider';
export * from '@/app/(main)/settings/teams/TeamsAddButton';
export * from '@/app/(main)/settings/teams/TeamsDataTable';
export * from '@/app/(main)/settings/teams/TeamsHeader';
@@ -37,7 +38,6 @@ export * from '@/app/(main)/settings/websites/WebsitesDataTable';
export * from '@/app/(main)/settings/websites/WebsitesHeader';
export * from '@/app/(main)/settings/websites/WebsitesTable';
-export * from '@/app/(main)/teams/[teamId]/TeamProvider';
export * from '@/app/(main)/websites/[websiteId]/WebsiteProvider';
export * from '@/components/common/ConfirmationForm';
@@ -46,7 +46,7 @@ export * from '@/components/common/Empty';
export * from '@/components/common/ErrorBoundary';
export * from '@/components/common/ErrorMessage';
export * from '@/components/common/Favicon';
-export * from '@/components/common/FilterButtons';
+export * from '@/components/input/FilterButtons';
export * from '@/components/common/FilterLink';
export * from '@/components/common/HamburgerButton';
export * from '@/components/common/LinkButton';