Add IntersectionObserver-based infinite scroll for mobile viewports
that fetches next pages as the user scrolls. Uses MutationObserver
to re-attach after DOM replacements. Hide pagination controls on
screens narrower than 768px.
Add search_header macro with Datastar-driven live search and debounced
input. Move pagination controls from above to below tables. Add
pagination-controls class for mobile hiding. Add infinite scroll
sentinel divs and data attributes to all list partials.
Add q parameter to ListQuery with into_request_and_search(). Extend
ListNavigator with search state, search_value(), has_search(), path(),
and search_query_base() for template URL generation. Thread search
through all route handlers and update backup tests.
Add `search: Option<&str>` to list methods on RoasterRepository,
RoastRepository, BagRepository, GearRepository, and BrewRepository.
Default trait methods pass None for backwards compatibility.
Add SearchFilter struct for LIKE-based search with dynamic column
matching. Add DatabaseDriver type alias to support QueryBuilder
generics across SQLite and Postgres feature flags.
- Hide horizontal nav links below md breakpoint
- Add hamburger/close toggle button using Datastar signals
- Show vertical dropdown menu on mobile with stacked nav links
- Add BackupData struct and BackupService with raw SQL export/import
- Restore uses raw inserts to bypass brew deductions and timeline creation
- Restore requires an empty database, inserts in FK dependency order
- Add comprehensive e2e test verifying full round-trip fidelity
- Add test for creating a brew with filter paper
- Add test verifying filter_paper_name is None when omitted
- Update enriched data test to assert on filter_paper_name
- Add filter_paper category to test helper
- Add Hario V60 Tabbed 02, Sibarist FAST Specialty 02, and Fellow
Stagg XF Filters as sample filter paper gear
- Reference filter papers in V60 and Stagg XF brews
- Add filter paper dropdown to brew form and gear category form
- Add custom deserializer for optional GearId from HTML forms
- Load filter papers in brews_page handler
- Show filter paper as subtext under brewer in brew list
- Add filter paper to timeline event details and brew-again forms
- Add --filter-paper-id flag to CLI brew add command
- Add filter_paper_id to SQL queries (SELECT, INSERT, LEFT JOIN)
- Add filter_paper_name via LEFT JOIN on gear table
- Update BrewRecord and BrewWithDetailsRecord structs
- Add filter_paper_id parameter to HTTP client create method
- Add migration to extend gear CHECK constraint with 'filter_paper'
and add nullable filter_paper_id column to brews
- Add FilterPaper variant to GearCategory enum
- Add filter_paper_id to Brew, NewBrew, and BrewWithDetails
- Add filter_paper_id to TimelineBrewData
Per-month <ol> containers caused :nth-of-type to reset at each month,
putting the first card of consecutive months on the same side. Flatten
all items into a single container and use :nth-child(odd of .timeline-item)
for continuous left/right alternation across months and infinite scroll.
Database:
- Add slug, roaster_slug, brew_data_json columns to timeline_events
- Migration backfills existing data from related tables
- Remove 9-way LEFT JOIN from list query, read directly from columns
Frontend:
- Use CSS :nth-of-type(odd/even) for alternating timeline layout
- Remove JavaScript class manipulation when appending month events
- Simplify infinite scroll month-merging logic
This eliminates query-time JOINs across 5 tables and ~20 lines of
client-side JavaScript for pattern maintenance.
- Simplify timeline layout to single-column without sidebar
- Update tests to explicitly pass page_size for pagination testing
- Include sticky headers and a neater alternating, side-by-side
timeline design
- Add sqlite3 commands to update timestamps after data creation
- Spread gear, roasters, bags, and brews logically from Aug 2025 to Jan 2026
- Ensure temporal consistency (entities created before being referenced)
- Update timeline_events to match entity creation times
- Add TimelineBrewData struct to domain layer to carry raw brew
parameters needed for repeating a brew
- Extend timeline SQL query to LEFT JOIN with brews table and fetch
brew data (bag_id, grinder_id, brewer_id, coffee_weight, etc.)
- Add brew_data field to TimelineEventView for template access
- Add is_authenticated to TimelineChunkTemplate for auth-aware chunks
- Add "brew again" button to timeline month partial, visible only for
authenticated users on brew events
- Server returns reload script when brew is created from timeline page,
using Datastar's fragment patching to append script to body
- Combine weight/water/ratio into single "Recipe" column
- Reorder columns: Coffee, Grind, Recipe, Temp, Brewer, Date, Actions
- Style actions column with icon buttons (consistent with roasts)
- Add "brew again" button to repeat a brew with same parameters
- Use data-model for form inputs (fixes Datastar binding error)
- Add raw values to BrewView for repeat brew functionality
- Test create, list, get, delete operations
- Test bag deduction and conflict on insufficient coffee
- Test CLI commands with defaults
- Add create_default_gear helper
- Add BrewView, BagOptionView, GearOptionView
- Add brews page with form and +/- adjustment buttons
- Add brew list partial with sortable columns
- Add Brews link to navigation
- Add SqlBrewRepository with transactional insert that deducts from bag
- Return Conflict error when insufficient coffee in bag
- Add BrewsClient for CLI HTTP operations
- 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
- 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
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).
- 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.
- 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.
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
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.
- 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
- 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
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.
- 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
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).