From f14875557be241c687c68bac2509d842cf892554 Mon Sep 17 00:00:00 2001 From: Steve Dudenhoeffer Date: Wed, 22 Jul 2026 21:41:11 -0400 Subject: [PATCH] Address #123 review: viewers can read plop notes; tighten the API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - The "add note" affordance no longer hides from viewers (it claimed parity with the bed inspector but gated on !readOnly). A viewer now sees "📓 Notes about this plant" and can open the plop's journal to read it; the composer stays edit-gated, so they can't write. Real parity now. - onScopePlantingChange is required, matching onScopeChange (its bed twin), so a caller can't pass a plop scope with no way to clear it. Dropped the now-dead guard on the "Show all" button. - Pulled the plop-over-bed scope-label priority into one `scopeLabel`, shared by the filter's sibling logic and the composer, so they can't drift; trimmed the invariant comment that was restated a third time. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ --- web/src/editor/JournalPanel.tsx | 37 +++++++++++++++----------------- web/src/editor/PlopInspector.tsx | 8 ++++--- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/web/src/editor/JournalPanel.tsx b/web/src/editor/JournalPanel.tsx index 5b6ec4d..bba0447 100644 --- a/web/src/editor/JournalPanel.tsx +++ b/web/src/editor/JournalPanel.tsx @@ -37,7 +37,7 @@ export function JournalPanel({ objects, scopeObjectId, onScopeChange, - scopePlantingId = null, + scopePlantingId, onScopePlantingChange, }: { gardenId: number @@ -48,17 +48,19 @@ export function JournalPanel({ /** Which bed the panel is filtered to, if any. */ scopeObjectId: number | null onScopeChange: (id: number | null) => void - /** Which single plop the panel is filtered to, if any (#85). Mutually - * exclusive with scopeObjectId — the editor store enforces that. */ - scopePlantingId?: number | null - onScopePlantingChange?: (id: number | null) => void + /** Which single plop the panel is filtered to, if any (#85). The store keeps + * this mutually exclusive with scopeObjectId. Required like its bed twin. */ + scopePlantingId: number | null + onScopePlantingChange: (id: number | null) => void }) { // Date-range narrowing (#85): the backend and JournalFilter already supported // from/to; they just had no UI. Empty inputs don't filter. const [from, setFrom] = useState('') const [to, setTo] = useState('') - // A plop scope wins over a bed scope — the two are mutually exclusive in the - // store, but guard here too so the filter is never double-scoped. + const scopedObject = objects.find((o) => o.id === scopeObjectId) ?? null + // One source of scope priority — plop over bed — for both the filter and the + // composer's label, so they can't drift. + const scopeLabel = scopePlantingId != null ? 'this planting' : scopedObject ? objectDisplayName(scopedObject) : null const filter = { ...(scopePlantingId != null ? { plantingId: scopePlantingId } @@ -70,7 +72,6 @@ export function JournalPanel({ } const journal = useJournal(gardenId, filter) const entries = journal.data?.pages.flatMap((p) => p.entries) ?? [] - const scopedObject = objects.find((o) => o.id === scopeObjectId) ?? null return (
@@ -98,15 +99,13 @@ export function JournalPanel({ {scopePlantingId != null && (
Notes about one planting - {onScopePlantingChange && ( - - )} +
)} @@ -150,9 +149,7 @@ export function JournalPanel({ gardenId={gardenId} objectId={scopePlantingId != null ? null : scopeObjectId} plantingId={scopePlantingId} - scopeLabel={ - scopePlantingId != null ? 'this planting' : scopedObject ? objectDisplayName(scopedObject) : null - } + scopeLabel={scopeLabel} /> )} diff --git a/web/src/editor/PlopInspector.tsx b/web/src/editor/PlopInspector.tsx index 5442427..08b2dcd 100644 --- a/web/src/editor/PlopInspector.tsx +++ b/web/src/editor/PlopInspector.tsx @@ -35,7 +35,9 @@ export function PlopInspector({ onChangePlant: () => void onClose: () => void /** Scope the journal to this plop and open it — the plop parallel of the bed - * inspector's "add note" (#85). Absent for a viewer, who can't write notes. */ + * inspector's "add note" (#85). Offered to viewers too (to READ the plop's + * notes, like the bed inspector does); the journal's composer is separately + * gated on edit rights, so a viewer just sees the entries. */ onAddNote?: () => void readOnly?: boolean }) { @@ -176,9 +178,9 @@ export function PlopInspector({ /> - {!readOnly && onAddNote && ( + {onAddNote && ( )}