Plants mode: recent-planted quick strip + clump/rows fill (#100)
Two things Steve asked for, both in the #99 Plants mode: - A "Recent" strip of the plants most recently planted IN THIS GARDEN (recentlyPlantedIds — derived from actual plantings, newest first, unique; NOT the manual localStorage tray), as tap-to-arm chips. So re-planting "more of the same" is one tap, no picker. Hidden until something's planted. - A clump/rows fill control (FillControl), shown once a plant is armed: pick a layout, "Fill bed", and it runs POST /objects/:id/fill region=all with the chosen layout — the #77 grid/clump fill the UI could NOT reach before (it was agent/REST only). Defaults to rows (a real planting). useFillObject mirrors useClearObject: one request, invalidate /full. Both live in the shared PlantPlacementTools, so desktop's focus toolbar and the mobile Plants strip get them identically. Fill is capped/validated server-side (#95) and covered spots are skipped, so re-filling is safe. Verified live at 390px: the Recent strip shows this garden's tomato/lettuce/ garlic; arming a chip reveals Clump|Rows + Fill bed; fill fires cleanly. tsc + vitest (95, incl. recentlyPlantedIds) + build green. DESIGN updated. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01H3zbym8Doka2d7D48maSgZ
This commit is contained in:
@@ -329,6 +329,38 @@ export function useUpdatePlanting(gardenId: number) {
|
||||
}
|
||||
|
||||
const clearResultSchema = z.object({ cleared: z.number() })
|
||||
const fillResultSchema = z.object({ created: z.number() })
|
||||
|
||||
/** Fill mode (#77/#100): the layout a region fill packs — fat clumps for quick
|
||||
* coverage, or a grid of individual plants at true spacing you could plant from.
|
||||
* Passed straight through to the server's `layout` field. */
|
||||
export type FillLayout = 'clump' | 'grid'
|
||||
|
||||
/** Fill a whole plantable object with one plant at the chosen layout, via the
|
||||
* same `POST /objects/:id/fill` the agent uses (region "all"). The response is
|
||||
* just a count; invalidate rather than optimistically splice a hex lattice we'd
|
||||
* have to recompute client-side. Returns how many plops it created. */
|
||||
export function useFillObject(gardenId: number) {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: async ({
|
||||
objectId,
|
||||
plantId,
|
||||
layout,
|
||||
}: {
|
||||
objectId: number
|
||||
plantId: number
|
||||
layout: FillLayout
|
||||
}): Promise<number> => {
|
||||
const res = fillResultSchema.parse(
|
||||
await api.post(`/objects/${objectId}/fill`, { plantId, region: 'all', layout }),
|
||||
)
|
||||
return res.created
|
||||
},
|
||||
onSettled: () => qc.invalidateQueries({ queryKey: fullKey(gardenId) }),
|
||||
onError: (err) => toast.error(objectErrorMessage(err, 'Could not fill the bed.')),
|
||||
})
|
||||
}
|
||||
|
||||
/** Clear a bed: soft-remove every active plop in an object (#82).
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user