garden plan web app
This commit is contained in:
362
CLAUDE.md
Normal file
362
CLAUDE.md
Normal file
@@ -0,0 +1,362 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user