Skip to content

pradeepreddyvv/freshbite

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

38 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

FreshBite β€” Freshness-First Dish Reviews

That 4.2-star restaurant? Most of those reviews are from 2022. The chef changed. The oil changed. The rating didn't.

Live Site

FreshBite is a production-ready web application that revolutionizes how people review dishes at restaurants by showing only time-windowed, fresh reviews. Built as a complete MVP β€” because a review from a week ago is data, a review from a year ago is noise.

Built with Next.js 14 + Spring Boot + FastAPI + PostgreSQL, deployed on Vercel + DigitalOcean Droplet with Docker. Features time-windowed review queries, live risk scoring, GPS-based restaurant discovery, and LLM-powered insights.


🎯 Project Goal

Build a web app where users can review specific dishes at specific restaurants, but the UI displays ONLY the most recent 5 days of reviews by default.

Why? Dish quality changes daily based on:

  • Chef shifts and skill levels
  • Fresh vs. reused ingredients (oil, spices)
  • Time of day and meal preparation
  • Kitchen management changes

Solution: Store all historical reviews, but surface only fresh data in the UI.


✨ Features

MVP (Current)

  • βœ… Dish-specific reviews at restaurant locations (DishAtRestaurant entity)
  • βœ… Time-windowed queries (24h, 48h, 5d)
  • βœ… Risk labels (🟒 Good, 🟑 Mixed, πŸ”΄ Risky, βšͺ No data)
  • βœ… Real-time stats (avg rating, review count)
  • βœ… Add reviews with server-generated UTC timestamps
  • βœ… Responsive UI with Tailwind CSS
  • βœ… SSR-first Next.js App Router architecture
  • βœ… Production-ready PostgreSQL schema with proper indexes
  • βœ… Per-page visit counters (unique + total views)
  • βœ… Global page tracker (Next.js client + API)
  • βœ… API endpoints for analytics

Planned Extensions

  • Evidence-based chat (RAG over time-windowed reviews with citations)
  • Alert subscriptions (notify when quality drops)
  • Meal slot awareness (breakfast/lunch/dinner breakdowns)
  • Time-of-day filtering (show only lunch reviews)

πŸ—οΈ Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                       Frontend (Vercel)                      β”‚
β”‚  Next.js 14 (App Router) + TypeScript + Tailwind CSS        β”‚
β”‚                                                              β”‚
β”‚  Pages:                                                      β”‚
β”‚  β€’ /                  β†’ Homepage (browse dishes)             β”‚
β”‚  β€’ /dish/[id]         β†’ Dish page (reviews + stats + form)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
                       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Backend (Next.js API)                     β”‚
β”‚                                                              β”‚
β”‚  Routes:                                                     β”‚
β”‚  β€’ GET  /api/dish/[id]/reviews?window=5d                    β”‚
β”‚  β€’ POST /api/dish/[id]/reviews                              β”‚
β”‚  β€’ GET  /api/dish/[id]/summary?window=24h                   β”‚
β”‚  β€’ POST /api/chat           (stub)                           β”‚
β”‚  β€’ POST /api/alerts/run     (stub)                           β”‚
β”‚  β€’ POST /api/analytics/pageviews        β†’ Page visit tracking (POST/GET)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
                       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               Database (Neon PostgreSQL)                     β”‚
β”‚                      Prisma ORM                              β”‚
β”‚                                                              β”‚
β”‚  Tables:                                                     β”‚
β”‚  β€’ Restaurant                                                β”‚
β”‚  β€’ Dish                                                      β”‚
β”‚  β€’ DishAtRestaurant    (first-class entity)                 β”‚
β”‚  β€’ Review              (append-only, never deleted)          β”‚
β”‚  β€’ AlertSubscription   (stub)                               β”‚
β”‚  β€’ PageVisit                      β”‚
β”‚  β€’ PageVisitCounter               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ—„οΈ Data Model

Core Entities

DishAtRestaurant (Why it matters)

  • "Chicken Biryani" at Restaurant A β‰  same dish at Restaurant B
  • Enables per-location freshness tracking
  • First-class entity for reviews and alerts

Review (Append-only log)

  • Never deleted (freshness enforced by queries, not deletes)
  • createdAt in UTC
  • mealSlot nullable (planned feature)

Critical Index

-- Optimized for time-window queries
CREATE INDEX idx_review_dish_time ON Review(dishAtRestaurantId, createdAt DESC);

πŸš€ Quick Start

Prerequisites

  • Node.js 18+
  • PostgreSQL database (or Neon free tier)
  • npm or yarn

1. Clone & Install

git clone <your-repo-url>
cd freshbite
npm install

2. Set Up Database

# Create .env file
cp .env.example .env

# Edit .env and add your DATABASE_URL:
# DATABASE_URL="postgresql://user:password@host:5432/freshbite?sslmode=require"

3. Run Migrations & Seed

# Generate Prisma client
npm run db:generate

# Push schema to database
npm run db:push

# Seed demo data
npm run db:seed

4. Start Development Server

npm run dev

Open http://localhost:3000


πŸ“Š Testing Checklist

After seeding, verify the following:

Freshness Logic

  • Homepage shows the seeded dish
  • Dish page displays 7 recent reviews (within last 5 days)
  • Old reviews (>5 days) are hidden from the feed
  • Stats reflect only the recent reviews

Risk Label

  • Risk badge shows correct status based on 24h window
  • Changes based on recent review quality
  • "Not enough data" when < 3 reviews

Review Submission

  • Can post new review with 1-5 rating
  • Review appears immediately in feed
  • Stats update correctly
  • Server-generated timestamp (check DB)

API Endpoints

# Get reviews (5 day window)
curl http://localhost:3000/api/dish/[ID]/reviews?window=5d

# Get summary (24h window)
curl http://localhost:3000/api/dish/[ID]/summary?window=24h

# Post review
curl -X POST http://localhost:3000/api/dish/[ID]/reviews \
  -H "Content-Type: application/json" \
  -d '{"rating": 5, "text": "Amazing!"}'

# Chat stub
curl -X POST http://localhost:3000/api/chat \
  -H "Content-Type: application/json" \
  -d '{"dishAtRestaurantId": "[ID]", "question": "How is it?", "window": "24h"}'

🌐 Deployment (Vercel + Neon)

1. Create Neon Database

  1. Go to neon.tech
  2. Create free PostgreSQL database
  3. Copy connection string

2. Deploy to Vercel

# Install Vercel CLI
npm i -g vercel

# Deploy
vercel

# Set environment variables in Vercel dashboard:
# DATABASE_URL=<your-neon-connection-string>
# NEXT_PUBLIC_BASE_URL=https://your-app.vercel.app

3. Run Migrations in Production

# Generate Prisma client in production
vercel env pull .env.production
npm run db:push
npm run db:seed

4. Configure GitHub Actions (Optional)

Required Secrets:

  • SITE_URL - Your deployed app URL
  • ALERTS_SECRET_TOKEN - Random secure token

Add in GitHub repo β†’ Settings β†’ Secrets


πŸ”§ Project Structure

freshbite/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ api/                  # API routes
β”‚   β”‚   β”œβ”€β”€ dish/[id]/
β”‚   β”‚   β”‚   β”œβ”€β”€ reviews/
β”‚   β”‚   β”‚   β”‚   └── route.ts  # GET/POST reviews
β”‚   β”‚   β”‚   └── summary/
β”‚   β”‚   β”‚       └── route.ts  # GET summary + risk
β”‚   β”‚   β”œβ”€β”€ chat/
β”‚   β”‚   β”‚   └── route.ts      # POST chat (stub)
β”‚   β”‚   └── alerts/
β”‚   β”‚       └── run/
β”‚   β”‚           └── route.ts  # POST alerts (stub)
β”‚   β”œβ”€β”€ dish/[id]/
β”‚   β”‚   └── page.tsx          # Dish detail page
β”‚   β”œβ”€β”€ layout.tsx
β”‚   β”œβ”€β”€ page.tsx              # Homepage
β”‚   β”œβ”€β”€ globals.css
β”‚   └── not-found.tsx
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ ChatPanel.tsx
β”‚   β”œβ”€β”€ DishHeader.tsx
β”‚   β”œβ”€β”€ ReviewCard.tsx
β”‚   β”œβ”€β”€ ReviewFeed.tsx
β”‚   β”œβ”€β”€ ReviewForm.tsx
β”‚   β”œβ”€β”€ RiskBadge.tsx
β”‚   └── StatsPanel.tsx
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ format-time.ts        # Relative time formatting
β”‚   β”œβ”€β”€ prisma.ts             # Prisma client singleton
β”‚   β”œβ”€β”€ risk-label.ts         # Risk calculation logic
β”‚   └── time-window.ts        # Window parsing (24h/48h/5d)
β”œβ”€β”€ prisma/
β”‚   β”œβ”€β”€ schema.prisma         # Database schema
β”‚   └── seed.ts               # Seed script
β”œβ”€β”€ .github/
β”‚   └── workflows/
β”‚       β”œβ”€β”€ ci.yml            # CI: lint + typecheck + build
β”‚       └── alerts-cron.yml   # Hourly alerts (stub)
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
β”œβ”€β”€ tailwind.config.ts
β”œβ”€β”€ next.config.js
β”œβ”€β”€ .env.example
└── README.md

πŸ§ͺ Development Scripts

# Development
npm run dev              # Start dev server
npm run build            # Build for production
npm run start            # Start production server
npm run lint             # Run ESLint
npm run typecheck        # TypeScript type check

# Database
npm run db:generate      # Generate Prisma Client
npm run db:push          # Push schema to DB (no migrations)
npm run db:migrate       # Create migration
npm run db:seed          # Seed demo data
npm run db:studio        # Open Prisma Studio

🎨 Design Decisions

Why Time Windows?

  • Problem: Old reviews mislead (chef changed, oil reused, ingredients differ)
  • Solution: Show only recent data (5d default)
  • Impact: Users see what's actually relevant today

Why DishAtRestaurant?

  • Problem: Same dish name at different locations = different quality
  • Solution: First-class entity linking dish + restaurant
  • Impact: Accurate per-location tracking

Why Append-Only Reviews?

  • Problem: Deleting old reviews loses historical context
  • Solution: Store everything, filter at query time
  • Impact: Can analyze trends, add ML later

Why Risk Labels?

  • Problem: Users don't have time to read all reviews
  • Solution: Color-coded summary (Good/Mixed/Risky)
  • Impact: Instant decision-making

πŸ“ˆ Roadmap

Evidence-Based Chat

  • RAG over time-windowed reviews
  • Answer questions with citations
  • Example: "Is it spicy?" β†’ Shows relevant review quotes

Alerts & Notifications

  • Subscribe to dishes
  • Email/SMS when quality drops
  • Configurable thresholds

Meal Slot Intelligence

  • Derive breakfast/lunch/dinner from timestamp + timezone
  • Filter reviews by time of day
  • "Show me only lunch reviews"

Advanced Analytics

  • Quality trends over time
  • Chef/shift correlation
  • Predictive risk scoring

🀝 Contributing

Contributions welcome! Key areas:

  1. UI/UX improvements (mobile responsiveness, accessibility)
  2. Performance optimizations (caching, edge functions)
  3. Feature implementations (chat, alerts, meal slots)
  4. Testing (unit tests, E2E tests)

πŸ“ License

MIT License - See LICENSE file


πŸ™ Acknowledgments

Built with:


πŸ“ž Support

Questions or issues?


🧠 AI Agent Project Summary

FreshBite is a Next.js 14 + Prisma + Neon PostgreSQL app for dish reviews, with time-windowed freshness logic and per-page analytics. Key features:

  • Dish-specific reviews at restaurant locations
  • Time-windowed queries (last 5 days)
  • Risk labels (Good/Mixed/Risky)
  • Real-time stats
  • Per-page visit counters (unique + total views)
  • Append-only review log
  • Responsive UI (Tailwind)
  • SSR-first architecture
  • Production-ready DB schema
  • API endpoints for reviews, summaries, chat (stub), alerts (stub), analytics

Data Model:

  • Restaurant
  • Dish
  • DishAtRestaurant (per-location dish entity)
  • Review (append-only)
  • AlertSubscription (stub)
  • PageVisit (event log)
  • PageVisitCounter (aggregated counters)

Analytics:

  • Page views tracked via client-side tracker and API
  • Unique visitors via session cookie
  • Bot traffic ignored

Deployment:

  • Vercel (frontend + API)
  • Neon PostgreSQL
  • Prisma ORM

How to extend:

  • Add new API endpoints in app/api/
  • Add new DB models in prisma/schema.prisma
  • Add new client components in components/
  • Analytics API is ready for dashboard or admin usage

Contact:

  • Open GitHub issue for questions

Built with ❀️ for better food decisions

About

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors