Fixed editing and navigation issues.

This commit is contained in:
Mike Cao 2025-07-13 00:37:43 -07:00
parent bf6c9395c6
commit 8c26e310f7
52 changed files with 118 additions and 122 deletions

View file

@ -4,7 +4,7 @@ import Script from 'next/script';
import { usePathname } from 'next/navigation';
import { UpdateNotice } from './UpdateNotice';
import { SideNav } from '@/app/(main)/SideNav';
import { MenuBar } from '@/app/(main)/MenuBar';
import { TopNav } from '@/app/(main)/TopNav';
import { useLoginQuery, useConfig } from '@/components/hooks';
export function App({ children }) {
@ -35,7 +35,7 @@ export function App({ children }) {
<SideNav />
</Column>
<Row gridColumn="2 / 3" gridRow="1 / 2">
<MenuBar />
<TopNav />
</Row>
<Column
gridColumn="2 / 3"

View file

@ -6,11 +6,10 @@ import { WebsiteSelect } from '@/components/input/WebsiteSelect';
import { PanelLeft, Slash } from '@/components/icons';
import { useNavigation, useGlobalState } from '@/components/hooks';
export function MenuBar() {
export function TopNav() {
const [isCollapsed, setCollapsed] = useGlobalState('sidenav-collapsed');
const { teamId, websiteId } = useNavigation();
const handleSelect = () => {};
const { teamId, websiteId, pathname } = useNavigation();
const isSettings = pathname.includes('/settings');
return (
<Row
@ -30,17 +29,12 @@ export function MenuBar() {
</Button>
<Row alignItems="center" gap="1">
<TeamsButton />
{websiteId && (
{websiteId && !isSettings && (
<>
<Icon strokeColor="7" rotate={-25}>
<Slash />
</Icon>
<WebsiteSelect
variant="quiet"
websiteId={websiteId}
teamId={teamId}
onSelect={handleSelect}
/>
<WebsiteSelect variant="quiet" websiteId={websiteId} teamId={teamId} />
</>
)}
</Row>

View file

@ -21,6 +21,7 @@ export function UserDeleteForm({
mutate(null, {
onSuccess: async () => {
touch('users');
touch(`users:${userId}`);
onSave?.();
onClose?.();
},
@ -35,9 +36,7 @@ export function UserDeleteForm({
confirmLabel={formatMessage(labels.delete)}
isDanger
>
<Row gap="1">
{formatMessage(messages.confirmDelete, { target: <b key={username}>{username}</b> })}
</Row>
<Row gap="1">{formatMessage(messages.confirmDelete, { target: username })}</Row>
</AlertDialog>
);
}

View file

@ -1,15 +1,16 @@
import { useState } from 'react';
import Link from 'next/link';
import { Row, Text, Icon, DataTable, DataColumn, MenuItem, Modal } from '@umami/react-zen';
import { Row, Text, Icon, DataTable, DataColumn, MenuItem, Modal, Dialog } from '@umami/react-zen';
import { Trash, Users } from '@/components/icons';
import { useMessages } from '@/components/hooks';
import { Edit } from '@/components/icons';
import { MenuButton } from '@/components/input/MenuButton';
import { DateDistance } from '@/components/common/DateDistance';
import { WebsiteDeleteForm } from '@/app/(main)/settings/websites/[websiteId]/WebsiteDeleteForm';
export function AdminWebsitesTable({ data = [] }: { data: any[] }) {
const { formatMessage, labels } = useMessages();
const [deleteUser, setDeleteUser] = useState(null);
const [deleteWebsite, setDeleteWebsite] = useState(null);
return (
<>
@ -64,7 +65,7 @@ export function AdminWebsitesTable({ data = [] }: { data: any[] }) {
</MenuItem>
<MenuItem
id="delete"
onAction={() => setDeleteUser(row)}
onAction={() => setDeleteWebsite(id)}
data-test="link-button-delete"
>
<Row alignItems="center" gap>
@ -79,7 +80,11 @@ export function AdminWebsitesTable({ data = [] }: { data: any[] }) {
}}
</DataColumn>
</DataTable>
<Modal isOpen={!!deleteUser}></Modal>
<Modal isOpen={!!deleteWebsite}>
<Dialog style={{ width: 400 }}>
<WebsiteDeleteForm websiteId={deleteWebsite} onClose={() => setDeleteWebsite(null)} />
</Dialog>
</Modal>
</>
);
}

View file

@ -42,7 +42,7 @@ export function SettingsLayout({ children }: { children: ReactNode }) {
<SideMenu items={items} selectedKey={value} />
</Column>
<Column>
<Panel>{children}</Panel>
<Panel minHeight="300px">{children}</Panel>
</Column>
</Grid>
</Column>

View file

@ -1,7 +1,7 @@
import { useContext, useState } from 'react';
import { Column, Tabs, TabList, Tab, TabPanel } from '@umami/react-zen';
import { TeamContext } from '@/app/(main)/teams/[teamId]/TeamProvider';
import { useLoginQuery, useMessages } from '@/components/hooks';
import { useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
import { SectionHeader } from '@/components/common/SectionHeader';
import { ROLES } from '@/lib/constants';
import { Users } from '@/components/icons';
@ -15,7 +15,10 @@ export function TeamDetails({ teamId }: { teamId: string }) {
const team = useContext(TeamContext);
const { formatMessage, labels } = useMessages();
const { user } = useLoginQuery();
const [tab, setTab] = useState('details');
const { query, pathname } = useNavigation();
const [tab, setTab] = useState(query?.tab || 'details');
const isAdmin = pathname.includes('/admin');
const isTeamOwner =
!!team?.teamUser?.find(({ userId, role }) => role === ROLES.teamOwner && userId === user.id) &&
@ -32,7 +35,7 @@ export function TeamDetails({ teamId }: { teamId: string }) {
return (
<Column gap>
<SectionHeader title={team?.name} icon={<Users />}>
{!isTeamOwner && <TeamLeaveButton teamId={team.id} teamName={team.name} />}
{!isTeamOwner && !isAdmin && <TeamLeaveButton teamId={team.id} teamName={team.name} />}
</SectionHeader>
<Tabs selectedKey={tab} onSelectionChange={(value: any) => setTab(value)}>
<TabList>

View file

@ -33,6 +33,10 @@ export function TeamMembersTable({
{allowEdit && (
<DataColumn id="action" align="end">
{(row: any) => {
if (row?.role === ROLES.teamOwner) {
return null;
}
return (
<Row alignItems="center">
<TeamMemberEditButton teamId={teamId} userId={row?.user?.id} role={row?.role} />

View file

@ -57,7 +57,7 @@ export function WebsitesTable({
</MenuItem>
)}
{allowEdit && (
<MenuItem href={renderUrl(`/settings/websites/${websiteId}`)}>
<MenuItem href={`/settings/websites/${websiteId}`}>
<Row alignItems="center" gap>
<Icon data-test="link-button-edit">
<SquarePen />

View file

@ -1,4 +1,4 @@
import { useApi, useMessages } from '@/components/hooks';
import { useApi, useMessages, useModified } from '@/components/hooks';
import { TypeConfirmationForm } from '@/components/common/TypeConfirmationForm';
const CONFIRM_VALUE = 'DELETE';
@ -17,10 +17,13 @@ export function WebsiteDeleteForm({
const { mutate, isPending, error } = useMutation({
mutationFn: () => del(`/websites/${websiteId}`),
});
const { touch } = useModified();
const handleConfirm = async () => {
mutate(null, {
onSuccess: async () => {
touch('websites');
touch(`websites:${websiteId}`);
onSave?.();
onClose?.();
},

View file

@ -73,7 +73,7 @@ export function WebsiteShareForm({ websiteId, shareId, onSave, onClose }: Websit
<Row>
{id && <Button onPress={handleGenerate}>{formatMessage(labels.regenerate)}</Button>}
</Row>
<Row>
<Row alignItems="center" gap>
{onClose && <Button onPress={onClose}>{formatMessage(labels.cancel)}</Button>}
<FormSubmitButton isDisabled={false} isLoading={isPending}>
{formatMessage(labels.save)}

View file

@ -18,7 +18,7 @@ export function WebsitesPage() {
<WebsiteAddButton teamId={teamId} />
</PageHeader>
<Panel>
<WebsitesDataTable teamId={teamId} allowEdit={false} />
<WebsitesDataTable teamId={teamId} />
</Panel>
</Column>
</PageBody>

View file

@ -5,12 +5,11 @@ 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, useNavigation } from '@/components/hooks';
import { useMessages } from '@/components/hooks';
import { LinkButton } from '@/components/common/LinkButton';
export function WebsiteHeader() {
const website = useWebsite();
const { renderUrl } = useNavigation();
return (
<PageHeader title={website.name} icon={<Favicon domain={website.domain} />} showBorder={false}>
@ -18,7 +17,7 @@ export function WebsiteHeader() {
<ActiveUsers websiteId={website.id} />
<Row alignItems="center" gap>
<ShareButton websiteId={website.id} shareId={website.shareId} />
<LinkButton href={renderUrl(`/settings/websites/${website.id}`)}>
<LinkButton href={`/settings/websites/${website.id}`}>
<Icon>
<Edit />
</Icon>
@ -42,7 +41,7 @@ const ShareButton = ({ websiteId, shareId }) => {
<Text>Share</Text>
</Button>
<Modal>
<Dialog title={formatMessage(labels.share)} style={{ width: 400 }}>
<Dialog title={formatMessage(labels.share)} style={{ width: 600 }}>
{({ close }) => {
return <WebsiteShareForm websiteId={websiteId} shareId={shareId} onClose={close} />;
}}