Commit graph

201 commits

Author SHA1 Message Date
Jon Seager
a3aba1b02a
feat(home): replace scan page with home dashboard and check-in flow
- Add home page at / with scan, last brew, open bags, activity, stats
- Add check-in page at /check-in with cafe search + roast scan + rating
- Use Datastar signals for check-in UI state (steps, rating, selection)
- Bridge async JS (geolocation, fetch) to Datastar via custom events
- Replace nav camera icon with house icon (always visible)
- Redirect /scan to / for backward compatibility
- Delete scan.html, add home.html, checkin.html, checkin.js
2026-02-04 12:55:08 +00:00
Jon Seager
c046be4e33
fix(templates): right-align Actions column header in brew list partial 2026-02-04 12:15:25 +00:00
Jon Seager
3ae3963c5b
fix(templates): align table position and structure across all list pages
Move list partials outside the section element on bags, gear, and brews
pages so they become separate flex children of <main>, matching the
pattern used by roasters, roasts, cafes, and cups. Add mt-6 to each
partial's outer div for consistent spacing.

Standardise brew and gear list partials to match the canonical structure:
empty-state conditional with dashed amber border, <section> wrapper for
the table, and data-star-scope attribute on the outer div.

Update CLAUDE.md to document page template and list partial structure
requirements, preventing future drift.
2026-02-04 12:04:09 +00:00
Jon Seager
f4090ba329
fix(bootstrap): remove blank lines breaking cup command continuations
When cup notes were removed, the blank lines left between --cafe-id
and --rating broke shell line continuations, causing --rating to be
interpreted as a command.
2026-02-03 21:21:00 +00:00
Jon Seager
4f7d31d47a
refactor(roasters): remove notes field from Roaster entity
- Add migration 0016 to drop notes column from roasters table
- Remove notes from domain structs, repositories, views, CLI, routes
- Remove notes from AI extraction prompts and ExtractedRoaster struct
- Remove notes from scan page form and submission handler
- Update templates, bootstrap script, backup module, and tests
2026-02-03 21:18:48 +00:00
Jon Seager
ae82e8a16e
feat(timeline): show "View on Map / Copy" for cafe position
Replace raw coordinates with a "View on Map" link and a "Copy" button
that copies lat/lon to the clipboard.
2026-02-03 21:17:06 +00:00
Jon Seager
ae3c1090de
refactor(cafes,cups): remove notes field from Cafe and Cup entities
- Add migrations 0014 and 0015 to drop notes columns
- Remove notes from domain structs, repositories, views, CLI, routes
- Remove notes textarea/columns from web templates
- Update bootstrap script, tests, backup module, and README
2026-02-03 21:09:34 +00:00
Jon Seager
23817ff95a
docs(claude): add bootstrap script update step to workflow requirements 2026-02-03 21:05:03 +00:00
Jon Seager
fb31d0a0c4
refactor(cafes): remove notes field from Cafe entity
- Add migration 0014 to drop the notes column from cafes table
- Remove notes from domain structs, repository, view model, CLI, routes
- Remove notes textarea/column from web templates
- Update bootstrap script and all test files
2026-02-03 21:02:37 +00:00
Jon Seager
ca4d382086
docs: add LICENSE 2026-02-03 20:53:45 +00:00
Jon Seager
2582970edf
docs(claude): add README update step to workflow requirements 2026-02-03 20:51:16 +00:00
Jon Seager
267b843a37
docs: update README with current CLI commands and features
- Fix CLI examples to use subcommand style (e.g. brewlog roaster add)
- Document all environment variables including .env file support
- Add sections for AI extraction, Foursquare nearby search, database
  feature flags, and backup/restore
2026-02-03 20:51:01 +00:00
Jon Seager
698c4e93c5
fix(bootstrap): use "United Kingdom" instead of "UK" for consistency
The Foursquare integration normalises ISO country codes to full names
(GB → United Kingdom). Align the bootstrap seed data to match.
2026-02-03 20:45:02 +00:00
Jon Seager
82f29d9cbd
docs: document Foursquare integration and update architecture tree
- Add ai/ and foursquare.rs to infrastructure layer in architecture diagram
- Add Foursquare Integration section covering SearchLocation enum, API
  details, config, and wiremock testing pattern
- Note wiremock external API mocking in Conventions
2026-02-03 20:43:10 +00:00
Jon Seager
5f3507fa92
feat(nearby): add city-based search via Foursquare near param
- Add SearchLocation enum to support coordinates or named location
- Accept optional `near` query param as alternative to lat/lng
- Add city text input with checkbox toggle in cafes template
- Foursquare `near` param enables searching any city worldwide
2026-02-03 20:36:59 +00:00
Jon Seager
ecb010e812
refactor(nearby): replace Nominatim with Foursquare Places API
- Replace osm.rs with foursquare.rs using Foursquare Places Search API
- Add isocountry crate for ISO 3166-1 country code to name conversion
- Override verbose country names (e.g. "United Kingdom" instead of
  "United Kingdom of Great Britain and Northern Ireland")
- Add BREWLOG_FOURSQUARE_API_KEY env var for API authentication
- Update route handler, tests, and template to use Foursquare
- Modernise cafes template JS to ES6+ (const/let, arrow fns, template
  literals)
2026-02-03 20:36:11 +00:00
Jon Seager
63765f17d7
docs: document static asset serving, AI extraction UI, and JS conventions 2026-02-03 19:33:17 +00:00
Jon Seager
3746568dd7
refactor(web): extract shared AI extraction JS into standalone library
Move duplicated photo capture, text extraction, and fetch/UI-toggle
logic from roasters, roasts, and scan templates into a shared
extract.js file served at /extract.js. Each page now provides only
its form-filling callback. Uses ES6+ syntax (const, let, arrow
functions, template literals).
2026-02-03 19:31:00 +00:00
Jon Seager
e3db350609
feat(ui): show waiting state during AI extraction
Hide input controls and display a spinner with "Waiting for
response..." while LLM extraction requests are in-flight. Controls
are restored on completion or failure. Applied consistently across
scan, roaster, and roast forms.
2026-02-03 19:25:19 +00:00
Jon Seager
3e3c57a07f
fix(ai): ensure tasting notes are returned in title case
Update ROAST_PROMPT to explicitly request tasting notes in Title Case
so extracted values like "dark chocolate" come back as "Dark Chocolate".
2026-02-03 19:15:49 +00:00
Jon Seager
84ffe36afc
feat(scan): add bag scanning page with combined AI extraction
Add /scan page that lets authenticated users photograph or describe a
coffee bag, extracts both roaster and roast data via a single AI call,
and creates both entities on submit. Existing roasters are matched by
slug to avoid duplicates. Camera icon added to nav bar.
2026-02-03 19:15:39 +00:00
Jon Seager
023ad1ac31
refactor(web): add has_ai_extract flag to all page templates
Propagate has_ai_extract boolean through every template struct and
route handler so nav.html can conditionally show the scan link.
2026-02-03 19:14:47 +00:00
Jon Seager
b668d368e7
feat(ai): add AI-powered extraction for roaster and roast forms
Users can upload a photo of a coffee bag or type a text prompt to
auto-fill roaster/roast form fields via OpenRouter's chat completions
API. The feature is opt-in: hidden when BREWLOG_OPENROUTER_API_KEY is
not set.

- Add src/infrastructure/ai.rs with OpenRouter client, prompt
  templates, JSON extraction from model responses, and unit tests
- Add extract-roaster and extract-roast API endpoints behind auth
- Add extraction UI (photo button + text input) to both form templates
- Add openrouter_api_key and openrouter_model to CLI args and AppState
- Enable reqwest gzip feature for compressed API responses
- Default model set to openrouter/free (auto-selects available models)
- Prompts instruct model to use web search for missing details
2026-02-03 18:00:19 +00:00
Jon Seager
a4da0598f6
feat: load environment variables from .env file via dotenvy
Add dotenvy::dotenv() call before CLI argument parsing so that
environment variables (e.g. API keys) can be set in a .env file
instead of the shell environment. Add .env to .gitignore.
2026-02-03 18:00:09 +00:00
Jon Seager
acaadbbcf6
refactor(web): split views.rs into entity-specific submodules
- Move entity view models into views/{entity}.rs submodules
- Keep Paginated and ListNavigator in views/mod.rs as shared infrastructure
- Re-export all types from mod.rs to preserve existing import paths
2026-02-03 16:58:44 +00:00
Jon Seager
714713827d
refactor(routes): extract form option helpers and fragment renderer macro
- Add load_roaster_options, load_roast_options, load_cafe_options
  helpers to support.rs, replacing duplicated inline loading in
  roasts.rs, bags.rs, and cups.rs
- Add define_list_fragment_renderer! macro for the 5 structurally
  identical render_*_list_fragment functions
- Fix gear.rs to return navigator from load_gear_page like all other
  loaders instead of discarding and reconstructing it
2026-02-03 16:49:36 +00:00
Jon Seager
4b55d1b65c
refactor(web): extract map_details to fix too-many-lines clippy warning
Extract the detail-mapping loop from TimelineEventView::from_domain
into a private map_details helper method, bringing the function under
clippy's 100-line limit.
2026-02-03 16:39:11 +00:00
Jon Seager
9420711a13
feat(bootstrap): add 22 cups across all 10 cafes to seed script
- Pair roasts with cafes visited, with tasting notes and ratings
- Timestamps align with cafe visit dates across the 6-month spread
- Include cup timeline event timestamp distribution
2026-02-03 16:36:45 +00:00
Jon Seager
114f68177a
test(cups): add integration tests for cups API
- Test CRUD operations: create, list, get, update, delete
- Test auth requirement, optional fields, enriched data
- Test validation: no changes returns 400, nonexistent returns 404
- Update test helpers with cup_repo wiring
2026-02-03 16:36:40 +00:00
Jon Seager
6e2c59814c
feat(cups): add web views, templates, and navigation
- Add CupView, CafeOptionView, RoastOptionView view models
- Add CupsTemplate (full page) and CupListTemplate (fragment)
- Create cups.html with form and cup_list.html with responsive table
- Add Cups link to desktop and mobile navigation
- Add cup timeline event link and label support
2026-02-03 16:36:35 +00:00
Jon Seager
4e644d5528
feat(cups): add CLI commands for cup management
- Add cup add/list/get/update/delete subcommands
- Wire Cup variant into Commands enum and main dispatch
2026-02-03 16:36:27 +00:00
Jon Seager
a6fad343fc
feat(cups): add route handlers and wire into application state
- Add cup_repo to AppState and server startup
- Register API routes: GET/POST /cups, GET/PUT/DELETE /cups/:id
- Register web route: GET /cups with Datastar fragment support
- Rating validation (1-5) on create and update
2026-02-03 16:36:23 +00:00
Jon Seager
c2e9c7395c
feat(cups): add HTTP client for cups API
- Add CupsClient with create, list, get, update, delete methods
- Register cups() accessor on BrewlogClient
2026-02-03 16:36:14 +00:00
Jon Seager
928c4c9aea
feat(cups): add SQL repository with timeline event integration
- Implement CupRepository trait for SqlCupRepository
- Transactional insert creates cup and timeline event atomically
- Support filtering by cafe_id/roast_id, search across roast/roaster/cafe names
- Use QueryBuilder with push_update_field! macro for updates
2026-02-03 16:36:10 +00:00
Jon Seager
ac7a4f9cf9
feat(cups): add migration and domain layer for cup entity
- Create cups table with roast_id/cafe_id FKs, optional notes and rating
- Update timeline_events CHECK constraint to include 'cup' entity type
- Add CupId typed wrapper, Cup/CupWithDetails/NewCup/UpdateCup structs
- Add CupFilter, CupSortKey, and CupRepository trait
2026-02-03 16:35:56 +00:00
Jon Seager
5e2ee63a1e
test(cafes): add integration tests for nearby cafe search API
- Add wiremock dev-dependency for HTTP mocking
- Make Nominatim URL configurable via AppState for test injection
- Add spawn_app_with_nominatim_mock() test helper
- Add 6 integration tests covering success, empty results,
  authentication, validation, and upstream failure scenarios
2026-02-03 16:03:54 +00:00
Jon Seager
a938e9bcd3
feat(cafes): add geolocation search UI to new cafe form
- Add "Find nearby" button that requests browser geolocation
- Show search input with debounced type-ahead after locating user
- Render clickable results with name, location, and distance
- Auto-fill form fields (name, city, country, lat, lng, website)
  when a result is selected
- Show loading spinner in search input during API requests
2026-02-03 15:56:35 +00:00
Jon Seager
4cd1203864
feat(cafes): add nearby cafe search via Nominatim API
- Add OSM infrastructure module with Nominatim forward search, viewbox
  location bias, and haversine distance calculation
- Add reqwest::Client to AppState for server-side HTTP requests
- Add GET /api/v1/nearby-cafes endpoint (authenticated) accepting
  lat, lng, and text query parameters
- Check website, contact:website, url, contact:url, and brand:website
  tags to maximise website coverage from OSM data
2026-02-03 15:56:28 +00:00
Jon Seager
3ab1c50cc8
refactor(timeline): remove map pin icon from timeline title
Position is already shown as a clickable detail row in the card body,
making the title icon redundant. Also removes the unused map_link field
from TimelineEventView.
2026-02-03 15:10:21 +00:00
Jon Seager
fff11b0512
feat(bootstrap): add 10 cafes across London, Madrid, Berlin, Munich and Bristol
Add cafe entries distributed over the last 6 months with timestamp
updates for both cafes and timeline events. Add .timeout to sqlite3
heredoc to handle database lock contention.
2026-02-03 14:53:18 +00:00
Jon Seager
069e861255
test(cafes): add API, CLI, Datastar, timeline, and backup tests
Add 14 server API tests, 6 CLI tests, 4 Datastar fragment tests,
1 timeline event test, and backup round-trip verification. Wire
cafe_repo into test helpers.
2026-02-03 14:53:08 +00:00
Jon Seager
3f83d74e20
feat(cafes): add CLI commands, web views, and templates
Add CLI subcommands (add, list, get, update, delete) with negative
number support for coordinates. Add CafeView with map URL generation,
Askama templates for list/detail pages, and nav link between Brews
and Gear.
2026-02-03 14:53:00 +00:00
Jon Seager
1781bff48d
feat(cafes): add route handlers and wire into application state
Add cafe route handlers (CRUD API + web pages with Datastar fragment
support). Register cafe_repo in AppState and mount API and web routes.
2026-02-03 14:52:51 +00:00
Jon Seager
677b77d70d
feat(cafes): add SQL repository, HTTP client, and backup support
Implement SqlCafeRepository with CRUD operations and timeline event
creation. Add CafesClient for CLI HTTP access. Wire cafes into backup
export/restore and empty-database verification.
2026-02-03 14:52:44 +00:00
Jon Seager
c6ad6a02c7
feat(cafes): add migration and domain layer for cafe entity
Add cafes table with name, slug, city, country, latitude, longitude,
website, and notes fields. Define domain types (Cafe, NewCafe, UpdateCafe),
CafeId typed wrapper, CafeRepository trait, and CafeSortKey enum.
2026-02-03 14:52:37 +00:00
Jon Seager
f1e8984ffa
refactor(roasts): extract helpers to remove too_many_lines suppression
- Add empty_to_none() to replace four repeated empty-string-to-None blocks
- Extract insert_timeline_event() from insert() to isolate timeline logic
- Remove #[allow(clippy::too_many_lines)] from RoastRepository impl
2026-02-03 14:09:53 +00:00
Jon Seager
09a629db9a
feat(brews): pre-populate Add Brew form with last recorded brew values
- Add BrewDefaultsView to carry last brew's values to the template
- Extract form data loading into load_brew_form_data() helper
- Use defaults for signal initialization and dropdown pre-selection
- Retain submitted values after form submission instead of resetting
2026-02-03 14:01:41 +00:00
Jon Seager
fa0cccade0
docs(claude): update CLAUDE.md to match current codebase
- Fix stale fragment rendering example (into_request → into_request_and_search)
- Correct domain conversion convention to to_domain() and fold into Repository Pattern
- Add listing.rs and missing ID types to architecture tree
- Document define_enriched_get_handler! macro
- Add Route Module Structure pattern (load/page/fragment trio, build_page_view)
- Note submission types vs direct domain structs in FlexiblePayload
2026-02-03 13:50:47 +00:00
Jon Seager
546e105b10
fix(templates): prefix Datastar signals with underscore to mark as local
Datastar sends all non-underscore signals with every backend request.
UI-only signals (showForm, navOpen, isSubmitting, form ref, brew
parameters) were being serialized into pagination/search/sort URLs,
creating bloated requests. Prefix them with _ so Datastar's default
filterSignals excludes them from requests.
2026-02-03 13:37:50 +00:00
Jon Seager
16dd72b624
fix(timeline): restore sticky month headers and jump-to-top button
Remove wrapper div around month headings so the h2 is a direct child
of the timeline container. position: sticky was broken because the
heading's containing block (the wrapper) was only as tall as the
heading itself, giving it no room to stick.

Also update MutationObserver to detect headings added as direct
children via node.matches() in addition to node.querySelector().
2026-02-03 13:29:46 +00:00