diff --git a/web/src/components/PageStub.tsx b/web/src/components/PageStub.tsx deleted file mode 100644 index 6ce21ba..0000000 --- a/web/src/components/PageStub.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { ReactNode } from 'react' - -/** Placeholder page scaffolding used until each feature issue fills these in. */ -export function PageStub({ title, children }: { title: string; children?: ReactNode }) { - return ( -
-

{title}

-

- {children ?? 'Placeholder — this page arrives in a later issue.'} -

-
- ) -} diff --git a/web/src/components/gardens/DeleteGardenModal.tsx b/web/src/components/gardens/DeleteGardenModal.tsx index 195552b..c187c89 100644 --- a/web/src/components/gardens/DeleteGardenModal.tsx +++ b/web/src/components/gardens/DeleteGardenModal.tsx @@ -1,42 +1,22 @@ -import { useState } from 'react' -import { Modal } from '@/components/ui/Modal' -import { Alert } from '@/components/ui/Alert' -import { Button } from '@/components/ui/Button' -import { errorMessage } from '@/lib/api' +import { ConfirmModal } from '@/components/ui/ConfirmModal' import { useDeleteGarden, type Garden } from '@/lib/gardens' /** Confirmation dialog for deleting a garden (and everything in it). */ export function DeleteGardenModal({ garden, onClose }: { garden: Garden; onClose: () => void }) { const deletion = useDeleteGarden() - const [error, setError] = useState(null) - - async function onConfirm() { - setError(null) - try { - await deletion.mutateAsync(garden.id) - onClose() - } catch (err) { - setError(errorMessage(err, 'Could not delete the garden.')) - } - } - return ( - -
-

- Delete {garden.name} and everything planned in it? - This can't be undone. -

- {error && {error}} -
- - -
-
-
+ deletion.mutateAsync(garden.id)} + onClose={onClose} + > +

+ Delete {garden.name} and everything planned in it? + This can't be undone. +

+
) } diff --git a/web/src/components/gardens/LeaveGardenModal.tsx b/web/src/components/gardens/LeaveGardenModal.tsx index 85c9dce..5ccb540 100644 --- a/web/src/components/gardens/LeaveGardenModal.tsx +++ b/web/src/components/gardens/LeaveGardenModal.tsx @@ -1,8 +1,4 @@ -import { useState } from 'react' -import { Modal } from '@/components/ui/Modal' -import { Alert } from '@/components/ui/Alert' -import { Button } from '@/components/ui/Button' -import { errorMessage } from '@/lib/api' +import { ConfirmModal } from '@/components/ui/ConfirmModal' import { useMe } from '@/lib/auth' import type { Garden } from '@/lib/gardens' import { useRemoveShare } from '@/lib/shares' @@ -12,36 +8,26 @@ import { useRemoveShare } from '@/lib/shares' export function LeaveGardenModal({ garden, onClose }: { garden: Garden; onClose: () => void }) { const me = useMe() const remove = useRemoveShare(garden.id) - const [error, setError] = useState(null) - - async function onConfirm() { - if (!me.data) return - setError(null) - try { - await remove.mutateAsync(me.data.id) - onClose() - } catch (err) { - setError(errorMessage(err, 'Could not leave the garden.')) - } - } - return ( - -
-

- Leave {garden.name}? You'll lose access until the owner - shares it with you again. -

- {error && {error}} -
- - -
-
-
+ { + // The button is disabled without a current user; throw rather than + // silently resolve (which would close the dialog as if it had worked) if + // that guard ever drifts. + if (!me.data) throw new Error('Not signed in.') + await remove.mutateAsync(me.data.id) + }} + onClose={onClose} + > +

+ Leave {garden.name}? You'll lose access until the owner + shares it with you again. +

+
) } diff --git a/web/src/components/layout/AppShell.tsx b/web/src/components/layout/AppShell.tsx index 7e525df..76973e9 100644 --- a/web/src/components/layout/AppShell.tsx +++ b/web/src/components/layout/AppShell.tsx @@ -39,14 +39,19 @@ export function AppShell() { // so the '/gardens' list itself still shows the bar. const inEditor = !!matchRoute({ to: '/gardens/$gardenId', fuzzy: false }) const inPublicGarden = !!matchRoute({ to: '/g/$token', fuzzy: false }) - const showBottomNav = !!user && !inEditor && !inPublicGarden + // A canvas route wants the whole width — the garden editor is squeezed by the + // max-w-5xl reading measure the other pages use (#107). + const canvasRoute = inEditor || inPublicGarden + const showBottomNav = !!user && !canvasRoute const visibleSections = sections.filter((s) => !s.adminOnly || user?.isAdmin) return (
-