From b5c6194f36a18472a70bcb2cf50cbb0abbd90fe3 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 21 Feb 2025 16:55:05 -0800 Subject: [PATCH] Converted user and website settings. --- src/app/(main)/App.tsx | 2 +- .../(main)/console/[[...websiteId]]/page.tsx | 2 +- src/app/(main)/reports/ReportsDataTable.tsx | 6 +- src/app/(main)/reports/[reportId]/page.tsx | 2 +- src/app/(main)/settings/layout.tsx | 2 +- .../(main)/settings/teams/TeamsDataTable.tsx | 6 +- .../(main)/settings/users/UserAddButton.tsx | 25 +++-- .../settings/users/UserDeleteButton.tsx | 20 ++-- .../(main)/settings/users/UserDeleteForm.tsx | 4 + .../(main)/settings/users/UsersDataTable.tsx | 6 +- src/app/(main)/settings/users/UsersTable.tsx | 52 ++++++----- .../settings/users/[userId]/UserEditForm.tsx | 93 +++++++++---------- .../settings/users/[userId]/UserPage.tsx | 2 +- .../settings/users/[userId]/UserProvider.tsx | 4 +- .../settings/users/[userId]/UserSettings.tsx | 45 +++------ .../settings/users/[userId]/UserWebsites.tsx | 6 +- .../(main)/settings/users/[userId]/page.tsx | 2 +- .../settings/websites/WebsitesDataTable.tsx | 6 +- .../websites/WebsitesSettingsPage.tsx | 2 +- .../settings/websites/WebsitesTable.tsx | 52 ++++++----- .../websites/[websiteId]/WebsiteEditForm.tsx | 76 ++++++++------- .../websites/[websiteId]/WebsiteSettings.tsx | 59 +++++------- .../settings/websites/[websiteId]/page.tsx | 2 +- src/app/(main)/settings/websites/page.tsx | 2 +- .../settings/members/TeamMembersDataTable.tsx | 6 +- .../teams/[teamId]/settings/team/page.tsx | 2 +- .../websites/TeamWebsitesDataTable.tsx | 6 +- .../teams/[teamId]/settings/websites/page.tsx | 2 +- .../[websiteId]/WebsiteExpandedView.tsx | 4 +- .../compare/WebsiteCompareTables.tsx | 4 +- .../websites/[websiteId]/compare/page.tsx | 2 +- .../[websiteId]/events/EventsDataTable.tsx | 6 +- .../websites/[websiteId]/events/page.tsx | 2 +- .../websites/[websiteId]/realtime/page.tsx | 2 +- .../websites/[websiteId]/reports/page.tsx | 2 +- .../sessions/SessionsDataTable.tsx | 6 +- .../[websiteId]/sessions/[sessionId]/page.tsx | 2 +- .../websites/[websiteId]/sessions/page.tsx | 2 +- src/app/api/users/[userId]/route.ts | 2 +- src/app/login/LoginForm.tsx | 10 +- src/assets/logo.svg | 2 +- src/components/common/ConfirmationForm.tsx | 16 ++-- ...taTable.module.css => DataGrid.module.css} | 0 .../common/{DataTable.tsx => DataGrid.tsx} | 55 +++++------ src/components/common/LoadingPanel.tsx | 2 +- src/components/common/Pager.tsx | 33 +++---- src/components/hooks/useApi.ts | 2 +- .../input/LanguageButton.module.css | 1 + src/components/layout/MenuLayout.tsx | 20 ++-- ...{SideNav.module.css => MenuNav.module.css} | 0 src/components/layout/MenuNav.tsx | 29 ++++++ src/components/layout/NavGroup.module.css | 80 ---------------- src/components/layout/NavGroup.tsx | 64 ------------- src/components/layout/PageHeader.tsx | 16 +--- src/components/layout/SideNav.tsx | 44 --------- src/index.ts | 3 +- src/lib/fetch.ts | 10 +- src/styles/global.css | 1 + src/styles/variables.css | 1 + 59 files changed, 363 insertions(+), 554 deletions(-) rename src/components/common/{DataTable.module.css => DataGrid.module.css} (100%) rename src/components/common/{DataTable.tsx => DataGrid.tsx} (64%) rename src/components/layout/{SideNav.module.css => MenuNav.module.css} (100%) create mode 100644 src/components/layout/MenuNav.tsx delete mode 100644 src/components/layout/NavGroup.module.css delete mode 100644 src/components/layout/NavGroup.tsx delete mode 100644 src/components/layout/SideNav.tsx diff --git a/src/app/(main)/App.tsx b/src/app/(main)/App.tsx index 56d50a6a..0ea6799f 100644 --- a/src/app/(main)/App.tsx +++ b/src/app/(main)/App.tsx @@ -14,7 +14,7 @@ export function App({ children }) { const pathname = usePathname(); if (isLoading) { - return ; + return ; } if (error) { diff --git a/src/app/(main)/console/[[...websiteId]]/page.tsx b/src/app/(main)/console/[[...websiteId]]/page.tsx index 7c3433c5..efb793a4 100644 --- a/src/app/(main)/console/[[...websiteId]]/page.tsx +++ b/src/app/(main)/console/[[...websiteId]]/page.tsx @@ -5,7 +5,7 @@ async function getEnabled() { return !!process.env.ENABLE_TEST_CONSOLE; } -export default async function ({ params }: { params: { websiteId: string } }) { +export default async function ({ params }: { params: Promise<{ websiteId: string }> }) { const { websiteId } = await params; const enabled = await getEnabled(); diff --git a/src/app/(main)/reports/ReportsDataTable.tsx b/src/app/(main)/reports/ReportsDataTable.tsx index dca2aaa2..90fd291d 100644 --- a/src/app/(main)/reports/ReportsDataTable.tsx +++ b/src/app/(main)/reports/ReportsDataTable.tsx @@ -1,6 +1,6 @@ import { useReports } from '@/components/hooks'; import { ReportsTable } from './ReportsTable'; -import { DataTable } from '@/components/common/DataTable'; +import { DataGrid } from '@/components/common/DataGrid'; import { ReactNode } from 'react'; export function ReportsDataTable({ @@ -15,8 +15,8 @@ export function ReportsDataTable({ const queryResult = useReports({ websiteId, teamId }); return ( - children}> + children}> {({ data }) => } - + ); } diff --git a/src/app/(main)/reports/[reportId]/page.tsx b/src/app/(main)/reports/[reportId]/page.tsx index 037e1430..b228896a 100644 --- a/src/app/(main)/reports/[reportId]/page.tsx +++ b/src/app/(main)/reports/[reportId]/page.tsx @@ -1,7 +1,7 @@ import { Metadata } from 'next'; import { ReportPage } from './ReportPage'; -export default async function ({ params }: { params: { reportId: string } }) { +export default async function ({ params }: { params: Promise<{ reportId: string }> }) { const { reportId } = await params; return ; diff --git a/src/app/(main)/settings/layout.tsx b/src/app/(main)/settings/layout.tsx index 94aed1fd..fcc7392a 100644 --- a/src/app/(main)/settings/layout.tsx +++ b/src/app/(main)/settings/layout.tsx @@ -1,5 +1,5 @@ -import { SettingsLayout } from './SettingsLayout'; import { Metadata } from 'next'; +import { SettingsLayout } from './SettingsLayout'; export default function ({ children }) { if (process.env.cloudMode) { diff --git a/src/app/(main)/settings/teams/TeamsDataTable.tsx b/src/app/(main)/settings/teams/TeamsDataTable.tsx index f04edd1b..a02c00c0 100644 --- a/src/app/(main)/settings/teams/TeamsDataTable.tsx +++ b/src/app/(main)/settings/teams/TeamsDataTable.tsx @@ -1,4 +1,4 @@ -import { DataTable } from '@/components/common/DataTable'; +import { DataGrid } from '@/components/common/DataGrid'; import { TeamsTable } from '@/app/(main)/settings/teams/TeamsTable'; import { useLogin, useTeams } from '@/components/hooks'; import { ReactNode } from 'react'; @@ -16,10 +16,10 @@ export function TeamsDataTable({ const queryResult = useTeams(user.id); return ( - children}> + children}> {({ data }) => { return ; }} - + ); } diff --git a/src/app/(main)/settings/users/UserAddButton.tsx b/src/app/(main)/settings/users/UserAddButton.tsx index ce08b05c..3dc312a7 100644 --- a/src/app/(main)/settings/users/UserAddButton.tsx +++ b/src/app/(main)/settings/users/UserAddButton.tsx @@ -1,29 +1,40 @@ -import { Button, Icon, Text, Modal, Icons, ModalTrigger, useToasts } from 'react-basics'; +import { + Button, + Icon, + Text, + Modal, + Icons, + DialogTrigger, + Dialog, + useToast, +} from '@umami/react-zen'; import { UserAddForm } from './UserAddForm'; import { useMessages, useModified } from '@/components/hooks'; export function UserAddButton({ onSave }: { onSave?: () => void }) { const { formatMessage, labels, messages } = useMessages(); - const { showToast } = useToasts(); + const { toast } = useToast(); const { touch } = useModified(); const handleSave = () => { - showToast({ message: formatMessage(messages.saved), variant: 'success' }); + toast(formatMessage(messages.saved)); touch('users'); onSave?.(); }; return ( - + - - {(close: () => void) => } + + + {({ close }) => } + - + ); } diff --git a/src/app/(main)/settings/users/UserDeleteButton.tsx b/src/app/(main)/settings/users/UserDeleteButton.tsx index 448f87cf..d40467d2 100644 --- a/src/app/(main)/settings/users/UserDeleteButton.tsx +++ b/src/app/(main)/settings/users/UserDeleteButton.tsx @@ -1,4 +1,4 @@ -import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics'; +import { Button, Icon, Icons, Modal, DialogTrigger, Dialog, Text } from '@umami/react-zen'; import { useMessages, useLogin } from '@/components/hooks'; import { UserDeleteForm } from './UserDeleteForm'; @@ -15,18 +15,20 @@ export function UserDeleteButton({ const { user } = useLogin(); return ( - - - - {(close: () => void) => ( - - )} + + + {({ close }) => ( + + )} + - + ); } diff --git a/src/app/(main)/settings/users/UserDeleteForm.tsx b/src/app/(main)/settings/users/UserDeleteForm.tsx index 6db585cd..4ad54526 100644 --- a/src/app/(main)/settings/users/UserDeleteForm.tsx +++ b/src/app/(main)/settings/users/UserDeleteForm.tsx @@ -1,3 +1,4 @@ +import { useToast } from '@umami/react-zen'; import { useApi, useMessages, useModified } from '@/components/hooks'; import { ConfirmationForm } from '@/components/common/ConfirmationForm'; @@ -6,11 +7,13 @@ export function UserDeleteForm({ userId, username, onSave, onClose }) { const { del, useMutation } = useApi(); const { mutate, error, isPending } = useMutation({ mutationFn: () => del(`/users/${userId}`) }); const { touch } = useModified(); + const { toast } = useToast(); const handleConfirm = async () => { mutate(null, { onSuccess: async () => { touch('users'); + toast(formatMessage(messages.successMessage)); onSave?.(); onClose?.(); }, @@ -23,6 +26,7 @@ export function UserDeleteForm({ userId, username, onSave, onClose }) { onConfirm={handleConfirm} onClose={onClose} buttonLabel={formatMessage(labels.delete)} + buttonVariant="danger" isLoading={isPending} error={error} /> diff --git a/src/app/(main)/settings/users/UsersDataTable.tsx b/src/app/(main)/settings/users/UsersDataTable.tsx index 0d3c9df6..86b4df27 100644 --- a/src/app/(main)/settings/users/UsersDataTable.tsx +++ b/src/app/(main)/settings/users/UsersDataTable.tsx @@ -1,4 +1,4 @@ -import { DataTable } from '@/components/common/DataTable'; +import { DataGrid } from '@/components/common/DataGrid'; import { useUsers } from '@/components/hooks'; import { UsersTable } from './UsersTable'; import { ReactNode } from 'react'; @@ -13,8 +13,8 @@ export function UsersDataTable({ const queryResult = useUsers(); return ( - children}> + children}> {({ data }) => } - + ); } diff --git a/src/app/(main)/settings/users/UsersTable.tsx b/src/app/(main)/settings/users/UsersTable.tsx index 1e5aa1d3..dcfc74c8 100644 --- a/src/app/(main)/settings/users/UsersTable.tsx +++ b/src/app/(main)/settings/users/UsersTable.tsx @@ -1,9 +1,9 @@ -import { Text, Icon, Icons, GridTable, GridColumn } from 'react-basics'; +import { Row, Button, Text, Icon, Icons, DataTable, DataColumn } from '@umami/react-zen'; +import Link from 'next/link'; import { formatDistance } from 'date-fns'; import { ROLES } from '@/lib/constants'; import { useMessages, useLocale } from '@/components/hooks'; import { UserDeleteButton } from './UserDeleteButton'; -import { LinkButton } from '@/components/common/LinkButton'; export function UsersTable({ data = [], @@ -16,44 +16,46 @@ export function UsersTable({ const { dateLocale } = useLocale(); return ( - - - - {row => + + + + {(row: any) => formatMessage( labels[Object.keys(ROLES).find(key => ROLES[key] === row.role)] || labels.unknown, ) } - - - {row => + + + {(row: any) => formatDistance(new Date(row.createdAt), new Date(), { addSuffix: true, locale: dateLocale, }) } - - - {row => row._count.website} - + + + {(row: any) => row._count.websiteUser} + {showActions && ( - - {row => { + + {(row: any) => { const { id, username } = row; return ( - <> + - - - - - {formatMessage(labels.edit)} - - + + ); }} - + )} - + ); } diff --git a/src/app/(main)/settings/users/[userId]/UserEditForm.tsx b/src/app/(main)/settings/users/[userId]/UserEditForm.tsx index a10a5f4b..94a6acd6 100644 --- a/src/app/(main)/settings/users/[userId]/UserEditForm.tsx +++ b/src/app/(main)/settings/users/[userId]/UserEditForm.tsx @@ -1,22 +1,27 @@ import { - Dropdown, - Item, + Select, + ListItem, Form, - FormRow, + FormField, FormButtons, - FormInput, TextField, - SubmitButton, + FormSubmitButton, PasswordField, -} from 'react-basics'; -import { useApi, useLogin, useMessages } from '@/components/hooks'; + useToast, +} from '@umami/react-zen'; +import { useApi, useLogin, useMessages, useModified } from '@/components/hooks'; import { ROLES } from '@/lib/constants'; -import { useContext, useRef } from 'react'; +import { useContext } from 'react'; import { UserContext } from './UserProvider'; export function UserEditForm({ userId, onSave }: { userId: string; onSave?: () => void }) { - const { formatMessage, labels, messages } = useMessages(); + const { formatMessage, labels, messages, getMessage } = useMessages(); const { post, useMutation } = useApi(); + const user = useContext(UserContext); + const { user: login } = useLogin(); + const { toast } = useToast(); + const { touch } = useModified(); + const { mutate, error } = useMutation({ mutationFn: ({ username, @@ -28,61 +33,47 @@ export function UserEditForm({ userId, onSave }: { userId: string; onSave?: () = role: string; }) => post(`/users/${userId}`, { username, password, role }), }); - const ref = useRef(null); - const user = useContext(UserContext); - const { user: login } = useLogin(); const handleSubmit = async (data: any) => { mutate(data, { onSuccess: async () => { - ref.current.reset(data); + toast(formatMessage(messages.saved)); + touch(`user:${user.id}`); onSave?.(); }, }); }; - const renderValue = (value: string) => { - if (value === ROLES.user) { - return formatMessage(labels.user); - } - if (value === ROLES.admin) { - return formatMessage(labels.admin); - } - if (value === ROLES.viewOnly) { - return formatMessage(labels.viewOnly); - } - }; - return ( -
- - - - - - - - - - + + + + + + + + {user.id !== login.id && ( - - - - {formatMessage(labels.viewOnly)} - {formatMessage(labels.user)} - {formatMessage(labels.admin)} - - - + + + )} - {formatMessage(labels.save)} + {formatMessage(labels.save)}
); diff --git a/src/app/(main)/settings/users/[userId]/UserPage.tsx b/src/app/(main)/settings/users/[userId]/UserPage.tsx index ab784dc9..56812c0c 100644 --- a/src/app/(main)/settings/users/[userId]/UserPage.tsx +++ b/src/app/(main)/settings/users/[userId]/UserPage.tsx @@ -2,7 +2,7 @@ import { UserSettings } from './UserSettings'; import { UserProvider } from './UserProvider'; -export default function ({ userId }: { userId: string }) { +export function UserPage({ userId }: { userId: string }) { return ( diff --git a/src/app/(main)/settings/users/[userId]/UserProvider.tsx b/src/app/(main)/settings/users/[userId]/UserProvider.tsx index 58ecc36c..0b87e63a 100644 --- a/src/app/(main)/settings/users/[userId]/UserProvider.tsx +++ b/src/app/(main)/settings/users/[userId]/UserProvider.tsx @@ -1,6 +1,6 @@ import { createContext, ReactNode, useEffect } from 'react'; +import { Loading } from '@umami/react-zen'; import { useModified, useUser } from '@/components/hooks'; -import { Loading } from 'react-basics'; export const UserContext = createContext(null); @@ -18,5 +18,5 @@ export function UserProvider({ userId, children }: { userId: string; children: R return ; } - return {children}; + return {children}; } diff --git a/src/app/(main)/settings/users/[userId]/UserSettings.tsx b/src/app/(main)/settings/users/[userId]/UserSettings.tsx index 9af656b8..505e8ea4 100644 --- a/src/app/(main)/settings/users/[userId]/UserSettings.tsx +++ b/src/app/(main)/settings/users/[userId]/UserSettings.tsx @@ -1,46 +1,31 @@ -import { Key, useContext, useState } from 'react'; -import { Item, Tabs, useToasts } from 'react-basics'; +import { useContext } from 'react'; +import { Tabs, Tab, TabList, TabPanel } from '@umami/react-zen'; import { Icons } from '@/components/icons'; import { UserEditForm } from './UserEditForm'; import { PageHeader } from '@/components/layout/PageHeader'; import { useMessages } from '@/components/hooks'; import { UserWebsites } from './UserWebsites'; import { UserContext } from './UserProvider'; -import { Breadcrumb } from '@/components/common/Breadcrumb'; export function UserSettings({ userId }: { userId: string }) { - const { formatMessage, labels, messages } = useMessages(); - const [tab, setTab] = useState('details'); + const { formatMessage, labels } = useMessages(); const user = useContext(UserContext); - const { showToast } = useToasts(); - - const handleSave = () => { - showToast({ message: formatMessage(messages.saved), variant: 'success' }); - }; - - const breadcrumb = ( - - ); return ( <> - } breadcrumb={breadcrumb} /> - - {formatMessage(labels.details)} - {formatMessage(labels.websites)} + } /> + + + {formatMessage(labels.details)} + {formatMessage(labels.websites)} + + + + + + + - {tab === 'details' && } - {tab === 'websites' && } ); } diff --git a/src/app/(main)/settings/users/[userId]/UserWebsites.tsx b/src/app/(main)/settings/users/[userId]/UserWebsites.tsx index d53a50f2..023b3420 100644 --- a/src/app/(main)/settings/users/[userId]/UserWebsites.tsx +++ b/src/app/(main)/settings/users/[userId]/UserWebsites.tsx @@ -1,15 +1,15 @@ import { WebsitesTable } from '@/app/(main)/settings/websites/WebsitesTable'; -import { DataTable } from '@/components/common/DataTable'; +import { DataGrid } from '@/components/common/DataGrid'; import { useWebsites } from '@/components/hooks'; export function UserWebsites({ userId }) { const queryResult = useWebsites({ userId }); return ( - + {({ data }) => ( )} - + ); } diff --git a/src/app/(main)/settings/users/[userId]/page.tsx b/src/app/(main)/settings/users/[userId]/page.tsx index b410643c..47bd3531 100644 --- a/src/app/(main)/settings/users/[userId]/page.tsx +++ b/src/app/(main)/settings/users/[userId]/page.tsx @@ -1,7 +1,7 @@ import { UserPage } from './UserPage'; import { Metadata } from 'next'; -export default async function ({ params }: { params: { userId: string } }) { +export default async function ({ params }: { params: Promise<{ userId: string }> }) { const { userId } = await params; return ; diff --git a/src/app/(main)/settings/websites/WebsitesDataTable.tsx b/src/app/(main)/settings/websites/WebsitesDataTable.tsx index f62bb9e6..48bb0922 100644 --- a/src/app/(main)/settings/websites/WebsitesDataTable.tsx +++ b/src/app/(main)/settings/websites/WebsitesDataTable.tsx @@ -1,6 +1,6 @@ import { ReactNode } from 'react'; import { WebsitesTable } from '@/app/(main)/settings/websites/WebsitesTable'; -import { DataTable } from '@/components/common/DataTable'; +import { DataGrid } from '@/components/common/DataGrid'; import { useWebsites } from '@/components/hooks'; export function WebsitesDataTable({ @@ -19,7 +19,7 @@ export function WebsitesDataTable({ const queryResult = useWebsites({ teamId }); return ( - children}> + children}> {({ data }) => ( )} - + ); } diff --git a/src/app/(main)/settings/websites/WebsitesSettingsPage.tsx b/src/app/(main)/settings/websites/WebsitesSettingsPage.tsx index 6965f4fe..a5bdfe30 100644 --- a/src/app/(main)/settings/websites/WebsitesSettingsPage.tsx +++ b/src/app/(main)/settings/websites/WebsitesSettingsPage.tsx @@ -10,7 +10,7 @@ export function WebsitesSettingsPage({ teamId }: { teamId: string }) { return ( <> - + ); diff --git a/src/app/(main)/settings/websites/WebsitesTable.tsx b/src/app/(main)/settings/websites/WebsitesTable.tsx index 0d9ea11b..6211b00b 100644 --- a/src/app/(main)/settings/websites/WebsitesTable.tsx +++ b/src/app/(main)/settings/websites/WebsitesTable.tsx @@ -1,7 +1,7 @@ import { ReactNode } from 'react'; -import { Text, Icon, Icons, GridTable, GridColumn } from 'react-basics'; +import { Row, Text, Icon, Icons, DataTable, DataColumn, Button } from '@umami/react-zen'; +import Link from 'next/link'; import { useMessages, useTeamUrl } from '@/components/hooks'; -import { LinkButton } from '@/components/common/LinkButton'; export interface WebsitesTableProps { data: any[]; @@ -27,37 +27,41 @@ export function WebsitesTable({ } return ( - - - + + + {showActions && ( - - {row => { - const { id: websiteId } = row; + + {(row: any) => { + const websiteId = row.id; return ( - <> + {allowEdit && ( - - - - - {formatMessage(labels.edit)} - + )} {allowView && ( - - - - - {formatMessage(labels.view)} - + )} - + ); }} - + )} - + ); } diff --git a/src/app/(main)/settings/websites/[websiteId]/WebsiteEditForm.tsx b/src/app/(main)/settings/websites/[websiteId]/WebsiteEditForm.tsx index 96006268..8e9c6d7c 100644 --- a/src/app/(main)/settings/websites/[websiteId]/WebsiteEditForm.tsx +++ b/src/app/(main)/settings/websites/[websiteId]/WebsiteEditForm.tsx @@ -1,5 +1,12 @@ -import { useContext, useRef } from 'react'; -import { SubmitButton, Form, FormInput, FormRow, FormButtons, TextField } from 'react-basics'; +import { useContext } from 'react'; +import { + FormSubmitButton, + Form, + FormField, + FormButtons, + TextField, + useToast, +} from '@umami/react-zen'; import { useApi, useMessages, useModified } from '@/components/hooks'; import { DOMAIN_REGEX } from '@/lib/constants'; import { WebsiteContext } from '@/app/(main)/websites/[websiteId]/WebsiteProvider'; @@ -8,16 +15,17 @@ export function WebsiteEditForm({ websiteId, onSave }: { websiteId: string; onSa const website = useContext(WebsiteContext); const { formatMessage, labels, messages } = useMessages(); const { post, useMutation } = useApi(); + const { toast } = useToast(); + const { touch } = useModified(); + const { mutate, error } = useMutation({ mutationFn: (data: any) => post(`/websites/${websiteId}`, data), }); - const ref = useRef(null); - const { touch } = useModified(); const handleSubmit = async (data: any) => { mutate(data, { onSuccess: async () => { - ref.current.reset(data); + toast(formatMessage(messages.saved)); touch(`website:${website.id}`); onSave?.(); }, @@ -25,38 +33,36 @@ export function WebsiteEditForm({ websiteId, onSave }: { websiteId: string; onSa }; return ( -
- - - - - - - - - - - - - + + + + + + + + + + - + {formatMessage(labels.save)} - +
); diff --git a/src/app/(main)/settings/websites/[websiteId]/WebsiteSettings.tsx b/src/app/(main)/settings/websites/[websiteId]/WebsiteSettings.tsx index d4a8d5d3..8cb96182 100644 --- a/src/app/(main)/settings/websites/[websiteId]/WebsiteSettings.tsx +++ b/src/app/(main)/settings/websites/[websiteId]/WebsiteSettings.tsx @@ -1,11 +1,10 @@ +import { useContext } from 'react'; +import { Button, Icon, Tabs, TabList, Tab, TabPanel, Text } from '@umami/react-zen'; +import Link from 'next/link'; import { WebsiteContext } from '@/app/(main)/websites/[websiteId]/WebsiteProvider'; -import { Breadcrumb } from '@/components/common/Breadcrumb'; import { useMessages } from '@/components/hooks'; import { Icons } from '@/components/icons'; import { PageHeader } from '@/components/layout/PageHeader'; -import Link from 'next/link'; -import { Key, useContext, useState } from 'react'; -import { Button, Icon, Item, Tabs, Text, useToasts } from 'react-basics'; import { ShareUrl } from './ShareUrl'; import { TrackingCode } from './TrackingCode'; import { WebsiteData } from './WebsiteData'; @@ -19,31 +18,11 @@ export function WebsiteSettings({ openExternal?: boolean; }) { const website = useContext(WebsiteContext); - const { formatMessage, labels, messages } = useMessages(); - const [tab, setTab] = useState('details'); - const { showToast } = useToasts(); - - const handleSave = () => { - showToast({ message: formatMessage(messages.saved), variant: 'success' }); - }; - - const breadcrumb = ( - - ); + const { formatMessage, labels } = useMessages(); return ( <> - } breadcrumb={breadcrumb}> + }> - - {formatMessage(labels.details)} - {formatMessage(labels.trackingCode)} - {formatMessage(labels.shareUrl)} - {formatMessage(labels.data)} + + + {formatMessage(labels.details)} + {formatMessage(labels.trackingCode)} + {formatMessage(labels.shareUrl)} + {formatMessage(labels.data)} + + + + + + + + + + + + + - {tab === 'details' && } - {tab === 'tracking' && } - {tab === 'share' && } - {tab === 'data' && } ); } diff --git a/src/app/(main)/settings/websites/[websiteId]/page.tsx b/src/app/(main)/settings/websites/[websiteId]/page.tsx index ce83c7ba..a65a5a9f 100644 --- a/src/app/(main)/settings/websites/[websiteId]/page.tsx +++ b/src/app/(main)/settings/websites/[websiteId]/page.tsx @@ -1,7 +1,7 @@ import { WebsiteSettingsPage } from './WebsiteSettingsPage'; import { Metadata } from 'next'; -export default async function ({ params }: { params: { websiteId: string } }) { +export default async function ({ params }: { params: Promise<{ websiteId: string }> }) { const { websiteId } = await params; return ; diff --git a/src/app/(main)/settings/websites/page.tsx b/src/app/(main)/settings/websites/page.tsx index 1200d912..cc8f886a 100644 --- a/src/app/(main)/settings/websites/page.tsx +++ b/src/app/(main)/settings/websites/page.tsx @@ -1,7 +1,7 @@ import { Metadata } from 'next'; import { WebsitesSettingsPage } from './WebsitesSettingsPage'; -export default async function ({ params }: { params: { teamId: string } }) { +export default async function ({ params }: { params: Promise<{ teamId: string }> }) { const { teamId } = await params; return ; diff --git a/src/app/(main)/teams/[teamId]/settings/members/TeamMembersDataTable.tsx b/src/app/(main)/teams/[teamId]/settings/members/TeamMembersDataTable.tsx index 44757349..339c1f3e 100644 --- a/src/app/(main)/teams/[teamId]/settings/members/TeamMembersDataTable.tsx +++ b/src/app/(main)/teams/[teamId]/settings/members/TeamMembersDataTable.tsx @@ -1,4 +1,4 @@ -import { DataTable } from '@/components/common/DataTable'; +import { DataGrid } from '@/components/common/DataGrid'; import { TeamMembersTable } from './TeamMembersTable'; import { useTeamMembers } from '@/components/hooks'; @@ -12,8 +12,8 @@ export function TeamMembersDataTable({ const queryResult = useTeamMembers(teamId); return ( - + {({ data }) => } - + ); } diff --git a/src/app/(main)/teams/[teamId]/settings/team/page.tsx b/src/app/(main)/teams/[teamId]/settings/team/page.tsx index 1d3d4565..4767b1c4 100644 --- a/src/app/(main)/teams/[teamId]/settings/team/page.tsx +++ b/src/app/(main)/teams/[teamId]/settings/team/page.tsx @@ -1,7 +1,7 @@ import { Metadata } from 'next'; import { TeamPage } from './TeamPage'; -export default async function ({ params }: { params: { teamId: string } }) { +export default async function ({ params }: { params: Promise<{ teamId: string }> }) { const { teamId } = await params; return ; diff --git a/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesDataTable.tsx b/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesDataTable.tsx index af2dbf58..68f853ac 100644 --- a/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesDataTable.tsx +++ b/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesDataTable.tsx @@ -1,4 +1,4 @@ -import { DataTable } from '@/components/common/DataTable'; +import { DataGrid } from '@/components/common/DataGrid'; import { useTeamWebsites } from '@/components/hooks'; import { TeamWebsitesTable } from './TeamWebsitesTable'; @@ -12,8 +12,8 @@ export function TeamWebsitesDataTable({ const queryResult = useTeamWebsites(teamId); return ( - + {({ data }) => } - + ); } diff --git a/src/app/(main)/teams/[teamId]/settings/websites/page.tsx b/src/app/(main)/teams/[teamId]/settings/websites/page.tsx index 2ae8e23e..e34c35d5 100644 --- a/src/app/(main)/teams/[teamId]/settings/websites/page.tsx +++ b/src/app/(main)/teams/[teamId]/settings/websites/page.tsx @@ -1,7 +1,7 @@ import { TeamWebsitesPage } from './TeamWebsitesPage'; import { Metadata } from 'next'; -export default async function ({ params }: { params: { teamId: string } }) { +export default async function ({ params }: { params: Promise<{ teamId: string }> }) { const { teamId } = await params; return ; diff --git a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx index adc45f37..7ca072c3 100644 --- a/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx +++ b/src/app/(main)/websites/[websiteId]/WebsiteExpandedView.tsx @@ -1,7 +1,7 @@ import { Dropdown, Icon, Icons, Item, Text } from 'react-basics'; import { LinkButton } from '@/components/common/LinkButton'; import { useLocale, useMessages, useNavigation } from '@/components/hooks'; -import { SideNav } from '@/components/layout/SideNav'; +import { MenuNav } from '@/components/layout/MenuNav'; import { BrowsersTable } from '@/components/metrics/BrowsersTable'; import { CitiesTable } from '@/components/metrics/CitiesTable'; import { CountriesTable } from '@/components/metrics/CountriesTable'; @@ -156,7 +156,7 @@ export function WebsiteExpandedView({ {formatMessage(labels.back)} - + - +
{formatMessage(labels.previous)}
}) { const { websiteId } = await params; return ; diff --git a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx index b81b51cb..d17406c7 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx @@ -1,6 +1,6 @@ import { useWebsiteEvents } from '@/components/hooks'; import { EventsTable } from './EventsTable'; -import { DataTable } from '@/components/common/DataTable'; +import { DataGrid } from '@/components/common/DataGrid'; import { ReactNode } from 'react'; export function EventsDataTable({ @@ -13,8 +13,8 @@ export function EventsDataTable({ const queryResult = useWebsiteEvents(websiteId); return ( - + {({ data }) => } - + ); } diff --git a/src/app/(main)/websites/[websiteId]/events/page.tsx b/src/app/(main)/websites/[websiteId]/events/page.tsx index 23cd5464..2bfc1401 100644 --- a/src/app/(main)/websites/[websiteId]/events/page.tsx +++ b/src/app/(main)/websites/[websiteId]/events/page.tsx @@ -1,7 +1,7 @@ import { Metadata } from 'next'; import { EventsPage } from './EventsPage'; -export default async function ({ params }: { params: { websiteId: string } }) { +export default async function ({ params }: { params: Promise<{ websiteId: string }> }) { const { websiteId } = await params; return ; diff --git a/src/app/(main)/websites/[websiteId]/realtime/page.tsx b/src/app/(main)/websites/[websiteId]/realtime/page.tsx index 09945b7f..e6f82f7d 100644 --- a/src/app/(main)/websites/[websiteId]/realtime/page.tsx +++ b/src/app/(main)/websites/[websiteId]/realtime/page.tsx @@ -1,7 +1,7 @@ import { WebsiteRealtimePage } from './WebsiteRealtimePage'; import { Metadata } from 'next'; -export default async function ({ params }: { params: { websiteId: string } }) { +export default async function ({ params }: { params: Promise<{ websiteId: string }> }) { const { websiteId } = await params; return ; diff --git a/src/app/(main)/websites/[websiteId]/reports/page.tsx b/src/app/(main)/websites/[websiteId]/reports/page.tsx index c307ae05..e760dc9a 100644 --- a/src/app/(main)/websites/[websiteId]/reports/page.tsx +++ b/src/app/(main)/websites/[websiteId]/reports/page.tsx @@ -1,7 +1,7 @@ import { WebsiteReportsPage } from './WebsiteReportsPage'; import { Metadata } from 'next'; -export default async function ({ params }: { params: { websiteId: string } }) { +export default async function ({ params }: { params: Promise<{ websiteId: string }> }) { const { websiteId } = await params; return ; diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx index dd6a98af..6cc7eff4 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx @@ -1,6 +1,6 @@ import { useWebsiteSessions } from '@/components/hooks'; import { SessionsTable } from './SessionsTable'; -import { DataTable } from '@/components/common/DataTable'; +import { DataGrid } from '@/components/common/DataGrid'; import { ReactNode } from 'react'; export function SessionsDataTable({ @@ -14,8 +14,8 @@ export function SessionsDataTable({ const queryResult = useWebsiteSessions(websiteId); return ( - children}> + children}> {({ data }) => } - + ); } diff --git a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/page.tsx b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/page.tsx index a14a70c7..8d85a7c7 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/page.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/[sessionId]/page.tsx @@ -4,7 +4,7 @@ import { Metadata } from 'next'; export default async function WebsitePage({ params, }: { - params: { websiteId: string; sessionId: string }; + params: Promise<{ websiteId: string; sessionId: string }>; }) { const { websiteId, sessionId } = await params; diff --git a/src/app/(main)/websites/[websiteId]/sessions/page.tsx b/src/app/(main)/websites/[websiteId]/sessions/page.tsx index 1012b6d1..7bf8216b 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/page.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/page.tsx @@ -1,7 +1,7 @@ import { SessionsPage } from './SessionsPage'; import { Metadata } from 'next'; -export default async function ({ params }: { params: { websiteId: string } }) { +export default async function ({ params }: { params: Promise<{ websiteId: string }> }) { const { websiteId } = await params; return ; diff --git a/src/app/api/users/[userId]/route.ts b/src/app/api/users/[userId]/route.ts index abb3331d..3dd88aab 100644 --- a/src/app/api/users/[userId]/route.ts +++ b/src/app/api/users/[userId]/route.ts @@ -27,7 +27,7 @@ export async function POST(request: Request, { params }: { params: Promise<{ use const schema = z.object({ username: z.string().max(255), password: z.string().max(255), - role: z.string().regex(/admin|user|view-only/i), + role: z.enum(['admin', 'user', 'view-only']), }); const { auth, body, error } = await parseRequest(request, schema); diff --git a/src/app/login/LoginForm.tsx b/src/app/login/LoginForm.tsx index e71fe6fc..129fd920 100644 --- a/src/app/login/LoginForm.tsx +++ b/src/app/login/LoginForm.tsx @@ -35,15 +35,7 @@ export function LoginForm() { }; return ( - + diff --git a/src/assets/logo.svg b/src/assets/logo.svg index b1395313..384c4f80 100644 --- a/src/assets/logo.svg +++ b/src/assets/logo.svg @@ -1 +1 @@ - \ No newline at end of file + diff --git a/src/components/common/ConfirmationForm.tsx b/src/components/common/ConfirmationForm.tsx index e532e6bb..5924bef3 100644 --- a/src/components/common/ConfirmationForm.tsx +++ b/src/components/common/ConfirmationForm.tsx @@ -1,11 +1,11 @@ import { ReactNode } from 'react'; -import { Button, LoadingButton, Form, FormButtons } from 'react-basics'; +import { Row, Button, FormSubmitButton, Form, FormButtons } from '@umami/react-zen'; import { useMessages } from '@/components/hooks'; export interface ConfirmationFormProps { message: ReactNode; buttonLabel?: ReactNode; - buttonVariant?: 'none' | 'primary' | 'secondary' | 'quiet' | 'danger'; + buttonVariant?: 'primary' | 'quiet' | 'danger'; isLoading?: boolean; error?: string | Error; onConfirm?: () => void; @@ -24,13 +24,13 @@ export function ConfirmationForm({ const { formatMessage, labels } = useMessages(); return ( -
-

{message}

- - + + {message} + + + {buttonLabel || formatMessage(labels.ok)} - - +
); diff --git a/src/components/common/DataTable.module.css b/src/components/common/DataGrid.module.css similarity index 100% rename from src/components/common/DataTable.module.css rename to src/components/common/DataGrid.module.css diff --git a/src/components/common/DataTable.tsx b/src/components/common/DataGrid.tsx similarity index 64% rename from src/components/common/DataTable.tsx rename to src/components/common/DataGrid.tsx index 91f02516..ef652b70 100644 --- a/src/components/common/DataTable.tsx +++ b/src/components/common/DataGrid.tsx @@ -1,12 +1,10 @@ import { ReactNode } from 'react'; -import classNames from 'classnames'; -import { Loading, SearchField } from 'react-basics'; +import { Loading, SearchField, Row, Column } from '@umami/react-zen'; import { useMessages, useNavigation } from '@/components/hooks'; import { Empty } from '@/components/common/Empty'; import { Pager } from '@/components/common/Pager'; -import { PagedQueryResult } from '@/lib/types'; -import styles from './DataTable.module.css'; import { LoadingPanel } from '@/components/common/LoadingPanel'; +import { PagedQueryResult } from '@/lib/types'; const DEFAULT_SEARCH_DELAY = 600; @@ -20,7 +18,7 @@ export interface DataTableProps { children: ReactNode | ((data: any) => ReactNode); } -export function DataTable({ +export function DataGrid({ queryResult, searchDelay = 600, allowSearch = true, @@ -30,12 +28,8 @@ export function DataTable({ children, }: DataTableProps) { const { formatMessage, labels, messages } = useMessages(); - const { - result, - params, - setParams, - query: { error, isLoading, isFetched }, - } = queryResult || {}; + const { result, params, setParams, query } = queryResult || {}; + const { error, isLoading, isFetched } = query || {}; const { page, pageSize, count, data } = result || {}; const { search } = params || {}; const hasData = Boolean(!isLoading && data?.length); @@ -43,45 +37,38 @@ export function DataTable({ const { router, renderUrl } = useNavigation(); const handleSearch = (search: string) => { - setParams({ ...params, search, page: params.page ? page : 1 }); + setParams({ ...params, search }); }; const handlePageChange = (page: number) => { - setParams({ ...params, search, page }); + setParams({ ...params, page }); router.push(renderUrl({ page })); }; return ( <> {allowSearch && (hasData || search) && ( - + + + )} -
+ {hasData ? (typeof children === 'function' ? children(result) : children) : null} {isLoading && } {!isLoading && !hasData && !search && (renderEmpty ? renderEmpty() : )} {!isLoading && noResults && } -
+
{allowPaging && hasData && ( - + + + )} diff --git a/src/components/common/LoadingPanel.tsx b/src/components/common/LoadingPanel.tsx index 0cdbd075..fd42b884 100644 --- a/src/components/common/LoadingPanel.tsx +++ b/src/components/common/LoadingPanel.tsx @@ -1,6 +1,6 @@ import { ReactNode } from 'react'; import classNames from 'classnames'; -import { Loading } from 'react-basics'; +import { Loading } from '@umami/react-zen'; import { ErrorMessage } from '@/components/common/ErrorMessage'; import { Empty } from '@/components/common/Empty'; import styles from './LoadingPanel.module.css'; diff --git a/src/components/common/Pager.tsx b/src/components/common/Pager.tsx index 9301850c..2706c107 100644 --- a/src/components/common/Pager.tsx +++ b/src/components/common/Pager.tsx @@ -1,7 +1,5 @@ -import classNames from 'classnames'; -import { Button, Icon, Icons } from 'react-basics'; +import { Button, Icon, Icons, Row, Text } from '@umami/react-zen'; import { useMessages } from '@/components/hooks'; -import styles from './Pager.module.css'; export interface PagerProps { page: string | number; @@ -11,7 +9,7 @@ export interface PagerProps { className?: string; } -export function Pager({ page, pageSize, count, onPageChange, className }: PagerProps) { +export function Pager({ page, pageSize, count, onPageChange }: PagerProps) { const { formatMessage, labels } = useMessages(); const maxPage = pageSize && count ? Math.ceil(+count / +pageSize) : 0; const lastPage = page === maxPage; @@ -34,24 +32,21 @@ export function Pager({ page, pageSize, count, onPageChange, className }: PagerP } return ( -
-
{formatMessage(labels.numberOfRecords, { x: count })}
-
- -
- {formatMessage(labels.pageOf, { current: page, total: maxPage })} -
- -
-
-
+ + ); } diff --git a/src/components/hooks/useApi.ts b/src/components/hooks/useApi.ts index 17b83e5f..bd46d6c3 100644 --- a/src/components/hooks/useApi.ts +++ b/src/components/hooks/useApi.ts @@ -8,7 +8,7 @@ import { useApp } from '@/store/app'; const selector = (state: { shareToken: { token?: string } }) => state.shareToken; async function handleResponse(res: FetchResponse): Promise { - if (!res.ok) { + if (res.error) { const { message, code } = res?.error?.error || {}; return Promise.reject(new Error(code || message || 'Unexpectd error.')); } diff --git a/src/components/input/LanguageButton.module.css b/src/components/input/LanguageButton.module.css index dccb098d..d73bcb77 100644 --- a/src/components/input/LanguageButton.module.css +++ b/src/components/input/LanguageButton.module.css @@ -24,6 +24,7 @@ } .selected { + color: var(--font-color); font-weight: 700; background: var(--blue100); } diff --git a/src/components/layout/MenuLayout.tsx b/src/components/layout/MenuLayout.tsx index d8840122..493d8e55 100644 --- a/src/components/layout/MenuLayout.tsx +++ b/src/components/layout/MenuLayout.tsx @@ -1,22 +1,18 @@ import { ReactNode } from 'react'; -import { usePathname } from 'next/navigation'; -import { SideNav } from '@/components/layout/SideNav'; -import styles from './MenuLayout.module.css'; +import { Grid, Column } from '@umami/react-zen'; +import { MenuNav } from '@/components/layout/MenuNav'; export function MenuLayout({ items = [], children }: { items: any[]; children: ReactNode }) { - const pathname = usePathname(); const cloudMode = !!process.env.cloudMode; - const getKey = () => items.find(({ url }) => pathname === url)?.key; - return ( -
+ {!cloudMode && ( -
- -
+ + + )} -
{children}
-
+ {children} + ); } diff --git a/src/components/layout/SideNav.module.css b/src/components/layout/MenuNav.module.css similarity index 100% rename from src/components/layout/SideNav.module.css rename to src/components/layout/MenuNav.module.css diff --git a/src/components/layout/MenuNav.tsx b/src/components/layout/MenuNav.tsx new file mode 100644 index 00000000..82e697cf --- /dev/null +++ b/src/components/layout/MenuNav.tsx @@ -0,0 +1,29 @@ +import { List, ListItem, Text } from '@umami/react-zen'; +import { usePathname } from 'next/navigation'; +import Link from 'next/link'; + +export interface SideNavProps { + items: any[]; + shallow?: boolean; + scroll?: boolean; +} + +export function MenuNav({ items, shallow = true, scroll = false }: SideNavProps) { + const pathname = usePathname(); + + return ( + + {items.map(({ key, label, url }) => { + const isSelected = pathname.startsWith(url); + + return ( + + + {label} + + + ); + })} + + ); +} diff --git a/src/components/layout/NavGroup.module.css b/src/components/layout/NavGroup.module.css deleted file mode 100644 index 4979210a..00000000 --- a/src/components/layout/NavGroup.module.css +++ /dev/null @@ -1,80 +0,0 @@ -.group { - display: flex; - flex-direction: column; - width: 100%; -} - -.header { - display: flex; - align-items: center; - justify-content: space-between; - color: var(--base600); - font-size: 11px; - font-weight: 600; - padding: 10px 20px; - text-transform: uppercase; - cursor: pointer; -} - -.body { - display: none; -} - -.expanded .body { - display: block; -} - -.item { - position: relative; - display: flex; - flex-direction: row; - align-items: center; - border-inline-end: 2px solid var(--base200); - padding: 1rem 2rem; - gap: var(--size500); - font-weight: 600; - width: 200px; - margin-inline-end: -2px; -} - -a.item { - color: var(--base700); -} - -.item.selected { - color: var(--base900); - border-inline-end-color: var(--primary400); - background: var(--blue100); -} - -.item:hover { - color: var(--base900); -} - -.minimized .text, -.minimized .header { - display: none; -} - -.minimized .item { - width: 60px; - padding: 20px; - display: flex; - align-items: center; - justify-content: center; -} - -.divider:before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - margin: auto; - border-top: 1px solid var(--base300); - width: 160px; -} - -.minimized .divider:before { - width: 60px; -} diff --git a/src/components/layout/NavGroup.tsx b/src/components/layout/NavGroup.tsx deleted file mode 100644 index c5a25893..00000000 --- a/src/components/layout/NavGroup.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useState } from 'react'; -import { Icon, Text, TooltipPopup } from 'react-basics'; -import classNames from 'classnames'; -import { usePathname } from 'next/navigation'; -import Link from 'next/link'; -import { Icons } from '@/components/icons'; -import styles from './NavGroup.module.css'; - -export interface NavGroupProps { - title: string; - items: any[]; - defaultExpanded?: boolean; - allowExpand?: boolean; - minimized?: boolean; -} - -export function NavGroup({ - title, - items, - defaultExpanded = true, - allowExpand = true, - minimized = false, -}: NavGroupProps) { - const pathname = usePathname(); - const [expanded, setExpanded] = useState(defaultExpanded); - - const handleExpand = () => setExpanded(state => !state); - - return ( -
- {title && ( -
- {title} - - - -
- )} -
- {items.map(({ label, url, icon, divider }) => { - return ( - - - {icon} - {label} - - - ); - })} -
-
- ); -} diff --git a/src/components/layout/PageHeader.tsx b/src/components/layout/PageHeader.tsx index db1608cd..cbbe12aa 100644 --- a/src/components/layout/PageHeader.tsx +++ b/src/components/layout/PageHeader.tsx @@ -1,29 +1,23 @@ import { ReactNode } from 'react'; -import { Heading, Icon, Breadcrumbs, Breadcrumb, Row } from '@umami/react-zen'; +import { Heading, Icon, Row } from '@umami/react-zen'; export function PageHeader({ title, icon, - breadcrumb, children, }: { title?: ReactNode; icon?: ReactNode; className?: string; - breadcrumb?: ReactNode; children?: ReactNode; }) { return ( - <> - - {breadcrumb} - - + + {icon && {icon}} - {title && {title}} - {children} - + {children} + ); } diff --git a/src/components/layout/SideNav.tsx b/src/components/layout/SideNav.tsx deleted file mode 100644 index 4be70c2f..00000000 --- a/src/components/layout/SideNav.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import classNames from 'classnames'; -import { Menu, Item } from 'react-basics'; -import { usePathname } from 'next/navigation'; -import Link from 'next/link'; -import styles from './SideNav.module.css'; - -export interface SideNavProps { - selectedKey: string; - items: any[]; - shallow?: boolean; - scroll?: boolean; - className?: string; - onSelect?: () => void; -} - -export function SideNav({ - selectedKey, - items, - shallow = true, - scroll = false, - className, - onSelect = () => {}, -}: SideNavProps) { - const pathname = usePathname(); - return ( - - {({ key, label, url }) => ( - - - {label} - - - )} - - ); -} diff --git a/src/index.ts b/src/index.ts index e7b0e6c6..8cc8bcbc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,7 +44,7 @@ export * from '@/app/(main)/teams/[teamId]/TeamProvider'; export * from '@/app/(main)/websites/[websiteId]/WebsiteProvider'; export * from '@/components/common/ConfirmationForm'; -export * from '@/components/common/DataTable'; +export * from '@/components/common/DataGrid'; export * from '@/components/common/Empty'; export * from '@/components/common/ErrorBoundary'; export * from '@/components/common/ErrorMessage'; @@ -59,6 +59,5 @@ export * from '@/components/common/Pager'; export * from '@/components/common/TypeConfirmationForm'; export * from '@/components/input/TeamsButton'; -export * from '@/components/input/ThemeButton'; export { ROLES } from '@/lib/constants'; diff --git a/src/lib/fetch.ts b/src/lib/fetch.ts index ee9e160f..37056899 100644 --- a/src/lib/fetch.ts +++ b/src/lib/fetch.ts @@ -1,10 +1,18 @@ import { buildUrl } from '@/lib/url'; +export interface ErrorResponse { + error: { + status: number; + message: string; + code?: string; + }; +} + export interface FetchResponse { ok: boolean; status: number; data?: any; - error?: any; + error?: ErrorResponse; } export async function request( diff --git a/src/styles/global.css b/src/styles/global.css index 5639ecb2..aeadce51 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -4,6 +4,7 @@ body { color: var(--font-color); font-size: var(--font-size); background-color: var(--background-color); + width: 100%; min-height: 100vh; } diff --git a/src/styles/variables.css b/src/styles/variables.css index 7852d78b..f7ebb028 100644 --- a/src/styles/variables.css +++ b/src/styles/variables.css @@ -1,3 +1,4 @@ html body { --primary-color: #147af3; + --primary-font-color: var(--light-color); }