Files
pansy/web/src/pages/PublicGardenPage.tsx
T
steveandClaude Opus 4.8 757ac7394d
Build image / build-and-push (push) Successful in 16s
Address Gadfly findings on #85
- PublicGardenPage had the same 100vh mobile bug I fixed in the editor — 100dvh
  there too, so the fix is consistent across both full-height pages.
- Cap the toast stack at 4 (drop oldest). Now that error toasts don't auto-
  dismiss, a burst of failures could otherwise grow the stack unbounded and push
  the newest — the one that just happened — off-screen.
- Hoist the From/To date-input styling to a shared dateInputClass const so the
  two don't drift.

Not taken: reusing safeRedirectPath for the 401 redirect (it validates an
incoming redirect param, it doesn't build the outgoing one — the two uses don't
overlap), and refactoring the three-branch filter spread (it's clear at three
fields; a helper earns its keep when there are more).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
2026-07-21 23:53:18 -04:00

75 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useEffect, useMemo } from 'react'
import { getRouteApi } from '@tanstack/react-router'
import { Alert } from '@/components/ui/Alert'
import { GardenCanvas } from '@/editor/GardenCanvas'
import { useEditorStore } from '@/editor/store'
import type { EditorGarden } from '@/editor/types'
import { toEditorObject } from '@/lib/objects'
import { toEditorPlanting } from '@/lib/plantings'
import { usePublicGarden } from '@/lib/publicGarden'
import { usePageTitle } from '@/lib/usePageTitle'
const routeApi = getRouteApi('/g/$token')
/**
* The public, read-only garden view behind a share token. Rendered on the
* unauthenticated `/g/$token` route (no auth guard), so a logged-out visitor
* never hits /login or OIDC. Reuses GardenCanvas with canEdit=false — pan/zoom
* to explore, but no editing chrome.
*/
export function PublicGardenPage() {
const { token } = routeApi.useParams()
const full = usePublicGarden(token)
usePageTitle(full.data?.garden.name ?? 'Shared garden')
// Start from a clean canvas: if a logged-in user reaches here from their own
// editor, stale focus/selection state would otherwise dim or highlight things.
useEffect(() => {
useEditorStore.getState().resetTransient()
}, [token])
const objects = useMemo(() => full.data?.objects.map(toEditorObject) ?? [], [full.data?.objects])
const plantings = useMemo(() => full.data?.plantings.map(toEditorPlanting) ?? [], [full.data?.plantings])
const plants = useMemo(() => full.data?.plants ?? [], [full.data?.plants])
const plantsById = useMemo(() => new Map(plants.map((p) => [p.id, p])), [plants])
if (full.isPending) return <p className="p-6 text-sm text-muted">Loading garden</p>
if (full.isError)
return (
<div className="p-6">
<Alert>This shared link isnt available. The owner may have turned it off or changed it.</Alert>
</div>
)
const g = full.data.garden
const garden: EditorGarden = {
id: g.id,
name: g.name,
widthCm: g.widthCm,
heightCm: g.heightCm,
unitPref: g.unitPref,
gridSizeCm: g.gridSizeCm,
snapToGrid: g.snapToGrid,
}
return (
<div className="flex h-[calc(100dvh-8rem)] flex-col gap-3">
<div className="flex flex-wrap items-center gap-2">
<h1 className="truncate text-lg font-semibold tracking-tight" title={garden.name}>
{garden.name}
</h1>
<span className="rounded-md bg-border/40 px-2 py-1 text-xs text-muted">👁 Shared · read-only</span>
</div>
<div className="min-h-0 flex-1">
<GardenCanvas
garden={garden}
objects={objects}
plantings={plantings}
plantsById={plantsById}
canEdit={false}
/>
</div>
</div>
)
}