# CLAUDE.md - Garden Planner Project This document explains the architecture, patterns, and conventions used in this garden planning application. It's designed to help AI assistants (and humans) understand and modify the codebase effectively. ## Project Overview A self-hosted web application for visualizing garden planting schedules across three beds in Western Zealand, Denmark. Built with Rust backend (Axum) and vanilla JavaScript frontend. **Key Features:** - Interactive timeline view showing planting schedules across 12 months - Multi-phase crop visualization (greenhouse → transplant) - Expandable crop details on click - Table view for detailed event listing - CSV-driven data (easy to update without code changes) ## Architecture ### Backend (Rust) - **Framework:** Axum web server - **Port:** 127.0.0.1:3000 - **Data Sources:** `garden-plan-2026.csv` (events), `crop-colors.json` (colors) - **Main file:** `src/main.rs` The backend: 1. Parses CSV file on startup 2. Loads crop color configuration from JSON 3. Groups related events (greenhouse sowing + transplanting) 4. Serves four endpoints: - `GET /` - HTML page - `GET /api/events` - Raw CSV data as JSON - `GET /api/timeline` - Processed timeline with phases - `GET /api/colors` - Crop color mappings ### Frontend (JavaScript) - **File:** `static/index.html` - **Style:** Vanilla HTML/CSS/JavaScript (no frameworks) - **Dynamic loading:** HTML is read from disk on each request (no rebuild needed for UI changes) The frontend: 1. Fetches data from `/api/timeline`, `/api/events`, and `/api/colors` 2. Renders timeline with color-coded crop boxes 3. Handles click interactions for expanding crop details ## File Structure ``` garden/ ├── Cargo.toml # Rust dependencies ├── src/ │ └── main.rs # Web server, CSV parsing, event grouping ├── static/ │ └── index.html # Complete UI (HTML + CSS + JS) ├── garden-plan-2026.csv # Garden data (user editable) ├── crop-colors.json # Crop color mappings (user editable) ├── climate-data-western-zealand.md # Climate reference data ├── bed-layout.md # Documentation of bed contents ├── README.md # User-facing documentation └── CLAUDE.md # This file ``` ## Key Data Structures ### Rust Backend ```rust // Input: Raw CSV row struct GardenEvent { date: String, // "2025-03-24" activity: String, // "Greenhouse Sow", "Transplant", "Direct Sow" crop: String, // "Tomato" variety: String, // "Sungold + Matina" bed: String, // "South", "Middle", "North" harvest_period: String, // "July-October" notes: String, // Growing instructions } // Output: Grouped timeline entry struct TimelineEvent { crop: String, variety: String, bed: String, phases: Vec, // Multiple phases per crop harvest_period: String, } struct TimelinePhase { activity: String, // "Greenhouse Sow" start_date: String, // "2025-03-24" end_date: Option, // "2025-05-05" or harvest end notes: String, } ``` ### Event Grouping Logic The `build_timeline()` function groups related events: 1. Filters out "Mulch" and "Harvest" activities 2. Identifies greenhouse sowings 3. Finds matching transplants (same crop + bed, later date) 4. Groups them as phases of a single timeline entry 5. Calculates end dates (next phase start, or harvest end) **Example:** Tomato has two CSV rows (Greenhouse Sow on 2026-03-23, Transplant on 2026-05-04) → grouped into one TimelineEvent with two phases. ## Frontend Patterns ### Color System Crop colors are defined in `crop-colors.json` and loaded dynamically via the `/api/colors` endpoint. The `getCropColor()` function looks up colors from the loaded configuration: ```json { "Tomato": "#e53e3e", "Basil": "#48bb78", "Bush Beans": "#9f7aea", "Cornflower": "#6495ed", ... } ``` **To add/change crop colors:** 1. Edit `crop-colors.json` 2. Restart server (no rebuild needed) 3. Refresh browser The function returns a default blue (`#4299e1`) for any crop not in the configuration. Multi-phase crops use `adjustColor()` to lighten the first phase by 20% (factor 0.8). ### Box Expansion System Each crop box has: - Unique ID: `crop-${bed}-${idx}-${phaseIdx}` (e.g., "crop-South-1-0") - Group attribute: `data-crop-group="${bed}-${idx}"` (e.g., "South-1") When clicked: 1. `toggleCropDetails()` finds all boxes with matching `data-crop-group` 2. Collapses all other expanded crops 3. Expands all phases of the clicked crop 4. Each phase shows its own details independently ### Timeline Positioning `dateToPosition()` converts dates to percentage positions: - Timeline spans: 2026-03-01 to 2027-03-01 (12 months) - Each date maps to 0-100% of timeline width - Example: 2026-03-23 = ~8%, 2026-05-04 = ~22% ## Common Modifications ### Adding a New Crop 1. **Update CSV:** Add row(s) to `garden-plan-2026.csv` 2. **Add color:** Add entry to `crop-colors.json` (optional - defaults to blue if not specified) 3. **Restart server:** `./target/release/garden-planner` 4. **Refresh browser** ### Changing Crop Colors 1. **Edit colors:** Modify `crop-colors.json` 2. **Restart server:** `./target/release/garden-planner` (no rebuild needed) 3. **Refresh browser** ### Changing Timeline Display Logic Edit `renderTimeline()` function in `static/index.html`: - Modify how boxes are rendered - Change color calculation - Adjust positioning logic - No Rust rebuild needed (HTML loaded dynamically) ### Modifying Event Grouping Edit `build_timeline()` in `src/main.rs`: - Change how phases are matched - Adjust end date calculation - Modify filtering logic - Requires: `cargo build --release` ### Adding New API Endpoints ```rust // In main.rs async fn new_endpoint(State(state): State>) -> Json { // Your logic Json(data) } // Add to router let app = Router::new() .route("/api/new", get(new_endpoint)) // ... other routes ``` ## Development Workflow ### Making UI Changes 1. Edit `static/index.html` 2. Refresh browser (no rebuild) 3. Check browser console for JS errors ### Making Backend Changes 1. Edit `src/main.rs` 2. Run `cargo build --release` 3. Restart server: `./target/release/garden-planner` 4. Refresh browser ### Updating Garden Plan 1. Edit `garden-plan-2026.csv` and/or `crop-colors.json` 2. Restart server (no rebuild needed) 3. Refresh browser ### Verifying Climate Alignment 1. Review `climate-data-western-zealand.md` for frost dates and temperature trends 2. Ensure greenhouse starts are after mid-March 3. Ensure transplants are after last frost (~April 20) 4. Ensure fall crops are planted before first frost (~October 10) 5. Update climate data document annually in January ### Debugging - **Backend:** Add `println!()` or `dbg!()` in Rust code, check terminal - **Frontend:** Add `console.log()` in JS, check browser console (F12) - **Data flow:** Check Network tab in browser DevTools for API responses ## Design Decisions ### Why Rust? - Fast CSV parsing - Type safety for data structures - Low resource usage for self-hosting - Good learning opportunity ### Why Vanilla JS? - No build step complexity - Easy to understand and modify - Fast iteration (no framework overhead) - Everything in one HTML file ### Why CSV for Garden Data? - Easy to edit in spreadsheet apps - Human-readable - Version control friendly - No database needed ### Why JSON for Colors? - Simpler than embedding in code - Easy to edit without touching HTML/JS - Centralized configuration - No rebuild needed to change colors - Can be updated independently of garden plan ### Why Group Greenhouse + Transplant? - Shows complete crop lifecycle - Cleaner visual representation - User can see planning timeline at a glance - Reduces clutter (one row instead of two) ## Gotchas and Edge Cases ### CSV Header Names Headers must match exactly (case-sensitive): ``` Date,Week,Activity,Crop,Variety,Bed,Quantity,Harvest Period,Notes ``` Rust structs use `#[serde(rename = "Date")]` to map to these headers. ### CSV Field Quoting Any field containing commas must be wrapped in double quotes: ```csv 2025-05-05,Week 18,Direct Plant,Tansy,Guldknap,Middle,6 plants,June-October,"Bed edges - native Danish wildflower, pest repellent" ``` Without quotes, the CSV parser will split the field and cause an "UnequalLengths" error. ### Color Configuration The `crop-colors.json` file must be valid JSON with crop names as keys and hex colors as values: ```json { "Crop Name": "#hexcolor" } ``` Missing crops will default to `#4299e1` (blue). ### ID Prefix Matching Never use `[id^="crop-South-1"]` - it matches both "crop-South-1-0" and "crop-South-10-0". Always use exact `data-crop-group` attribute matching. ### Date Parsing `month_to_date()` function is simple and may not handle all formats. Current supported formats: - "July-October" → extracts "October" - "May 2027" → extracts "May" + "2027" - Defaults to Dec 31, 2026 if unparseable ### Multi-Phase Detection Grouping only works for greenhouse → transplant pattern. If you add a third phase type, update `build_timeline()` logic. ## Future Enhancement Ideas If you want to extend this project: 1. **Add edit mode:** Click to edit CSV data in-browser, save changes 2. **Export formats:** PDF, calendar (iCal), print-friendly view 3. **Weather integration:** Show frost dates, temperature ranges 4. **Mobile responsive:** Better touch interactions 5. **Search/filter:** Find specific crops, filter by bed 6. **Notes system:** Add per-crop growing notes from previous years 7. **Harvest tracking:** Mark actual harvest dates vs. planned 8. **Multi-year view:** Compare plans across seasons ## Dependencies ### Rust (Cargo.toml) - `axum` - Web framework - `tokio` - Async runtime - `tower` & `tower-http` - Middleware - `serde` & `serde_json` - Serialization - `csv` - CSV parsing - `chrono` - Date handling - `tracing` - Logging All dependencies are stable, well-maintained crates. ## Questions to Ask When Modifying Before making changes, consider: 1. **Is this a UI or data change?** - UI → Edit `index.html` - Data structure → Edit `main.rs` 2. **Does it affect grouping logic?** - If yes, test with crops that have multiple phases 3. **Does it change the timeline calculation?** - Test with crops at beginning, middle, and end of season 4. **Will it break existing CSS?** - Check both expanded and collapsed states - Test with beds that have many crops 5. **Is the CSV format changing?** - Update both Rust structs and documentation ## Getting Help If you're stuck: 1. Check the browser console for JS errors 2. Check terminal output for Rust errors 3. Verify CSV format matches expected structure 4. Test API endpoints directly: `curl http://localhost:3000/api/timeline` 5. Add debug logging to isolate the issue ## Summary This is a straightforward project with clear separation of concerns: - **Rust:** Data processing and serving - **JavaScript:** Rendering and interaction - **CSV:** Single source of truth The code prioritizes simplicity and maintainability over clever abstractions. When in doubt, add explicit code rather than complex logic.