Season view: filter the editor to a year (#54) (#66)
Build image / build-and-push (push) Successful in 17s
Build image / build-and-push (push) Successful in 17s
Co-authored-by: Steve Dudenhoeffer <[email protected]>
This commit was merged in pull request #66.
This commit is contained in:
@@ -12,12 +12,21 @@ import { Palette } from '@/editor/Palette'
|
||||
import { SeedTray } from '@/editor/SeedTray'
|
||||
import { ClearBedModal } from '@/editor/ClearBedModal'
|
||||
import { EditorHint } from '@/editor/EditorHint'
|
||||
import { SeasonBanner, SeasonPicker } from '@/editor/SeasonPicker'
|
||||
import { objectDisplayName } from '@/editor/kinds'
|
||||
import { useEditorStore } from '@/editor/store'
|
||||
import type { EditorGarden } from '@/editor/types'
|
||||
import { ShareGardenModal } from '@/components/gardens/ShareGardenModal'
|
||||
import { useMe } from '@/lib/auth'
|
||||
import { toEditorObject, useEnsurePlantInFull, useGardenFull, useUpdateObject, useUpdatePlanting } from '@/lib/objects'
|
||||
import {
|
||||
toEditorObject,
|
||||
useEnsurePlantInFull,
|
||||
useGardenFull,
|
||||
useGardenSeason,
|
||||
useGardenYears,
|
||||
useUpdateObject,
|
||||
useUpdatePlanting,
|
||||
} from '@/lib/objects'
|
||||
import { toEditorPlanting } from '@/lib/plantings'
|
||||
import type { Plant } from '@/lib/plants'
|
||||
import { useSeedTray } from '@/lib/seedTray'
|
||||
@@ -30,7 +39,14 @@ export function GardenEditorPage() {
|
||||
const gid = Number(gardenId)
|
||||
const { focus } = routeApi.useSearch()
|
||||
const navigate = routeApi.useNavigate()
|
||||
const full = useGardenFull(gid)
|
||||
const seasonYear = useEditorStore((s) => s.seasonYear)
|
||||
const setSeasonYear = useEditorStore((s) => s.setSeasonYear)
|
||||
// Two queries, one shown. The live one stays mounted so returning to "now" is
|
||||
// instant and so the optimistic mutation cache it owns is never displaced.
|
||||
const live = useGardenFull(gid)
|
||||
const season = useGardenSeason(gid, seasonYear)
|
||||
const full = seasonYear === null ? live : season
|
||||
const years = useGardenYears(gid)
|
||||
const me = useMe()
|
||||
usePageTitle(full.data?.garden.name ?? 'Garden')
|
||||
|
||||
@@ -68,7 +84,11 @@ export function GardenEditorPage() {
|
||||
// Role gating, computed before the effects/returns so the nudge handler can use
|
||||
// it. Ownership is the authoritative ownerId==me check.
|
||||
const gd = full.data?.garden
|
||||
const canEdit = gd != null && (gd.ownerId === me.data?.id || gd.myRole === 'editor')
|
||||
// A past season is read-only: it is a record of what happened, and editing it
|
||||
// would be editing the past. This is the single gate — the palette, inspector,
|
||||
// nudging, placement and drag handles all key off canEdit.
|
||||
const canEdit =
|
||||
seasonYear === null && gd != null && (gd.ownerId === me.data?.id || gd.myRole === 'editor')
|
||||
const isOwner = gd != null && me.data != null && gd.ownerId === me.data.id
|
||||
|
||||
// Latest values for the mount-once nudge keydown handler to read, so its effect
|
||||
@@ -330,9 +350,14 @@ export function GardenEditorPage() {
|
||||
Share
|
||||
</Button>
|
||||
)}
|
||||
{!canEdit && (
|
||||
{!canEdit && seasonYear === null && (
|
||||
<p className="mb-2 rounded-md bg-border/40 px-2 py-1 text-xs text-muted">👁 View only</p>
|
||||
)}
|
||||
{years.data && years.data.length > 0 && (
|
||||
<div className="mb-2">
|
||||
<SeasonPicker years={years.data} value={seasonYear} onChange={setSeasonYear} />
|
||||
</div>
|
||||
)}
|
||||
{focusedObjectId == null && canEdit && <Palette />}
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -343,7 +368,8 @@ export function GardenEditorPage() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="relative min-h-0 flex-1">
|
||||
<div className="relative flex min-h-0 flex-1 flex-col gap-2">
|
||||
{seasonYear !== null && <SeasonBanner year={seasonYear} onExit={() => setSeasonYear(null)} />}
|
||||
{focusedObject && (
|
||||
<div className="absolute left-2 top-2 z-20 flex flex-wrap items-center gap-2 rounded-lg border border-border bg-surface/90 px-2 py-1.5 text-sm shadow-sm backdrop-blur">
|
||||
<button type="button" onClick={exitFocus} className="rounded px-1.5 py-0.5 font-medium text-accent-strong hover:underline">
|
||||
@@ -380,7 +406,9 @@ export function GardenEditorPage() {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<GardenCanvas garden={garden} objects={objects} plantings={plantings} plantsById={plantsById} canEdit={canEdit} />
|
||||
<div className="relative min-h-0 flex-1">
|
||||
<GardenCanvas garden={garden} objects={objects} plantings={plantings} plantsById={plantsById} canEdit={canEdit} />
|
||||
</div>
|
||||
|
||||
{/* Empty-state hints (non-interactive overlays). */}
|
||||
{canEdit && focusedObjectId == null && objects.length === 0 && (
|
||||
|
||||
Reference in New Issue
Block a user