Small ops dashboard for managing today's visits. Built for the Belle take-home.
Ruby 3.4.9 · Rails 8.1.3 · PostgreSQL · UUID primary keys
You need Ruby, PostgreSQL, and Bundler installed locally.
bin/setup --skip-server # gems, db, seed from JSON
bin/dev # http://localhost:3000bin/setup loads db/seed_data/visits.json and reschedule_requests.json. Safe to run again.
Re-seed only:
bin/rails db:seed"Today" on the dashboard is hardcoded to 2026-06-29 (Visit::OPERATING_DATE) so the demo matches the dataset.
bundle exec rspec
bin/rubocop
bin/brakeman
bin/bundler-audit
bin/ci # runs the lot locallyCI on GitHub runs the same checks in parallel (see .github/workflows/ci.yml).
I spent most of my time in domain logic, models, and RSpec. That's where I think this project is strongest.
The two pieces that matter most to me:
1. Cancellation fee recommendation (Visits::CancellationFeePolicy)
This was my top priority. When a visit falls through, Belle eats the CHW drive time and the slot goes empty — that's direct revenue leakage. The policy answers "should ops recommend charging this member?" based on how the visit failed: on-site/no-show gets a fee, last-minute remote cancel gets a fee, enough notice or hospitalized gets waived, unclear data goes to review. It never charges anyone; it just gives the coordinator a clear recommendation and reason.
2. Risk scoring (Visits::RiskScorer)
Second priority. I wanted a simple way to spot members who keep cancelling or no-showing so ops can act before sending a CHW out again. Prior no-shows, prior cancellations (derived from the member's other cancelled visits), confirmation signals, and same-day failure all add points. High risk → call and confirm, maybe line up a backfill from the reschedule queue.
The reschedule queue is basic on purpose: pending members who need an open slot, and a separate tab for assignments waiting on confirmation. No second scoring system — reliability lives in the one risk model.
Light DDD split: models for persistence, domain objects for business rules, query objects for the dashboard, thin controllers, plain ERB views.
app/domains/visits/cancellation_fee_policy.rb
app/domains/visits/risk_scorer.rb
app/queries/ops/dashboard_query.rb
Fee policy and risk scorer specs run without the database (plain attribute objects).
Testing & quality
rspec-rails— domain logic, models, request specsrubocop-rails-omakase— Rails-flavored RuboCop defaults (bin/rubocop)brakeman— static security scan on the codebasebundler-audit— checks gems against known CVEsbullet— dev-only N+1 query warnings (caught the reschedule eager-load issue)
Other
ostruct— lightweight visit stand-ins in pure domain specs (no DB)bootsnap— faster boot; Rails default
I used AI mostly for architecture discussions — where to draw boundaries, what to leave out given the time box, tradeoffs on the fee rules and risk signals.
I leaned on it more for views and templating (Tailwind markup, Stimulus pagination, the reschedule tabs). The domain code and tests I wrote and iterated on myself.
No auth, no background jobs, no real billing integration, no scheduling engine. Fee recommendations are internal only.