Add My Bar, Bartender, Recommend features + drink images

- Drink Images: upload/display photos of bottles/cans on drink cards and detail pages
- My Bar: inventory tracker for spirits, liqueurs, mixers, bitters, garnishes, tools
- Bartender: AI-powered cocktail recipe generation, "what can I make" suggestions,
  saved recipes. Cross-references bar inventory for ingredient availability.
- Recommend: AI flavor profile analysis, personalized drink recommendations,
  "find similar" drinks based on highly-rated favorites
- Navigation: desktop sidebar with all 8 routes, mobile bottom nav with
  4 primary items + "More" popup menu
- New Prisma models: BarItem, Recipe, FlavorProfile
- Backup/restore updated to include bar items

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
JP Scott
2026-03-01 18:28:02 -07:00
parent d8f069cce4
commit 2ac2c4b2d4
40 changed files with 3709 additions and 11 deletions

View File

@@ -2,15 +2,15 @@
import Link from "next/link"
import { usePathname } from "next/navigation"
import { LayoutDashboard, Camera, Wine, Bookmark, Settings } from "lucide-react"
import { LayoutDashboard, Wine, FlaskConical, GlassWater } from "lucide-react"
import { cn } from "@/lib/utils"
import { MoreMenu } from "./more-menu"
const navItems = [
const primaryItems = [
{ href: "/dashboard", label: "Home", icon: LayoutDashboard },
{ href: "/scan", label: "Scan", icon: Camera },
{ href: "/drinks", label: "Drinks", icon: Wine },
{ href: "/wishlist", label: "Later", icon: Bookmark },
{ href: "/settings", label: "Settings", icon: Settings },
{ href: "/bar", label: "Bar", icon: FlaskConical },
{ href: "/bartender", label: "Mix", icon: GlassWater },
]
export function BottomNav() {
@@ -19,7 +19,7 @@ export function BottomNav() {
return (
<nav className="md:hidden fixed bottom-0 left-0 right-0 z-50 bg-card border-t safe-area-bottom">
<div className="flex items-center justify-around h-16">
{navItems.map((item) => {
{primaryItems.map((item) => {
const isActive = pathname.startsWith(item.href)
return (
<Link
@@ -37,6 +37,7 @@ export function BottomNav() {
</Link>
)
})}
<MoreMenu />
</div>
</nav>
)

View File

@@ -0,0 +1,82 @@
"use client"
import { useState } from "react"
import Link from "next/link"
import { usePathname } from "next/navigation"
import {
Camera,
Sparkles,
Bookmark,
Settings,
MoreHorizontal,
X,
} from "lucide-react"
import { cn } from "@/lib/utils"
const moreItems = [
{ href: "/scan", label: "Scan Menu", icon: Camera },
{ href: "/recommend", label: "For You", icon: Sparkles },
{ href: "/wishlist", label: "Try Later", icon: Bookmark },
{ href: "/settings", label: "Settings", icon: Settings },
]
export function MoreMenu() {
const [open, setOpen] = useState(false)
const pathname = usePathname()
const isActiveInMore = moreItems.some((item) =>
pathname.startsWith(item.href)
)
return (
<div className="relative">
<button
onClick={() => setOpen(!open)}
className={cn(
"flex flex-col items-center gap-1 px-3 py-2 text-xs font-medium transition-colors min-w-[64px]",
open || isActiveInMore ? "text-primary" : "text-muted-foreground"
)}
>
{open ? (
<X className="h-5 w-5" />
) : (
<MoreHorizontal className="h-5 w-5" />
)}
More
</button>
{open && (
<>
{/* Backdrop */}
<div
className="fixed inset-0 z-40"
onClick={() => setOpen(false)}
/>
{/* Menu */}
<div className="absolute bottom-full right-0 mb-2 z-50 bg-card border rounded-lg shadow-lg p-2 min-w-[160px]">
{moreItems.map((item) => {
const isActive = pathname.startsWith(item.href)
return (
<Link
key={item.href}
href={item.href}
onClick={() => setOpen(false)}
className={cn(
"flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium transition-colors",
isActive
? "bg-primary/10 text-primary"
: "text-muted-foreground hover:bg-accent hover:text-accent-foreground"
)}
>
<item.icon className="h-4 w-4" />
{item.label}
</Link>
)
})}
</div>
</>
)}
</div>
)
}

View File

@@ -11,6 +11,9 @@ import {
Settings,
LogOut,
Beer,
FlaskConical,
GlassWater,
Sparkles,
} from "lucide-react"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
@@ -19,6 +22,9 @@ const navItems = [
{ href: "/dashboard", label: "Dashboard", icon: LayoutDashboard },
{ href: "/scan", label: "Scan Menu", icon: Camera },
{ href: "/drinks", label: "My Drinks", icon: Wine },
{ href: "/bar", label: "My Bar", icon: FlaskConical },
{ href: "/bartender", label: "Bartender", icon: GlassWater },
{ href: "/recommend", label: "Recommend", icon: Sparkles },
{ href: "/wishlist", label: "Try Later", icon: Bookmark },
{ href: "/settings", label: "Settings", icon: Settings },
]