363 lines
11 KiB
Markdown
363 lines
11 KiB
Markdown
# 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<TimelinePhase>, // Multiple phases per crop
|
|
harvest_period: String,
|
|
}
|
|
|
|
struct TimelinePhase {
|
|
activity: String, // "Greenhouse Sow"
|
|
start_date: String, // "2025-03-24"
|
|
end_date: Option<String>, // "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<Arc<AppState>>) -> Json<YourType> {
|
|
// 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.
|