From 0382d5daa757bd8d01235b90c3588725e296ec65 Mon Sep 17 00:00:00 2001 From: Nirmal Khedkar Date: Sun, 25 Oct 2026 12:14:07 -0700 Subject: [PATCH] feat: add a local garden bed planner Expose existing crop spacing, compatibility, and maturity calculations through an editable Next.js garden bed. Show crop totals, neighbor conflicts, and estimated harvest dates without a backend service. Keep saving explicit and browser-local, with validated JSON export and import. Confirm resizes that remove plantings and label estimate limits. Add test-first coverage for planning, validation, portability, storage errors, and resize impact. Document NTARI scope and include UI screenshots. Signed-off-by: Nirmal Khedkar --- CONFORMANCE.md | 18 ++ frontend/README.md | 32 +++ frontend/app/garden-planner/page.js | 10 + frontend/components/GardenPlanner.js | 194 ++++++++++++++++++ frontend/components/GardenPlanner.module.css | 94 +++++++++ frontend/components/Header.js | 1 + .../screenshots/garden-planner-conflicts.png | Bin 0 -> 46086 bytes frontend/docs/screenshots/garden-planner.png | Bin 0 -> 42320 bytes frontend/lib/gardenPlanner.js | 116 +++++++++++ frontend/package.json | 3 +- frontend/tests/gardenPlanner.test.cjs | 103 ++++++++++ 11 files changed, 570 insertions(+), 1 deletion(-) create mode 100644 frontend/app/garden-planner/page.js create mode 100644 frontend/components/GardenPlanner.js create mode 100644 frontend/components/GardenPlanner.module.css create mode 100644 frontend/docs/screenshots/garden-planner-conflicts.png create mode 100644 frontend/docs/screenshots/garden-planner.png create mode 100644 frontend/lib/gardenPlanner.js create mode 100644 frontend/tests/gardenPlanner.test.cjs diff --git a/CONFORMANCE.md b/CONFORMANCE.md index f3b9f0a1..a9843d48 100644 --- a/CONFORMANCE.md +++ b/CONFORMANCE.md @@ -43,3 +43,21 @@ Adopts the covenant scale (LBTAS, v2 floor) and carries its own record instance. ## Product-specific notes (last, per the ordering rule) The reference economy. Its operator/federation model is the named reference for the substrate's frontend-as-operator design target. + +### Browser garden planner + +The Next.js `/garden-planner` page is a member-local planning aid, not a ledger +or a new protocol implementation. It reuses the existing square-foot gardening +utility for spacing, adjacent-crop rules, and fixed maturity estimates. The UI +states the limits of those estimates. + +Saving is explicit and browser-local; versioned JSON export/import provides a +portable editable copy without account or vendor services. The format contains +only version, planting date, and crop layout. It is not sent to the shared record +or federation. No settlement, covenant, reputation, identity, or governance +binding changes, and the in-review statuses above remain in review. + +`frontend/tests/gardenPlanner.test.cjs` checks crop totals, leap-day harvest +dates, adjacent-crop conflicts, resize impact, portable round-tripping, invalid +imports, and storage errors. This is feature verification, not a claim that the +delegated JFA invariants are now enforced by this repository. diff --git a/frontend/README.md b/frontend/README.md index f5a7b57b..c3431f32 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -7,6 +7,38 @@ This directory hosts the web interfaces for Fruitful. - `app/` – Next.js site providing the main user interface. - `chat-ui/` – Conversational interface built with Vite and React. See [chat-ui/ARCHITECTURE.md](chat-ui/ARCHITECTURE.md) for architectural details. +## Garden planner + +Run `npm install` then `npm run dev` in this directory and open `/garden-planner`, +or follow **Garden planner** in the site navigation. This page needs no backend +server, account, API key, or external service. + +Choose a bed from 1–8 feet in each dimension, set the planting date, select a crop, +then click or keyboard-activate a square. **Erase a square** clears a planting. +Counts, adjacent-crop warnings, and estimated harvest dates reuse +`backend/utils/squareFootGardening.js`. The four supported crops are tomato, +lettuce, carrot, and basil. Harvest estimates use fixed maturity intervals; +they do not account for climate, soil, variety, or the local growing season. + +**Save plan** explicitly writes one plan to this browser's local storage. +**Export JSON** downloads a portable copy; **Import JSON** restores an editable +plan. Imports accept version 1 rectangular beds, supported crops, and a real +date, with a 32 KB file limit. Invalid imports leave the current bed intact. +Shrinking a bed requires confirmation if planted squares would be removed. +Browser storage can be cleared or unavailable, so export a file for a backup. +This is a local planning format, not a federated Plan protocol message. + +Run `npm run test:planner` for dependency-free planner tests, `npm run lint` for +frontend lint, or `npm test` for planner tests followed by a production build. + +Design context: [Agrinet's local-growing purpose (Slack, October 19, 2026)](https://ntari.slack.com/archives/C0AENHR5KG8/p1792416355639889) +and [modular planting coordination (Confluence)](https://ntari.atlassian.net/wiki/spaces/Agrinet/pages/6815770). +The current repository's `CLAUDE.md` and `CONFORMANCE.md` govern implementation; +older roadmap text does not supersede those invariants. + +Screenshots: [editable bed](docs/screenshots/garden-planner.png) and +[neighbor conflicts](docs/screenshots/garden-planner-conflicts.png). + ## Environment variables Both sub-apps rely on environment variables to communicate with backend services. diff --git a/frontend/app/garden-planner/page.js b/frontend/app/garden-planner/page.js new file mode 100644 index 00000000..218763ea --- /dev/null +++ b/frontend/app/garden-planner/page.js @@ -0,0 +1,10 @@ +import GardenPlanner from '../../components/GardenPlanner'; + +export const metadata = { + title: 'Garden planner · Agrinet', + description: 'Plan a square-foot garden with existing Agrinet crop spacing and harvest estimates. Your plan stays in your browser.', +}; + +export default function GardenPlannerPage() { + return ; +} diff --git a/frontend/components/GardenPlanner.js b/frontend/components/GardenPlanner.js new file mode 100644 index 00000000..6b7b2a3c --- /dev/null +++ b/frontend/components/GardenPlanner.js @@ -0,0 +1,194 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { + CROPS, MAX_PLAN_BYTES, createPlan, analyzePlan, previewResize, plantSquare, + serializePlan, parsePlan, saveLocalPlan, loadLocalPlan, +} from '../lib/gardenPlanner'; +import { getPlantsPerSquare } from '../../backend/utils/squareFootGardening'; +import styles from './GardenPlanner.module.css'; + +const cropNames = { tomato: 'Tomato', lettuce: 'Lettuce', carrot: 'Carrot', basil: 'Basil' }; +const dateLabel = value => new Date(`${value}T00:00:00Z`).toLocaleDateString('en', { day: 'numeric', month: 'short', year: 'numeric', timeZone: 'UTC' }); +const squareLabel = ([r, c]) => `row ${r + 1}, column ${c + 1}`; + +export default function GardenPlanner() { + const [plan, setPlan] = useState(null); + const [selectedCrop, setSelectedCrop] = useState('tomato'); + const [dimensions, setDimensions] = useState({ rows: '4', columns: '4' }); + const [savedText, setSavedText] = useState(''); + const [notice, setNotice] = useState(''); + const [error, setError] = useState(''); + const [pendingResize, setPendingResize] = useState(null); + + useEffect(() => { + const today = new Date(); + const localDate = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`; + let initial = createPlan(4, 4, localDate); + try { + const saved = loadLocalPlan(window.localStorage); + if (saved) { + initial = saved; + setSavedText(serializePlan(saved)); + setNotice('Saved plan restored from this browser.'); + } + } catch { + setError('Your saved plan could not be restored. You can still plan and export a file.'); + } + setDimensions({ rows: String(initial.layout.length), columns: String(initial.layout[0].length) }); + setPlan(initial); + }, []); + + if (!plan) return

Garden planner

Opening your garden bed…

; + + let analysis; + let validationError = ''; + try { analysis = analyzePlan(plan); } catch (err) { validationError = err.message; } + const dirty = !analysis || serializePlan(plan) !== savedText; + const conflictSquares = new Set(analysis?.conflicts.flatMap(pair => [pair.first.join(','), pair.second.join(',')]) || []); + + function edit(next) { + setPlan(next); + setNotice(''); + setError(''); + setPendingResize(null); + } + + function applySize(event) { + event.preventDefault(); + try { + const preview = previewResize(plan, Number(dimensions.rows), Number(dimensions.columns)); + if (preview.removedSquares > 0) { + setPendingResize(preview); + return; + } + edit(preview.plan); + } catch (err) { setNotice(''); setError(err.message); } + } + + function save() { + try { + saveLocalPlan(window.localStorage, plan); + setSavedText(serializePlan(plan)); + setError(''); + setNotice('Plan saved in this browser. Export a copy to keep it elsewhere.'); + } catch { setNotice(''); setError('Could not save in this browser. Export your plan to keep a copy.'); } + } + + function exportFile() { + const url = URL.createObjectURL(new Blob([serializePlan(plan)], { type: 'application/json' })); + const link = document.createElement('a'); + link.href = url; + link.download = 'agrinet-garden-plan.json'; + link.click(); + setTimeout(() => URL.revokeObjectURL(url), 1000); + setNotice('Plan exported. Import the JSON file here to edit it again.'); + } + + async function importFile(event) { + const file = event.target.files?.[0]; + event.target.value = ''; + if (!file) return; + try { + if (file.size > MAX_PLAN_BYTES) throw new Error('Plan file is too large. Maximum size is 32 KB.'); + const next = parsePlan(await file.text()); + if (analysis?.occupiedSquares > 0 && !window.confirm('Replace the current bed with this imported plan? Unsaved changes will be lost.')) return; + edit(next); + setDimensions({ rows: String(next.layout.length), columns: String(next.layout[0].length) }); + setNotice('Plan imported. Save in this browser to keep it after reloading.'); + } catch (err) { setNotice(''); setError(err.message); } + } + + return ( +
+
+

AGRINET / GROWING TOGETHER

+

A little space.
A growing plan.

+

Choose a crop, then plant a square. See what fits and when it could be ready.

+
+ +
+ {dirty ? 'Unsaved plan' : 'Saved in this browser'} +
+ + + +
+
+

Your plan stays on this device. Saving uses this browser’s storage; export a file to move it.

+ {notice &&

{notice}

} + {(error || validationError) &&

{error || validationError}

} + {pendingResize &&
+

Resizing to {pendingResize.plan.layout[0].length} × {pendingResize.plan.layout.length} ft removes {pendingResize.removedSquares} planted {pendingResize.removedSquares === 1 ? 'square' : 'squares'}.

+
+ + +
+
} + +
+ + +
+

{plan.layout[0].length} × {plan.layout.length} ft bed

Each square = 1 sq ft
+

{selectedCrop ? `Planting ${cropNames[selectedCrop].toLowerCase()}. Select any square to place it.` : 'Erasing. Select a planted square to clear it.'}

+
+
+ {plan.layout.flatMap((row, r) => row.map((crop, c) => ))} +
+
+
+
Bed area
{plan.layout.length * plan.layout[0].length} sq ft
+
Planted squares
{analysis?.occupiedSquares ?? '—'}
+
Total plants
{analysis?.totalPlants ?? '—'}
+
+
+
+ + {analysis?.conflicts.length > 0 &&
+

Give these crops different neighbors

+
    {analysis.conflicts.map(pair =>
  • + {cropNames[pair.crops[0]]} ({squareLabel(pair.first)}) and {cropNames[pair.crops[1]]} ({squareLabel(pair.second)}) are incompatible in Agrinet’s current crop rules. +
  • )}
+

Choose another crop for either square, or erase it.

+
} + +
+

A look ahead

Estimated harvests
+ {!analysis ?

Choose a valid planting date to see estimates.

: analysis.crops.length === 0 ?

Your harvest starts with one square. Choose a crop above to begin.

:
+ + + {analysis.crops.map(crop => )} +
Crop counts and estimated harvest dates
CropSquaresPlantsEstimated harvest
{crop.squares}{crop.plants}
} +

Uses Agrinet’s existing spacing, neighbor rules, and fixed days to maturity for four crops. Estimates do not account for variety, weather, soil, or your local growing season.

+
+
+ ); +} diff --git a/frontend/components/GardenPlanner.module.css b/frontend/components/GardenPlanner.module.css new file mode 100644 index 00000000..20588ace --- /dev/null +++ b/frontend/components/GardenPlanner.module.css @@ -0,0 +1,94 @@ +.planner { --ink: #243d31; --muted: #5c6a60; --line: #d5dfd4; color: var(--ink); padding: 32px 0 48px; } +.planner * { box-sizing: border-box; } +.heading { margin-bottom: 30px; } +.kicker { font-size: 11px; letter-spacing: .14em; font-weight: 700; color: #587258; margin: 0 0 16px; } +.heading h1 { font-family: Georgia, 'Times New Roman', serif; font-weight: 400; font-size: clamp(40px, 6vw, 62px); line-height: 1.06; letter-spacing: -.035em; margin: 0 0 18px; } +.heading h1 span { color: #617b45; } +.heading > p:last-child { color: var(--muted); font-size: 16px; line-height: 1.6; max-width: 540px; margin: 0; } +.toolbar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 14px; padding: 18px 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); } +.saveState { font-size: 13px; color: var(--muted); } +.actions { display: flex; gap: 8px; flex-wrap: wrap; } +.planner button, .importButton { font: inherit; font-size: 13px; border: 1px solid #bfcdbf; border-radius: 6px; padding: 10px 14px; background: #fff; color: var(--ink); cursor: pointer; min-height: 42px; transition: background .12s; } +.planner button:hover, .importButton:hover { background: #edf3e9; } +.planner button:focus-visible, .importButton:focus-within, .planner input:focus-visible { outline: 3px solid #557b2f; outline-offset: 3px; } +.planner button:disabled { cursor: not-allowed; opacity: .5; } +.planner .primary { background: #2d523b; color: white; border-color: #2d523b; } +.planner .primary:hover { background: #1e3d2a; } +.importButton { position: relative; display: inline-flex; align-items: center; overflow: hidden; } +.importButton input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; } +.privacy { color: var(--muted); font-size: 12px; line-height: 1.6; margin: 12px 0 24px; } +.notice, .error { padding: 13px 16px; border-radius: 5px; font-size: 14px; line-height: 1.5; } +.notice { background: #eaf3e7; border: 1px solid #c9dcc2; } +.error { background: #fff0e9; color: #82330f; border: 1px solid #e5bba5; } +.workspace { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 34px; margin-top: 28px; } +.planner h2 { font-size: 17px; letter-spacing: -.02em; margin: 0 0 18px; } +.sidebar { min-width: 0; } +.dimensions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; } +.planner label { font-size: 12px; color: var(--muted); } +.planner input:not([type='file']) { width: 100%; border: 1px solid #bccab9; padding: 10px; border-radius: 5px; margin-top: 8px; background: #fff; color: var(--ink); font: inherit; min-height: 42px; } +.sizeForm > button { width: 100%; margin-top: 10px; } +.dateField { display: block; margin-top: 20px; } +.planner .cropHeading { margin-top: 30px; } +.palette { display: grid; gap: 8px; } +.planner .cropOption { display: flex; align-items: center; gap: 12px; text-align: left; padding: 10px; position: relative; } +.planner .cropOption[aria-pressed='true'] { border-color: #42653e; box-shadow: 0 0 0 1px #42653e; background: #f1f5ed; } +.cropOption strong, .cropOption small { display: block; } +.cropOption strong { font-size: 13px; font-weight: 600; } +.cropOption small { font-size: 11px; color: var(--muted); margin-top: 3px; } +.cropMark { width: 34px; height: 34px; display: grid; place-items: center; border-radius: 50%; font: 19px Georgia, serif; flex-shrink: 0; } +.selectedMark { margin-left: auto; color: #3f633d; font-size: 16px; } +.planner .erase { margin-top: 4px; background: transparent; border-style: dashed; } +.planner .erase[aria-pressed='true'] { border-style: solid; border-color: #42653e; background: #edf3e9; } +.bedPanel { min-width: 0; } +.bedHeader { display: flex; gap: 12px; align-items: baseline; justify-content: space-between; flex-wrap: wrap; } +.bedHeader h2 { margin-bottom: 0; } +.bedHeader > span { font-size: 11px; color: var(--muted); } +.instruction { font-size: 12px; color: var(--muted); line-height: 1.5; margin: 10px 0 18px; } +.bedScroll { overflow-x: auto; padding: 5px; margin: -5px; } +.bed { display: grid; grid-template-columns: repeat(var(--columns), minmax(65px, 1fr)); gap: 7px; padding: 12px; background: #e6e9da; border: 1px solid #c5ccb8; border-radius: 9px; min-width: min-content; } +.planner .square { aspect-ratio: 1; border-radius: 5px; min-height: 65px; padding: 6px 2px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; position: relative; border: 1px solid #ffffffa0; } +.planner .square:hover { outline: 2px solid #53744a; outline-offset: -2px; filter: brightness(.97); } +.square strong { font: 29px Georgia, serif; line-height: 1; } +.square > span:not(.conflictMark) { font-size: 12px; font-weight: 600; } +.square small { font-size: 10px; opacity: .85; } +.tomato, .planner .tomato { background: #f0d9cf; color: #823b29; } +.lettuce, .planner .lettuce { background: #d7e5b9; color: #36522d; } +.carrot, .planner .carrot { background: #f2dfb7; color: #825116; } +.basil, .planner .basil { background: #c9ddd1; color: #2b5847; } +.planner .empty { background: #f8f9f0; border: 1px dashed #c5cbb7; color: #6b795f; } +.square.empty > span { font-size: 25px; font-weight: 300; line-height: 1; } +.planner .conflictSquare { border: 2px solid #a85c23; } +.conflictMark { position: absolute; right: 3px; top: 3px; width: 16px; height: 16px; background: #85400f; color: #fff; font-size: 11px; font-weight: 700; border-radius: 50%; } +.totals { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; border-top: 1px solid var(--line); margin: 24px 0 0; padding-top: 20px; } +.totals dt { font-size: 11px; color: var(--muted); } +.totals dd { margin: 7px 0 0; font: 29px Georgia, serif; } +.totals dd small { font: 12px -apple-system, BlinkMacSystemFont, sans-serif; color: var(--muted); } +.conflicts { background: #fff5e5; border-left: 3px solid #b97a30; padding: 20px; margin-top: 26px; font-size: 13px; line-height: 1.6; } +.conflicts h2 { margin-bottom: 8px; color: #74461b; } +.conflicts ul { padding-left: 18px; } +.conflicts p { margin-bottom: 0; color: #74461b; } +.harvest { border-top: 1px solid var(--line); margin-top: 40px; padding-top: 24px; } +.harvestHeading { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; } +.harvestHeading h2 { font: 28px Georgia, serif; } +.harvestHeading > span { color: var(--muted); font-size: 12px; } +.emptyHarvest { padding: 20px 0; color: var(--muted); font-size: 14px; } +.tableScroll { overflow-x: auto; } +.harvest table { border-collapse: collapse; width: 100%; text-align: left; white-space: nowrap; } +.harvest thead th { font-size: 11px; font-weight: 500; color: var(--muted); padding: 10px 12px; border-bottom: 1px solid var(--line); } +.harvest tbody th, .harvest td { padding: 15px 12px; font-size: 13px; border-bottom: 1px solid #e7ece3; } +.harvest tbody th { font-weight: 600; } +.tableDot { display: inline-block; width: 11px; height: 11px; margin-right: 10px; border: 1px solid currentColor; border-radius: 50%; } +.estimateNote { color: var(--muted); font-size: 12px; line-height: 1.7; margin-top: 20px; max-width: 690px; } +.srOnly { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); } +@media (max-width: 680px) { + .planner { padding-top: 20px; } + .workspace { grid-template-columns: minmax(0, 1fr); gap: 28px; } + .palette { grid-template-columns: 1fr 1fr; } + .palette .erase { grid-column: 1 / -1; } + .cropMark { width: 28px; height: 28px; } + .heading h1 { font-size: 44px; } + .planner .cropHeading { margin-top: 24px; } + .bed { grid-template-columns: repeat(var(--columns), minmax(60px, 1fr)); } + .harvest tbody th, .harvest td, .harvest thead th { padding-left: 6px; padding-right: 6px; } +} +@media (prefers-reduced-motion: reduce) { .planner button { transition: none; } } diff --git a/frontend/components/Header.js b/frontend/components/Header.js index 3660c4c3..1fead598 100644 --- a/frontend/components/Header.js +++ b/frontend/components/Header.js @@ -5,6 +5,7 @@ export default function Header() {
diff --git a/frontend/docs/screenshots/garden-planner-conflicts.png b/frontend/docs/screenshots/garden-planner-conflicts.png new file mode 100644 index 0000000000000000000000000000000000000000..3bdb62e77cf270d4e2743fc3d9c59e94251c4626 GIT binary patch literal 46086 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 V&R8!29s!4~nnhGh6tn){1OS06@B07% literal 0 HcmV?d00001 diff --git a/frontend/lib/gardenPlanner.js b/frontend/lib/gardenPlanner.js new file mode 100644 index 00000000..88eff0ad --- /dev/null +++ b/frontend/lib/gardenPlanner.js @@ -0,0 +1,116 @@ +const { + PLANT_SPACING, arePlantsCompatible, getPlantsPerSquare, projectGrowth, +} = require('../../backend/utils/squareFootGardening'); + +const CROPS = Object.keys(PLANT_SPACING); +const STORAGE_KEY = 'agrinet:garden-plan:v1'; +const MAX_PLAN_BYTES = 32768; + +function checkDimensions(rows, columns) { + if (![rows, columns].every(value => Number.isInteger(value) && value >= 1 && value <= 8)) { + throw new Error('Bed dimensions must be whole numbers from 1 to 8 feet.'); + } +} + +function checkDate(value) { + if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value) || value.startsWith('0000')) { + throw new Error('Choose a valid planting date.'); + } + const parsed = new Date(`${value}T00:00:00Z`); + if (Number.isNaN(parsed.valueOf()) || parsed.toISOString().split('T')[0] !== value) { + throw new Error('Choose a valid planting date.'); + } +} + +function validatePlan(plan) { + if (!plan || plan.version !== 1 || !Array.isArray(plan.layout) || !Array.isArray(plan.layout[0])) { + throw new Error('This is not a supported Agrinet garden plan.'); + } + checkDate(plan.startDate); + checkDimensions(plan.layout.length, plan.layout[0].length); + if (!plan.layout.every(row => Array.isArray(row) && row.length === plan.layout[0].length)) { + throw new Error('The garden bed must be rectangular.'); + } + if (!plan.layout.every(row => row.every(crop => crop === null || CROPS.includes(crop)))) { + throw new Error('The plan contains an unsupported crop.'); + } + return plan; +} + +function createPlan(rows, columns, startDate) { + checkDimensions(rows, columns); + checkDate(startDate); + return { version: 1, startDate, layout: Array.from({ length: rows }, () => Array(columns).fill(null)) }; +} + +function resizePlan(plan, rows, columns) { + validatePlan(plan); + const resized = createPlan(rows, columns, plan.startDate); + resized.layout = resized.layout.map((row, r) => row.map((_, c) => plan.layout[r]?.[c] ?? null)); + return resized; +} + +function plantSquare(plan, row, column, crop) { + validatePlan(plan); + if (!Number.isInteger(row) || !Number.isInteger(column) || row < 0 || column < 0 || row >= plan.layout.length || column >= plan.layout[0].length) { + throw new Error('Choose a square inside the garden bed.'); + } + if (crop !== null && !CROPS.includes(crop)) throw new Error('Choose a supported crop.'); + return { ...plan, layout: plan.layout.map((cells, r) => cells.map((value, c) => r === row && c === column ? crop : value)) }; +} + +function previewResize(plan, rows, columns) { + const resized = resizePlan(plan, rows, columns); + return { plan: resized, removedSquares: analyzePlan(plan).occupiedSquares - analyzePlan(resized).occupiedSquares }; +} + +function analyzePlan(plan) { + validatePlan(plan); + const counts = new Map(); + const conflicts = []; + plan.layout.forEach((row, r) => row.forEach((crop, c) => { + if (!crop) return; + counts.set(crop, (counts.get(crop) || 0) + 1); + for (const [nr, nc] of [[r, c + 1], [r + 1, c]]) { + const neighbor = plan.layout[nr]?.[nc]; + if (neighbor && !arePlantsCompatible(crop, neighbor)) { + conflicts.push({ first: [r, c], second: [nr, nc], crops: [crop, neighbor] }); + } + } + })); + const crops = Array.from(counts, ([crop, squares]) => ({ + crop, squares, plants: squares * getPlantsPerSquare(crop), + harvestDate: projectGrowth(crop, plan.startDate).harvestDate.toISOString().split('T')[0], + })).sort((a, b) => a.harvestDate.localeCompare(b.harvestDate) || a.crop.localeCompare(b.crop)); + return { + area: plan.layout.length * plan.layout[0].length, + occupiedSquares: crops.reduce((sum, crop) => sum + crop.squares, 0), + totalPlants: crops.reduce((sum, crop) => sum + crop.plants, 0), + crops, conflicts, + }; +} + +function serializePlan(plan) { + validatePlan(plan); + return JSON.stringify({ version: 1, startDate: plan.startDate, layout: plan.layout }, null, 2); +} + +function parsePlan(text) { + if (typeof text !== 'string' || text.length > MAX_PLAN_BYTES) throw new Error('Plan file is too large. Maximum size is 32 KB.'); + let plan; + try { plan = JSON.parse(text); } catch { throw new Error('Choose a valid garden plan JSON file.'); } + validatePlan(plan); + // Only portable planning fields survive import; no extra metadata is retained. + return JSON.parse(serializePlan(plan)); +} + +function saveLocalPlan(storage, plan) { + storage.setItem(STORAGE_KEY, serializePlan(plan)); +} + +function loadLocalPlan(storage) { + const saved = storage.getItem(STORAGE_KEY); + return saved === null ? null : parsePlan(saved); +} + +module.exports = { CROPS, MAX_PLAN_BYTES, createPlan, analyzePlan, resizePlan, previewResize, plantSquare, serializePlan, parsePlan, saveLocalPlan, loadLocalPlan }; diff --git a/frontend/package.json b/frontend/package.json index 9fcb21a6..8a457185 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,8 @@ "build": "next build", "start": "next start", "lint": "eslint --ext .js,.jsx,.mjs . --max-warnings 0", - "test": "next build", + "test": "npm run test:planner && next build", + "test:planner": "node --test tests/gardenPlanner.test.cjs", "federation-status": "node federationStatusCLI.js", "federation-status:ci": "BACKEND_URL=$BACKEND_URL node federationStatusCLI.js", "test-connection": "node test-connection.js" diff --git a/frontend/tests/gardenPlanner.test.cjs b/frontend/tests/gardenPlanner.test.cjs new file mode 100644 index 00000000..275b0fbc --- /dev/null +++ b/frontend/tests/gardenPlanner.test.cjs @@ -0,0 +1,103 @@ +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const { + createPlan, analyzePlan, resizePlan, previewResize, plantSquare, serializePlan, parsePlan, saveLocalPlan, loadLocalPlan, +} = require('../lib/gardenPlanner'); + +test('counts plants using existing spacing and estimates harvest across a leap day', () => { + const plan = { version: 1, startDate: '2028-02-01', layout: [['tomato', 'basil'], ['lettuce', null]] }; + const result = analyzePlan(plan); + assert.equal(result.area, 4); + assert.equal(result.occupiedSquares, 3); + assert.equal(result.totalPlants, 6); + assert.deepEqual(result.crops, [ + { crop: 'lettuce', squares: 1, plants: 4, harvestDate: '2028-03-27' }, + { crop: 'basil', squares: 1, plants: 1, harvestDate: '2028-04-01' }, + { crop: 'tomato', squares: 1, plants: 1, harvestDate: '2028-04-11' }, + ]); + assert.deepEqual(result.conflicts, []); +}); + +test('reports each incompatible adjacent pair once, without treating diagonals as neighbors', () => { + const result = analyzePlan({ version: 1, startDate: '2026-03-01', layout: [['tomato', 'carrot'], ['basil', 'lettuce']] }); + assert.deepEqual(result.conflicts, [{ first: [0, 0], second: [0, 1], crops: ['tomato', 'carrot'] }, { first: [1, 0], second: [1, 1], crops: ['basil', 'lettuce'] }]); +}); + +test('an empty bed has zero plants and no invented crop estimates', () => { + const result = analyzePlan(createPlan(3, 4, '2026-03-01')); + assert.equal(result.area, 12); + assert.equal(result.totalPlants, 0); + assert.equal(result.occupiedSquares, 0); + assert.deepEqual(result.crops, []); +}); + +test('planting and erasing update only the target square without mutating the original', () => { + const original = createPlan(2, 2, '2026-03-01'); + const planted = plantSquare(original, 0, 1, 'carrot'); + assert.deepEqual(planted.layout, [[null, 'carrot'], [null, null]]); + assert.equal(analyzePlan(planted).totalPlants, 16); + assert.equal(original.layout[0][1], null); + assert.equal(plantSquare(planted, 0, 1, null).layout[0][1], null); + assert.throws(() => plantSquare(original, 2, 0, 'basil'), /square/i); + assert.throws(() => plantSquare(original, 0, 0, 'potato'), /crop/i); +}); + +test('resizing preserves overlapping squares and fills new squares without shared rows', () => { + const plan = { version: 1, startDate: '2026-03-01', layout: [['tomato', null], [null, 'basil']] }; + const grown = resizePlan(plan, 3, 3); + assert.deepEqual(grown.layout, [['tomato', null, null], [null, 'basil', null], [null, null, null]]); + grown.layout[2][0] = 'lettuce'; + assert.equal(grown.layout[0][0], 'tomato'); + assert.deepEqual(resizePlan(plan, 1, 1).layout, [['tomato']]); + assert.equal(plan.layout[1][1], 'basil'); +}); + +test('previewing a smaller bed reports planted squares that would be removed without changing the plan', () => { + const plan = { version: 1, startDate: '2026-03-01', layout: [['tomato', 'basil'], ['lettuce', null]] }; + const preview = previewResize(plan, 1, 1); + assert.equal(preview.removedSquares, 2); + assert.deepEqual(preview.plan.layout, [['tomato']]); + assert.deepEqual(plan.layout, [['tomato', 'basil'], ['lettuce', null]]); + assert.equal(previewResize(plan, 3, 3).removedSquares, 0); +}); + +test('export and import preserve an editable plan, excluding unrelated fields', () => { + const plan = { version: 1, startDate: '2026-03-01', layout: [['basil', 'tomato']], privateNote: 'must not export' }; + const serialized = serializePlan(plan); + assert.deepEqual(JSON.parse(serialized), { version: 1, startDate: '2026-03-01', layout: [['basil', 'tomato']] }); + assert.deepEqual(parsePlan(serialized), { version: 1, startDate: '2026-03-01', layout: [['basil', 'tomato']] }); + assert.equal(analyzePlan(parsePlan(serialized)).totalPlants, 2); +}); + +test('rejects malformed, oversized, unsupported and nonrectangular imported plans', () => { + for (const invalid of [ + 'not JSON', ' '.repeat(33000), 'null', + JSON.stringify({ version: 2, startDate: '2026-03-01', layout: [[null]] }), + JSON.stringify({ version: 1, startDate: '2026-02-30', layout: [[null]] }), + JSON.stringify({ version: 1, startDate: '2026-03-01T00:00:00Z', layout: [[null]] }), + JSON.stringify({ version: 1, startDate: '2026-03-01', layout: [['potato']] }), + JSON.stringify({ version: 1, startDate: '2026-03-01', layout: [[null], [null, null]] }), + JSON.stringify({ version: 1, startDate: '2026-03-01', layout: [] }), + ]) assert.throws(() => parsePlan(invalid), /plan|date|crop|bed/i); + for (const size of [0, -1, 1.5, 9, NaN, '4']) assert.throws(() => createPlan(size, 4, '2026-03-01'), /bed/i); +}); + +test('saving and restoring preserves a portable plan in browser storage', () => { + const values = new Map(); + const storage = { setItem: (key, value) => values.set(key, value), getItem: key => values.get(key) ?? null }; + assert.equal(loadLocalPlan(storage), null); + const plan = { version: 1, startDate: '2026-03-01', layout: [['lettuce', null]] }; + saveLocalPlan(storage, plan); + assert.deepEqual(loadLocalPlan(storage), plan); + assert.equal(values.size, 1); + saveLocalPlan(storage, plantSquare(plan, 0, 1, 'carrot')); + assert.equal(analyzePlan(loadLocalPlan(storage)).totalPlants, 20); + assert.equal(values.size, 1); +}); + +test('storage failures and damaged saved data surface instead of claiming a save succeeded', () => { + const plan = createPlan(1, 1, '2026-03-01'); + const unavailable = { setItem: () => { throw new Error('storage unavailable'); }, getItem: () => '{broken' }; + assert.throws(() => saveLocalPlan(unavailable, plan), /storage unavailable/); + assert.throws(() => loadLocalPlan(unavailable), /valid garden plan/i); +});