Commit graph

562 commits

Author SHA1 Message Date
Jon Seager
6b7b7f9c6e
feat(brews): add application routes and server integration
- Add brew routes: page, create, list, get, delete
- Add timeline event creation when brews are logged
- Add brew_repo to AppState
- Add Conflict variant to AppError for 409 responses
2026-02-02 19:30:45 +00:00
Jon Seager
5f8de35404
feat(brews): add infrastructure layer
- Add SqlBrewRepository with transactional insert that deducts from bag
- Return Conflict error when insufficient coffee in bag
- Add BrewsClient for CLI HTTP operations
2026-02-02 19:30:14 +00:00
Jon Seager
91bd3172ea
feat(brews): add domain layer and database migration
- Add BrewId typed wrapper
- Add Brew, BrewWithDetails, NewBrew, BrewFilter, BrewSortKey
- Add BrewRepository trait with transactional insert semantics
- Add brews table with foreign keys to bags and gear
- Update timeline_events constraint to include 'brew' entity type
2026-02-02 19:29:23 +00:00
Jon Seager
2bd008a8d7
chore(lint): enable clippy pedantic and restriction lints
- Add [lints.clippy] section to Cargo.toml with pedantic group
- Cherry-pick restriction lints: dbg_macro (deny), todo, unwrap_used, expect_used (warn)
- Allow noisy pedantic lints (missing_errors_doc, module_name_repetitions, etc.)
- Fix unnecessary Result wrappers in token and user repository to_domain functions
- Merge duplicate match arms in TimelineEventViewModel
- Add justified #[allow] attributes for startup code and tests
2026-02-02 17:55:16 +00:00
Jon Seager
500389583f
chore(deps): remove unused wiremock dev-dependency
The wiremock crate was declared in dev-dependencies but never
imported or used anywhere in the test code.
2026-02-02 17:43:13 +00:00
Jon Seager
583f249059
refactor(nix): migrate flake.nix to flake-parts
- Replace manual forAllSystems pattern with flake-parts.lib.mkFlake
- Use perSystem for packages and devShells definitions
- Pin rust-overlay's nixpkgs to follow main nixpkgs input
- No functional changes to package or dev shell
2026-02-02 17:39:38 +00:00
Jon Seager
c7ea7420ae
fix(tests): use CARGO_BIN_EXE for binary path in CLI tests
The CLI tests hardcoded target/debug/brewlog, but nix builds in
release mode. Use Cargo's CARGO_BIN_EXE_brewlog env var which
automatically points to the correct binary location.

This enables running tests during nix build (removes doCheck = false).
2026-02-02 17:37:04 +00:00
Jon Seager
6cd621e10a
refactor(deps): switch from openssl to rustls for TLS
- Add tls-rustls feature to sqlx
- Disable default features on reqwest to avoid native-tls
- Remove openssl from flake.nix build and dev dependencies

This simplifies the build by removing the native OpenSSL dependency,
using pure-Rust TLS implementation instead.
2026-02-02 17:23:42 +00:00
Jon Seager
00477bdbc0
feat(roast): add update command for consistency with other entities
- Add PUT /api/v1/roasts/:id route handler
- Add update() method to roasts HTTP client
- Add UpdateRoastCommand to CLI with optional fields
- Add CLI tests for roast update authentication and functionality

Brings roast entity in line with roaster, bag, and gear which all
support add/list/get/update/delete operations.
2026-02-02 17:09:32 +00:00
Jon Seager
0090c4ba43
refactor(cli): restructure commands from flat to nested subcommands
Change CLI structure from `brewlog {verb}-{entity}` to `brewlog {entity} {verb}`:
- brewlog add-roaster → brewlog roaster add
- brewlog list-bags → brewlog bag list
- brewlog create-token → brewlog token create
- etc.

Each entity module now owns its subcommand enum and dispatch logic,
simplifying main.rs and improving discoverability via `brewlog {entity} --help`.

- Add RoasterCommands, RoastCommands, BagCommands, GearCommands, TokenCommands enums
- Add run() dispatcher to each entity module
- Simplify top-level Commands enum to delegate to entity modules
- Update all CLI tests and bootstrap script
2026-02-02 17:02:46 +00:00
Jon Seager
4edc66f44a
chore(bootstrap): add sample gear (2 grinders, 3 brewers) 2026-02-02 16:48:27 +00:00
Jon Seager
2ed8772302
chore(bootstrap): finish 4 older bags in sample data
Mark 4 of the 12 sample bags as finished to provide more realistic
test data with both open and closed bags in the timeline.
2026-02-02 16:45:27 +00:00
Jon Seager
b3c13faacf
fix(api): make all roast and bag endpoints return enriched types
Previously, list endpoints returned enriched types (RoastWithRoaster,
BagWithRoast) with related entity names, while get/create/update
endpoints returned bare types without this information.

This change makes all endpoints consistent by returning enriched types:
- Added get_with_roaster and get_with_roast repository methods
- Created define_enriched_get_handler! macro for custom getter methods
- Updated create and update handlers to fetch enriched data after write
- Updated CLI client to expect enriched types
2026-02-02 16:43:31 +00:00
Jon Seager
bee94d2a2e
fix(api): flatten RoastWithRoaster JSON response for consistency
The list-roasts API was returning nested objects with a "roast" field,
which was inconsistent with other list APIs in the codebase. Added
serde's #[serde(flatten)] attribute to make the response flat while
maintaining the internal struct composition.
2026-02-02 16:32:56 +00:00
Jon Seager
a54ca8f42d
build: add demo bags to database seeding script 2026-02-02 16:32:29 +00:00
Jon Seager
ebf338eb80
docs: add workflow requirements and migration instructions to CLAUDE.md
- Add sqlx migrate add command for creating database migrations
- Add workflow requirements section mandating lint/format/test before finishing
- Require draft commit message at end of every task
- Include example commit message format
2026-02-02 16:20:15 +00:00
Jon Seager
0abb1ffcc6
fix(web): make gear table actions consistent with roasts table
- Add visible "Actions" header instead of screen-reader-only text
- Change delete icon from always-red to neutral gray with red hover
- Add focus-visible styles and title attribute for accessibility
2026-02-02 16:18:48 +00:00
Jon Seager
6bbbafbef4
refactor(gear): remove notes field from Gear entity
The notes field was not providing enough value to justify its presence.
Simplified the Gear entity by removing notes from:
- Domain structs (Gear, NewGear, UpdateGear)
- SQL repository queries and GearRecord
- HTTP client methods
- CLI commands (--notes flag)
- Web views and templates
- All related tests

Added migration 0008_remove_gear_notes.sql to drop the column.
2026-02-02 16:16:54 +00:00
Jon Seager
708d89d452
fix: resolve clippy warnings for argument count and FromStr trait
- Allow too_many_arguments for AppState::new since 8 repos are needed
- Implement FromStr trait for GearCategory instead of custom from_str
  method to follow Rust conventions
- Update callers to use map_err for Result handling
2026-02-02 16:12:13 +00:00
Jon Seager
c1a5d763e2
test(gear): add comprehensive test suite for gear entity
Implement complete test coverage for the Gear entity across CLI, API,
timeline, and Datastar integration layers. This completes the Gear
entity implementation with 32 dedicated tests ensuring all CRUD
operations, authentication requirements, and reactive UI updates work
correctly.

Test Coverage:
- CLI commands (11 tests): Authentication, CRUD operations, category
  filtering, notes handling
- API endpoints (13 tests): HTTP status codes, validation, auth
  requirements, filtering
- Timeline integration (1 test): Gear creation events appear correctly
- Datastar fragments (7 tests): Fragment rendering vs full page/JSON
  responses for list, create, update, delete

Changes:
- Add tests/cli/gear_cli.rs with CLI integration tests
- Add tests/server/gear_api.rs with API integration tests
- Update tests/server/timeline.rs with gear timeline test
- Update tests/server/datastar.rs with gear fragment rendering tests
- Register test modules in tests/cli/main.rs and tests/server/main.rs
- Clean up unused imports in tests/server/helpers.rs

All 140 tests pass (8 unit + 37 CLI + 95 server integration).
2026-02-02 16:09:02 +00:00
Jon Seager
d1ebe835f9
fix: ensure gear events show up in timeline 2026-02-02 15:58:42 +00:00
Jon Seager
0cd4bc999c
fix: correct the database migration for gear 2026-02-02 15:58:31 +00:00
Jon Seager
7fc1d4db70
build: update bootstrap script 2026-02-02 15:58:05 +00:00
Jon Seager
74a8bef475
feat(web): add Gear web UI with reactive updates
Implement complete web interface for the Gear entity:
- Add GearView model with category badges and formatted display
- Create GearTemplate and GearListTemplate for Askama rendering
- Build main gear page with collapsible add form (Datastar-powered)
- Implement gear list table with sortable columns and pagination
- Use trash icon for delete actions matching roasts table design
- Add Gear navigation link in main menu between Bags and Timeline
- Integrate gear events into timeline view with proper labels and links

The web UI follows the established patterns from other entities with
Datastar for reactive fragment updates and proper authentication gating.

Apply code formatting fixes across all gear-related modules.
2026-02-02 15:35:57 +00:00
Jon Seager
1f5561d7ab
feat(cli): add Gear CLI commands
Implement complete CLI interface for managing gear via brewlog CLI.

Commands (presentation/cli/gear.rs):
- add-gear: Create gear with --category, --make, --model, --notes flags
- list-gear: List all gear with optional --category filter
- get-gear: Retrieve single gear by --id
- update-gear: Update gear fields (make, model, notes)
- delete-gear: Delete gear by --id

Uses define_get_command! and define_delete_command! macros for get/delete
operations to reduce boilerplate.

Command Registration:
- Register gear module in presentation/cli/mod.rs
- Add commands to Commands enum: AddGear, ListGear, GetGear, UpdateGear, DeleteGear
- Wire command execution in main.rs

All commands output JSON and use BrewlogClient for HTTP API calls.

Examples:
  brewlog add-gear --category grinder --make Baratza --model Encore
  brewlog list-gear --category brewer
  brewlog update-gear --id 1 --notes "Updated notes"
2026-02-02 15:24:34 +00:00
Jon Seager
1ef447157e
feat(application): add Gear API routes and web handlers
Implement complete HTTP interface for Gear entity with API endpoints and web views.

Route Handlers (application/routes/gear.rs):
- gear_page(): Web page handler with Datastar fragment support
- create_gear(): Create gear with timeline event logging
- list_gear(): JSON API with optional category filter
- get_gear(): Single gear retrieval (using macro)
- update_gear(): Update gear fields
- delete_gear(): Delete gear (using macro)
- load_gear_page(): Helper for paginated gear data
- render_gear_list_fragment(): Fragment rendering for Datastar updates
- NewGearSubmission: Input validation with category parsing and empty checks

Timeline Integration:
- Logs "added" events with Category, Make, Model details
- Events created in route handler (route-based approach like Bags)

Route Registration (application/routes/mod.rs):
- API routes: GET/POST /api/v1/gear, GET/PUT/DELETE /api/v1/gear/:id
- Web route: GET /gear

AppState Updates (application/server.rs):
- Add gear_repo field to AppState struct
- Initialize SqlGearRepository in serve() function
- Update AppState::new() to include gear_repo parameter

Supports both JSON API and form submissions with Datastar reactive updates.
2026-02-02 15:21:26 +00:00
Jon Seager
2309a696cc
feat(infrastructure): implement Gear repository and HTTP client
Add SQL repository and HTTP client implementations for Gear entity.

SQL Repository (infrastructure/repositories/gear.rs):
- SqlGearRepository with CRUD operations
- order_clause() for sorting: Make/Model (case-insensitive), Category, CreatedAt
- build_where_clause() for category filtering
- to_domain() converts GearRecord to domain Gear with category parsing
- Uses push_update_field! macro for partial updates
- Proper error handling with RepositoryError types

HTTP Client (infrastructure/client/gear.rs):
- GearClient for CLI access to API endpoints
- Methods: create(), list(), get(), update(), delete()
- Supports optional category filter in list()
- Error context with anyhow for user-friendly messages

Module Registration:
- Register gear module in infrastructure/repositories/mod.rs
- Register gear module and gear() method in infrastructure/client/mod.rs

Follows the exact patterns from BagRepository and BagsClient.
2026-02-02 15:19:19 +00:00
Jon Seager
b794d8a63f
feat(domain): add Gear entity with database migrations
Add Gear entity to track brewing equipment (grinders and brewers) with complete domain layer implementation.

Database changes:
- migrations/0006_add_gear.sql: Create gear table with category CHECK constraint and indexes
- migrations/0007_update_timeline_for_gear.sql: Document 'gear' as valid timeline entity type

Domain layer:
- Add GearId typed ID wrapper
- Create domain/gear.rs with:
  - GearCategory enum (Grinder/Brewer) with string conversion methods
  - Gear entity with make, model, notes fields
  - NewGear and UpdateGear DTOs
  - GearFilter for category-based filtering
  - GearSortKey with Make (default), Model, Category, CreatedAt options
- Add GearRepository trait to domain/repositories.rs with standard CRUD operations
- Register gear module in domain/mod.rs

This follows the same architectural pattern as the Bag entity.
2026-02-02 15:14:23 +00:00
Jon Seager
4bae7f1762
refactor(routes): re-export is_authenticated for shorter imports
Add pub(crate) re-export of is_authenticated from routes/mod.rs,
allowing handlers to use super::is_authenticated instead of the
verbose crate::application::routes::auth::is_authenticated path.

Updated 9 call sites across roasters.rs, roasts.rs, bags.rs, and
timeline.rs.
2026-02-02 14:32:30 +00:00
Jon Seager
7b0b6409e5
refactor(repos): use SQLx is_unique_violation for constraint checks
Replace string matching on "UNIQUE constraint failed" with SQLx's
type-safe is_unique_violation() method in roasters.rs and roasts.rs.

This aligns with users.rs and tokens.rs, and ensures the check works
across different database backends (SQLite, PostgreSQL) without
depending on error message formatting.
2026-02-02 14:30:30 +00:00
Jon Seager
2e4c8722ca
refactor(bags): align order_clause with other repositories
- Use method access (sort_key(), sort_direction()) instead of field
  access for consistency with roasters.rs and roasts.rs
- Add secondary sort clauses (e.g., created_at DESC) as tiebreakers
  to ensure deterministic pagination results
2026-02-02 14:28:45 +00:00
Jon Seager
c941cd83fb
docs(bags): add SAFETY comment for SQL string interpolation
Document why direct format!() interpolation is acceptable in
build_where_clause(): the values are type-safe (bool outputs literal
TRUE/FALSE, roast_id is i64 from typed wrapper). Warns future
developers to use parameterized queries if string fields are added.
2026-02-02 14:27:03 +00:00
Jon Seager
3a9fb16793
refactor(bags): replace multiple list methods with composable filter
- Add BagFilter struct with constructor methods (all, open, closed, for_roast)
- Replace 5 repository methods with single list(filter, request) method
- Add build_where_clause helper for dynamic WHERE clause construction
- Update all callers in bags and roasts routes

This eliminates method explosion when adding new filters - now only
BagFilter and build_where_clause need updating instead of adding
new repository methods.
2026-02-02 14:15:18 +00:00
Jon Seager
df0ebc283b
test(server): add integration tests for datastar endpoints 2026-02-02 14:02:55 +00:00
Jon Seager
036307d179
docs: document datastar patterns for Claude 2026-02-02 13:50:39 +00:00
Jon Seager
2d183e6955
refactor(routes): add macros to reduce route handler boilerplate
- Create define_get_handler! macro for GET-by-ID endpoints
- Create define_delete_handler! macro for DELETE endpoints with Datastar support
- Apply macros to roasters, roasts, and bags route modules
- Reduces 6 handlers from ~78 lines to ~12 lines total
2026-02-02 13:40:14 +00:00
Jon Seager
9f70aa0892
docs: add basic CLAUDE.md 2026-02-02 13:27:32 +00:00
Jon Seager
d689e48328
refactor(infra): standardize SQL query construction across repositories
- Add push_update_field! macro to reduce UPDATE query boilerplate
- Rename sort_clause() to order_clause() for consistency
- Convert bags.rs update method from string concatenation to QueryBuilder
- Apply macro to roasters.rs, roasts.rs, and bags.rs update methods

This reduces ~100 lines of repetitive code and ensures consistent
patterns for building dynamic UPDATE queries across all repositories.
2026-02-02 13:09:33 +00:00
Jon Seager
3e0aa1653f
chore(refactor): define macro for get/delete cli commands 2026-02-02 13:00:48 +00:00
Jon Seager
6611643424
chore: move RepositoryError into dedicated file 2026-02-02 12:57:15 +00:00
Jon Seager
ab9d4bfa13
feat: add amount to Bag Finished events 2025-11-27 15:10:17 +00:00
Jon Seager
f750107bb1
fix: add an action field to timeline events 2025-11-27 14:52:13 +00:00
Jon Seager
144257c2c8
fix: ensure bags are closed properly and closures are on the timeline 2025-11-27 14:42:38 +00:00
Jon Seager
02c6f6d675
test: add test for listing all bags 2025-11-27 14:32:56 +00:00
Jon Seager
673f76f09a
fix: ensure the list-bags command with no id lists all bags 2025-11-27 14:32:48 +00:00
Jon Seager
57260a71c2
fix: remove superfluous /bags/:id/finish endpoint 2025-11-27 14:24:26 +00:00
Jon Seager
7eab263071
chore: slight simplifications to bags 2025-11-27 14:03:00 +00:00
Jon Seager
ec8dbea7ea
test: add server tests for bags 2025-11-27 14:02:59 +00:00
Jon Seager
5252cacad0
test: add cli tests for bags 2025-11-27 14:02:58 +00:00
Jon Seager
d708a0e112
feat: add bags web views and templates 2025-11-27 14:02:55 +00:00