Commit graph

208 commits

Author SHA1 Message Date
Jon Seager
af13ee472a
fix: brew again on homepage should be instant without reload 2026-02-05 21:56:28 +00:00
Jon Seager
f1cdf75eaf
refactor(templates): replace manual JS with Datastar patterns
- Convert data.html search from manual fetch+debounce to Datastar @get
- Replace filterList() on checkin page with <searchable-select> component
- Convert account.html form show/hide from classList to data-show signals
- Rename _scanWaiting/_scanError to _extracting/_extractError for consistency
- Delete unused render_signals_fragment() and escape_html_attr() from support.rs
- Document Datastar vs JS guidelines in CLAUDE.md
2026-02-05 21:42:14 +00:00
Jon Seager
ba71bb69c5
style: update design of recent brew card 2026-02-05 21:20:38 +00:00
Jon Seager
8777ceb25b
fix(brews): carry over quick notes when using "Brew Again"
- Add quick_notes_raw field to BrewView with comma-separated form values
- Include quick_notes hidden input in all three brew-again forms
  (homepage, brew list desktop, brew list mobile)
2026-02-05 21:18:11 +00:00
Jon Seager
6b1d6a3187
fix(ui): tone down token-created success banner in dark mode
Add dark: variants for the green success banner on the account page
so it doesn't appear overly bright against the dark background.
Also tidy .gitignore to catch all .db and .db-journal files.
2026-02-05 21:06:42 +00:00
Jon Seager
709adcdd3e
fix(ui): tokenize neutral text colors for dark mode contrast
Replace ~268 hardcoded text-stone-* classes with theme-aware token
utilities (text-text, text-text-secondary, text-text-muted) across all
templates and JS components. Adjust dark mode token values to meet
WCAG AA contrast ratios on dark surfaces.
2026-02-05 20:57:59 +00:00
Jon Seager
1377d43935
fix: resolve clippy warnings for dead field and struct prefix
- Remove unused passkey_name from RegisterStartRequest (only used in RegisterFinishRequest)
- Rename AiUsageView fields from total_calls/total_tokens/total_cost to calls/tokens/cost
2026-02-05 20:49:17 +00:00
Jon Seager
fbc2d66a84
refactor(home): move quick notes pills to card footer with icon-only brew again
- Move quick notes pills into border-t footer row, left-aligned
- Show "No Notes" muted pill when brew has no quick notes
- Replace text "Brew Again" button with compact refresh icon button
- Footer now always visible (pills + optional brew-again action)
2026-02-05 20:46:07 +00:00
Jon Seager
010b700595
feat(brews): add quick notes
Predefined toggleable tags for brews: Good, Too Fast, Too Slow,
Too Hot, Under Extracted, Over Extracted.

- Add quick_notes TEXT column storing JSON array
- Add QuickNote enum with label/form-value/positive helpers
- Update repository to encode/decode JSON, backup to export notes
- Add toggle pill UI on brew form with Datastar signals
- Show pills on home page cards (muted for Good, amber for others)
- Show comma-joined text in data table Notes column
- Add --quick-notes flag to CLI, update HTTP client
- Update bootstrap script with sample quick notes
2026-02-05 20:42:03 +00:00
Jon Seager
3bc6fae656
refactor(ui): improve data table detail rows and mobile cards
- Split created_at into created_date + created_time across all view models
- Add inline card-detail pattern for mobile action buttons
- Add desktop detail-row with ellipsis toggle for table actions
- Add pill-warning and pill-success CSS variants with dark mode
- Center mobile card-detail buttons, fix card-date vertical spacing
- Rename action labels: "View on Map" → "View Map", "Visit website/homepage" → "Homepage"
2026-02-05 20:40:38 +00:00
Jon Seager
56109f3afe
refactor: reorganize templates into pages/, partials/lists/, and forms/
Move 9 page templates to templates/pages/, nav to partials/,
7 list partials + table.html to partials/lists/. Extract duplicated
scan result form (~85 lines) from home.html and add.html into
partials/forms/scan_result_form.html. Update all Askama template
paths, include/import directives.
2026-02-05 17:48:18 +00:00
Jon Seager
4e917cee75
refactor: move static assets out of templates/ into static/
Move CSS, JS, favicon files to static/ directory to separate
compiled-in assets from Askama templates. Update all include paths,
build.rs, flake.nix, and Tailwind @source directives.
2026-02-05 17:47:32 +00:00
Jon Seager
4a54834424
refactor(ui): extract reusable tab bar component
- Create shared tab_bar.html partial with desktop + mobile layouts
- Add CSS component classes (.tab, .tab-active, .tab-mobile variants)
- Replace divergent implementations in data.html and add.html
- Rename DataTab to Tab for shared use across templates
- Improve readability: darker inactive text, more spacing, hover states
2026-02-05 17:23:50 +00:00
Jon Seager
fb67f0659c
feat(ui): redesign with theme tokens, dark mode, and reusable components
Overhaul the web UI with CSS custom property-based theming and
dark mode support. Extract reusable template partials (location
search, scan input) and web components (photo-capture,
searchable-select). Add version and commit info to page footer.

- Replace hardcoded amber palette with semantic theme tokens
- Add dark mode with localStorage persistence and system preference detection
- Extract <brew-photo-capture> and <searchable-select> web components
- Extract location_search and scan_input Askama macros
- Add sun/moon, timeline, database, map icons
- Display version and git commit in footer
- Improve timeline coordinate rounding and map link formatting
- Add roast_name, roaster_name, remaining to BagOptionView
2026-02-05 17:03:09 +00:00
Jon Seager
33c27ed0d0
build: add Tailwind CSS v4 build pipeline with theme tokens
Replace the checked-in styles.css with a build-time Tailwind CSS
pipeline. input.css defines CSS custom properties for light/dark
themes, mapped to Tailwind utilities via @theme. build.rs runs
the Tailwind CLI to generate styles.css at compile time.
2026-02-05 17:02:13 +00:00
Jon Seager
4fc81dd0c5
style(templates): modernize JS to arrow functions and const/let
Convert all remaining function declarations, var usage, and string
concatenation across base, add, checkin, and data templates.
2026-02-05 12:45:52 +00:00
Jon Seager
5acbddd65b
refactor(account): simplify passkey/token JS and modernize syntax
Replace DOMContentLoaded event-listener block with global arrow
functions and inline onclick handlers. Convert string concatenation
to template literals throughout.
2026-02-05 12:43:03 +00:00
Jon Seager
149921dc31
refactor(account): simplify backup/restore with native download link
Replace the blob URL download JS with a Content-Disposition header on
the export endpoint and a plain <a> tag. Consolidate restore JS from
event listeners into a single global function.
2026-02-05 12:27:17 +00:00
Jon Seager
9eb9a9a56c
feat(account): display OpenRouter usage on account page
Show cumulative cost, API call count, and total tokens in card
layout when the user has AI usage data.
2026-02-05 12:09:43 +00:00
Jon Seager
69b039b02d
feat(account): add backup download and restore UI
Add a Data section to the account page with buttons to download
a JSON backup and restore from a previously exported backup file.
2026-02-05 11:51:36 +00:00
Jon Seager
6a0d94739b
feat(auth): allow naming the initial passkey during registration
- Add "Passkey Name" field to the registration form
- Pass passkey_name through start/finish WebAuthn ceremony
- Use user-provided name instead of hardcoded "default"
2026-02-05 11:24:52 +00:00
Jon Seager
16a4704fcf
feat(account): add account page with passkey and token management
- Add /account page with passkey list, token list, and sign-out
- Replace nav login/logout buttons with user icon linking to /account or /login
- Add passkey management: add new passkeys (WebAuthn ceremony), delete with confirmation
- Add token management: create, copy one-time value, revoke with confirmation
- Add user/key/clipboard icon macros
- Add PasskeyCredentialRepository::get() for ownership verification
- Add WebAuthn passkey/start and passkey/finish endpoints for adding passkeys
2026-02-05 11:24:43 +00:00
Jon Seager
25705ea0c0
fix(ui): update empty state messages based on auth status
Show a simple "No X added yet" for unauthenticated visitors.
Authenticated users see a prompt to click the Add button.
2026-02-05 11:14:39 +00:00
Jon Seager
e6d9f861e6
style(tables): normalize text styling to match brews table
Replace bold amber (font-semibold text-amber-800) with medium stone
(font-medium text-stone-800) across roaster, roast, cafe, and cup
list templates to match the brews table convention.
2026-02-05 11:11:44 +00:00
Jon Seager
03e03d87d9
feat(auth): replace password auth with WebAuthn passkeys
Replace username/password authentication with FIDO2/WebAuthn passkey-based
auth using webauthn-rs. Sessions and bearer tokens are unchanged — only the
way they are created changes.

- Add webauthn-rs, uuid, open, url deps; remove argon2, rpassword
- Add passkey_credentials and registration_tokens tables (migrations 17-18)
- Add domain entities, typed IDs, and repository traits for passkeys/tokens
- Add SQL repository implementations for passkeys and registration tokens
- Add ChallengeStore for in-memory WebAuthn ceremony state
- Add WebAuthn route handlers (register/auth start+finish ceremonies)
- Add CLI browser handoff for token creation (opens browser, local callback)
- Replace login form with "Sign in with Passkey" button
- Add registration page for first-user bootstrap via one-time token
- Replace BREWLOG_ADMIN_USERNAME/PASSWORD with BREWLOG_RP_ID/RP_ORIGIN
- Change default BREWLOG_URL from 127.0.0.1 to localhost (WebAuthn requires it)
2026-02-05 11:00:07 +00:00
Jon Seager
8c521f0e05
feat(data): redesign mobile data page with card layout and tab selector
- Add collapsible mobile tab selector with chevron icons
- Integrate search bar into tab component (below tabs)
- Replace Datastar search handler with vanilla JS for reliability
- Add mobile card layout: card-title, card-date, card-actions classes
- Add three-dot action menu for mobile with details/summary pattern
- Hide duplicate search inside data-content via CSS
- Pass search_value to DataTemplate for input preservation
2026-02-04 22:22:18 +00:00
Jon Seager
2c045f9335
fix(templates): remove dead detail-page links from list tables
The detail page routes and templates were removed but list templates
still linked to them. Replace the <a> tags with plain text to avoid
navigating to non-existent pages.
2026-02-04 20:53:19 +00:00
Jon Seager
2eca0ae3c6
refactor(bags): replace dual-section bag list with single paginated table
Remove the open-bags card grid and closed-bags history table in favour
of a unified table showing all bags with status badges and actions.

- Simplify load_bag_page to single BagFilter::all() query
- Remove open_bags field from BagPageData and BagListTemplate
- Add Status column with Open/Closed badges and remaining weight
- Add Close and Delete action buttons per row
2026-02-04 20:53:12 +00:00
Jon Seager
4bdfc661f9
refactor: consolidate entity pages into unified /data and /add views
Replace per-entity pages (/roasters, /roasts, /bags, /brews, /gear,
/cafes, /cups) and detail pages with a single tabbed /data view and
a dedicated /add page for entity creation.

- Add /data route with tab-based navigation using Datastar
- Add /add route consolidating all create forms
- Remove per-entity page handlers and standalone templates
- Remove detail page routes, handlers, and templates
- Update ListNavigator to accept String paths for query-param URLs
- Update home page and timeline links to use new /data?type=X paths
- Update nav to reference /data instead of individual entity pages
2026-02-04 20:51:30 +00:00
Jon Seager
939743a224
feat(home): add inline detail subtitles to timeline preview cards
Show contextual details inline with event titles on home page timeline
cards. Each event type picks relevant fields: brew shows roaster and
brewer, roast shows roaster and origin, cafe shows city and country,
cup shows roaster and cafe, others show first three detail values.
2026-02-04 19:14:30 +00:00
Jon Seager
cdf90d1ff3
feat(home): add relative dates to timeline and brew cards
- Add relative_date() helper to views module for humanized timestamps
- Show relative dates on home page timeline preview and brew cards
- Format: "Just now", "Xm ago", "Xh ago", "Yesterday", "Xd/Xw ago"
2026-02-04 19:03:05 +00:00
Jon Seager
ba7a38b16f
refactor(home): redesign stats section with icon pills and responsive layout
- Replace single bordered card with compact pill-style stat links
- Add Heroicons for each stat category (beaker, fire, building, bag, cup, location, wrench)
- Rename old bag icon to cup, add new shopping-bag icon for bags
- Use 2-column grid on mobile, centered flex wrap on desktop
- Add min-width on numbers to align labels consistently
2026-02-04 18:54:37 +00:00
Jon Seager
ef039da123
fix(home): prevent text wrapping in brew and bag card detail fields 2026-02-04 17:49:57 +00:00
Jon Seager
37cd595c93
fix(home): truncate long names in brew and bag cards
Add text truncation with ellipsis to roast and roaster names in the
recent brews and open bags cards to prevent layout inconsistency when
titles wrap.
2026-02-04 17:43:36 +00:00
Jon Seager
91a9900c24
feat(scan): optionally open a bag when scanning coffee
Add "Open a bag of this coffee" checkbox (default: checked) to the
scan form with a configurable amount field (default: 250g). When
enabled, creates a bag and timeline event alongside the roaster/roast.
2026-02-04 17:41:53 +00:00
Jon Seager
ec62a1fc7b
fix(scan): clear signals and reset form on repeated extractions
Clear all roaster/roast signals before each extraction request to
prevent stale values from persisting across consecutive scans. Also
reset the extract form on completion to clear the prompt input.
2026-02-04 17:41:46 +00:00
Jon Seager
249d9c2583
refactor(home): consolidate scan results form and improve save behavior
- Merge roaster and roast sections into single bordered box
- Move cancel and save buttons inside the form border
- Reset form and reload homepage after save instead of redirecting to /roasts
2026-02-04 17:29:26 +00:00
Jon Seager
327a820b52
fix(home): simplify scan bag input layout
- Use camera icon-only button instead of "Take Photo" text
- Align camera button and text input horizontally
- Remove Go button, form submits on Enter
2026-02-04 17:28:24 +00:00
Jon Seager
52084be7ed
refactor(nav): replace login and logout text with icons
- Add login and logout icon macros to shared icons partial
- Desktop nav uses icon-only with aria-label for both
- Mobile nav uses icon + text for both
2026-02-04 17:21:58 +00:00
Jon Seager
e508af4c52
feat(nav): add check-in icon to navigation bar
- Add checkin map-pin icon to shared icons partial
- Add check-in nav link (icon on desktop, icon + text on mobile)
- Only shown when authenticated
- Set nav_active to "checkin" on the check-in page
2026-02-04 17:14:52 +00:00
Jon Seager
ebd2698b42
feat(home): redesign home page with recent brews cards and inline scan
- Replace single "Last Brew" with grid of 3 recent brew cards
- Show brew details with emphasis on weight/volume, grind, and brewer
- Add "Brew Again" action matching bag card button style
- Replace quick-action buttons with always-visible "Scan Bag" section
- Fix activity chip wrapping by widening to w-28 with whitespace-nowrap
- Increase section gap from gap-6 to gap-8 for better visual spacing
2026-02-04 17:14:46 +00:00
Jon Seager
62740c4c68
fix(home): move Last Brew heading outside card for consistent section styling 2026-02-04 16:43:45 +00:00
Jon Seager
836b241977
fix(home): align recent activity chips to fixed width for consistent text positioning 2026-02-04 16:40:54 +00:00
Jon Seager
3bbcd33933
refactor(templates): extract inline SVG icons into shared icons partial
Replace duplicated inline SVGs across 16 templates with reusable Askama
macros from a new icons.html partial, reducing repetition and making
icon updates a single-point change.
2026-02-04 16:32:18 +00:00
Jon Seager
a31c91211c
refactor: require OpenRouter and Foursquare API keys at startup
- Remove has_ai_extract/has_foursquare conditionals from all templates
- Remove boolean fields from template structs and route handlers
- Remove has_ai_extract()/has_foursquare() methods from AppState
- Change API key fields from Option<String> to String in ServerConfig
  and AppState
- Validate keys in run_server() with clear error messages
- Remove runtime key checks from extraction/nearby route handlers
- Update README and CLAUDE.md to reflect required configuration
2026-02-04 16:13:04 +00:00
Jon Seager
98c8391670
refactor(templates): extract shared bag card partial for open bags
- Create bag_card.html macro used by both home page and bags page
- Replace divergent card styles with consistent amber-themed design
- Add closeBag() JS to bags page for closing bags with confirmation
- Use "Close" button with cross icon consistently across both pages
2026-02-04 16:01:57 +00:00
Jon Seager
10c19b1f67
fix(templates): align cancel button styling across all form pages
- Update bags, brews, and gear cancel buttons to match cafes/roasters style
- Use amber-300 border with stone-700 text for consistent cancel button appearance
2026-02-04 16:01:44 +00:00
Jon Seager
e9bb291f83
fix(templates): use amber outline style for secondary action buttons
- Update btn-adjust CSS from white/stone to amber border with transparent bg
- Apply consistent amber-500 border, amber-700 text across all action buttons
- Affected pages: roasters, roasts, home, cafes, check-in, brews
2026-02-04 16:01:26 +00:00
Jon Seager
fa5386c671
feat(checkin): add roast/cafe summary bars and data attribute selects
- Add roast name and roaster name signals to check-in page
- Show selected cafe city as subtext in cafe summary bar
- Add roast summary bar showing roast name and roaster after selection
- Use data attributes on select options for structured cafe/roast data
- Add name/city fields to CafeOptionView and name/roaster_name to RoastOptionView
- Return roaster name signal from scan endpoint for roast summary display
2026-02-04 16:00:53 +00:00
Jon Seager
0309ee08df
feat(checkin): add city search, cafe review form, and UX improvements
- Add "Search by city" toggle as alternative to GPS location
- Replace saved cafes button list with select dropdown
- Show editable review form when selecting a Foursquare result
- Guard all datastar-fetch handlers with in-progress signals
- Fix step indicator spacing (mt-4 on steps 2 and 3)
2026-02-04 15:02:20 +00:00
Jon Seager
2a94b87d49
refactor: remove extract.js and checkin.js
All extraction and check-in interactivity is now handled by Datastar
signals and inline FileReader JS. No separate JavaScript files remain.
2026-02-04 14:37:04 +00:00
Jon Seager
e4a304b406
feat(checkin): Datastar-native check-in and nearby cafe search
- Add POST /api/v1/check-in endpoint with CheckInSubmission
- nearby_cafes returns HTML fragment for Datastar, JSON for API
- Add NearbyCafeView and NearbyCafesFragment for server-rendered results
- Rewrite checkin.html: replace checkin.js with Datastar signals,
  @post for submission, @get for nearby search, inline geolocation JS
- Cafe filtering uses data-show with signal-based search
2026-02-04 14:36:55 +00:00
Jon Seager
bc30555447
refactor(extraction): Datastar-native AI extraction across all pages
Extraction endpoints now accept FlexiblePayload (form or JSON) and
return application/json signal patches for Datastar requests. Templates
use data-bind for two-way signal binding and data-on:datastar-fetch
with per-form guards to prevent event bubbling between forms.

- extract_roaster, extract_roast_info, extract_bag_scan return signals
- submit_scan extended with optional image/prompt for combined extract+save
- match_roaster_id fuzzy-matches extracted roaster name to existing IDs
- Templates use data-bind:_signal-name (not data-model) for form binding
- Each datastar-fetch handler guards with its own in-progress signal
2026-02-04 14:36:42 +00:00
Jon Seager
bb040ed153
fix(timeline): centre month headers on mobile view 2026-02-04 13:07:32 +00:00
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
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
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
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
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
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
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
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
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
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
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
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
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
Jon Seager
145ddb4765
fix(timeline): centre line behind cards on mobile for full-width layout
- Move timeline line to centre on mobile instead of left gutter
- Remove left padding from cards to maximise horizontal space
- Reposition dots to top-centre of each card border on mobile
- Make card background fully opaque so line doesn't show through
- Desktop alternating layout unchanged
2026-02-03 13:21:55 +00:00
Jon Seager
3475c91273
fix(brews): prevent New Brew form from overflowing on mobile
- Add width: 100% and min-width: 0 to .input-field so selects/inputs
  fill their container instead of expanding to fit content
- Add overflow-hidden to the brews form as a safety net
2026-02-03 13:14:12 +00:00
Jon Seager
35e8460219
fix(brews): lay out action icons horizontally and merge temp into recipe
Wrap the brew-again form and delete button in an inline-flex container
to match the horizontal layout used in other tables. Fold water
temperature into the Recipe column as desktop subtext alongside the
ratio, reducing the table from 7 to 6 columns and eliminating the
horizontal scrollbar. Document the Actions column pattern in CLAUDE.md.
2026-02-03 13:08:07 +00:00
Jon Seager
2d27228059
refactor(tables): standardise Added column as first column across all tables
Move the Added/Date column to first position in brew, gear, and bag
list templates and apply consistent text-xs font-medium text-stone-600
styling. Document the pattern in CLAUDE.md.
2026-02-03 13:03:43 +00:00
Jon Seager
087ff00177
fix(mobile): better layout for brews table 2026-02-03 12:58:37 +00:00
Jon Seager
8fe88c8ff6
fix(mobile): hide infinite scroll sentinel on desktop
The sentinel div (h-4) added extra space below the pagination bar
on pages with a next page, making the pagination bar appear taller
than on the last page. Since infinite scroll only runs on mobile,
hide the sentinel on desktop with md:hidden.
2026-02-03 12:52:23 +00:00
Jon Seager
8213543ed3
feat(roasters): consolidate table columns and restyle actions
- Combine Country/City into Location column with city as desktop subtext
- Add City as separate mobile-only field for card layout
- Move homepage icon into Actions column alongside delete button
- Remove bordered style from homepage icon to match other action icons
2026-02-03 12:52:14 +00:00
Jon Seager
39598c1709
fix(search): keep search bar visible when search returns no results
- Change empty state condition to also check navigator.has_search()
- Show "No X match your search" message inside the table container
- Applied to roast, roaster, and gear list templates
2026-02-03 12:52:06 +00:00
Jon Seager
1fa88d054c
feat(roasts): consolidate table columns and add tasting notes search
- Combine Roast/Roaster into single column with roaster as subtext on desktop
- Combine Origin/Producer into single column with producer as subtext on desktop
- Add Roaster and Producer as separate mobile-only fields for card layout
- Replace tasting notes pill badges with comma-separated list
- Rename "Notes" column to "Tasting Notes" with right-aligned mobile text
- Include tasting_notes in repository search filter columns
2026-02-03 12:51:59 +00:00
Jon Seager
39bad42f9c
feat(mobile): add infinite scroll and hide pagination on mobile
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.
2026-02-03 12:51:52 +00:00
Jon Seager
8e92385c56
feat(search): add search input and reposition pagination in templates
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.
2026-02-03 12:51:42 +00:00
Jon Seager
ff8f0c781e
fix(web): better alignment on mobile for table views 2026-02-03 12:51:01 +00:00
Jon Seager
32c0935124
fix(nav): add responsive hamburger menu for mobile
- 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
2026-02-03 12:50:52 +00:00
Jon Seager
8b77eb90eb
feat(brews): add filter paper to routes, views, CLI, and templates
- 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
2026-02-03 10:14:22 +00:00
Jon Seager
4ed1544d66
fix(timeline): ensure cards always alternate sides across month boundaries
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.
2026-02-03 09:51:48 +00:00
Jon Seager
949f65ac16
refactor(timeline): denormalize data and simplify frontend
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.
2026-02-03 08:53:29 +00:00
Jon Seager
267ef2bf17
feat(timeline): remove sidebar nav, include sticky headers on scroll
- 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
2026-02-02 21:18:20 +00:00
Jon Seager
b12ad2bf6d
feat(timeline): add "brew again" button to timeline
- 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
2026-02-02 20:14:51 +00:00
Jon Seager
bda59f1c2a
fix(brews): allow arbitrary values in numeric form fields
- Change step to "any" for coffee weight, grind setting, water volume,
  and temperature inputs to allow arbitrary decimal values
- Keep +/- button increments unchanged (0.5g weight, 0.5 grind, 10ml
  water, 0.5°C temp)
2026-02-02 19:59:51 +00:00
Jon Seager
9402d98bb6
fix(brews): improve table layout and add "brew again" feature
- 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
2026-02-02 19:55:18 +00:00
Jon Seager
7e579cb2d1
fix(brews): improve form defaults and table layout
- Prepopulate form inputs with default values (15g, 6.0 grind, 250ml, 91°C)
- Combine weight/water/ratio into single "Recipe" column
- Reorder columns: Coffee, Grind, Recipe, Temp, Brewer, Date, Actions
- Use trash icon button for delete action (consistent with roasts table)
2026-02-02 19:42:50 +00:00
Jon Seager
8aa72b55eb
fix(brews): prepopulate form fields with default values
Add value attributes to numeric inputs so defaults are visible
when the form is opened.
2026-02-02 19:35:30 +00:00
Jon Seager
c4357f2698
feat(brews): add web views and templates
- Add BrewView, BagOptionView, GearOptionView
- Add brews page with form and +/- adjustment buttons
- Add brew list partial with sortable columns
- Add Brews link to navigation
2026-02-02 19:31:46 +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
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
57260a71c2
fix: remove superfluous /bags/:id/finish endpoint 2025-11-27 14:24:26 +00:00
Jon Seager
d708a0e112
feat: add bags web views and templates 2025-11-27 14:02:55 +00:00
Jon Seager
0e06d4c5dc
feat: push URL state when sorting/paging tables 2025-11-26 15:24:33 +00:00
Jon Seager
7df6cbcc51
fix: don't display delete icons when unauthenticated 2025-11-25 21:38:40 +00:00
copilot-swe-agent[bot]
e6811d45ad
feat(frontend): add login page and session-based authentication
- Add tower and tower-cookies dependencies for session management
- Create login page template with username/password form
- Implement /login and /logout routes with cookie-based sessions
- Update navigation bar to show Login/Logout based on auth state
- Add is_authenticated field to all page templates
- Hide create/update/delete UI controls when unauthenticated
- Session tokens stored in secure HttpOnly cookies with SameSite=Lax
- Password verification uses constant-time comparison via Argon2

Co-authored-by: jnsgruk <668505+jnsgruk@users.noreply.github.com>
2025-11-25 16:28:47 +00:00
Jon Seager
4f69d20bb1
feat: add pagination/infinite scroll to timeline 2025-11-24 21:45:59 +00:00
Jon Seager
fb9fb6cc23
refactor(roasts): require roast metadata 2025-11-24 11:44:16 +00:00
Jon Seager
3241f3c961
feat: bootstrap brewlog platform 2025-11-24 11:44:12 +00:00