Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/components/RoleSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ export default function RoleSwitcher() {
if (!user) return null;

return (
<div className="fixed bottom-6 left-6 z-[9999]">
<div className="fixed bottom-24 left-4 z-40 md:bottom-6 md:left-6">
<button
onClick={toggleRole}
className="flex items-center gap-2 rounded-full bg-slate-900/90 dark:bg-orange-600/90 px-4 py-2 text-sm font-bold text-white shadow-2xl backdrop-blur-md hover:scale-105 transition-all border border-white/20"
className="flex items-center gap-2 rounded-full border border-orange-200 bg-gradient-to-r from-orange-500 to-red-600 px-4 py-2 text-sm font-bold text-white shadow-2xl backdrop-blur-md transition-all hover:scale-105 hover:shadow-orange-500/30 dark:border-orange-700"
>
<span className="text-lg">{user.role === "customer" ? "🚲" : "🛍️"}</span>
Switch to {user.role === "customer" ? "Driver" : "Customer"}
Expand Down
3 changes: 3 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
@import "tailwindcss";

/* Make Tailwind dark: variants follow .dark class instead of system preference */
@custom-variant dark (&:where(.dark, .dark *));

:root {
--background: #f8f8f8;
--foreground: #111111;
Expand Down
64 changes: 43 additions & 21 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,9 @@
const [savedAddresses, setSavedAddresses] = useState<Array<{ id: string; name: string; address: string }>>([]);

// Favorites & filters
const [favoriteIds, setFavoriteIds] = useState<Set<number>>(new Set());

Check warning on line 199 in app/page.tsx

View workflow job for this annotation

GitHub Actions / Lint

'favoriteIds' is assigned a value but never used
const [dietaryFilter, setDietaryFilter] = useState<string>("All");

Check warning on line 200 in app/page.tsx

View workflow job for this annotation

GitHub Actions / Lint

'setDietaryFilter' is assigned a value but never used

Check warning on line 200 in app/page.tsx

View workflow job for this annotation

GitHub Actions / Lint

'dietaryFilter' is assigned a value but never used
const [showFavoritesOnly, setShowFavoritesOnly] = useState(false);

Check warning on line 201 in app/page.tsx

View workflow job for this annotation

GitHub Actions / Lint

'setShowFavoritesOnly' is assigned a value but never used

Check warning on line 201 in app/page.tsx

View workflow job for this annotation

GitHub Actions / Lint

'showFavoritesOnly' is assigned a value but never used

// Chat
const [chatMessages, setChatMessages] = useState<ChatMessage[]>([
Expand All @@ -211,7 +211,9 @@
const [chatInput, setChatInput] = useState("");
const [chatLoading, setChatLoading] = useState(false);
const [statusMessage, setStatusMessage] = useState<string | null>(null);
const messagesEndRef = useRef<HTMLDivElement>(null);
const contentScrollRef = useRef<HTMLDivElement>(null);
const chatSectionRef = useRef<HTMLHeadingElement>(null);
const chatScrollRef = useRef<HTMLDivElement>(null);

// ─── Load from localStorage ───────────────────────────────────────────────

Expand Down Expand Up @@ -243,10 +245,12 @@
});
}, [aiView, selectedRestaurantId, restaurants]);

// ─── Auto-scroll chat ─────────────────────────────────────────────────────
// ─── Auto-scroll chat container only ─────────────────────────────────────

useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
const el = chatScrollRef.current;
if (!el) return;
el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
}, [chatMessages]);

// ─── Fetch restaurants ────────────────────────────────────────────────────
Expand Down Expand Up @@ -329,6 +333,9 @@
);
const deliveryFee = items.length > 0 ? (selectedRestaurant?.deliveryFee ?? 0) : 0;
const orderTotal = Math.max(0, totalPrice + deliveryFee + tip - promoDiscount);
const restaurantGridClass = isOpen
? "grid gap-4 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
: "grid gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5";

// ─── Saved meals ──────────────────────────────────────────────────────────

Expand All @@ -354,7 +361,7 @@

// ─── Favorites ────────────────────────────────────────────────────────────

const toggleFavorite = (rId: number) => {

Check warning on line 364 in app/page.tsx

View workflow job for this annotation

GitHub Actions / Lint

'toggleFavorite' is assigned a value but never used
setFavoriteIds((prev) => {
const next = new Set(prev);
if (next.has(rId)) next.delete(rId);
Expand Down Expand Up @@ -436,6 +443,17 @@
]);
};

const scrollToChatSection = () => {
const container = contentScrollRef.current;
const target = chatSectionRef.current;
if (!container || !target) return;

const containerRect = container.getBoundingClientRect();
const targetRect = target.getBoundingClientRect();
const top = targetRect.top - containerRect.top + container.scrollTop - 12;
container.scrollTo({ top: Math.max(top, 0), behavior: "smooth" });
Comment on lines +451 to +454
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scrollToChatSection uses a hard-coded - 12 offset when computing the scroll target. Please replace this magic number with a named constant and/or derive it from layout (e.g., container padding / desired spacing) so future style changes don’t silently break scroll positioning.

Copilot uses AI. Check for mistakes.
};

const handleSendMessage = async (presetText?: string) => {
const text = (presetText ?? chatInput).trim();
if (!text || chatLoading) return;
Expand All @@ -444,8 +462,13 @@
setChatInput("");
setChatLoading(true);
setStatusMessage(null);
// Switch back to chat view when user sends a message
// Keep current panel selections while sending a message
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says this keeps current panel selections, but this code still forces sidebarView to "none" (closing any sidebar panel). Please update the comment to reflect the actual behavior (or adjust the state update if the intent is truly to preserve the current panel).

Suggested change
// Keep current panel selections while sending a message
// Close any open sidebar panel while sending a message

Copilot uses AI. Check for mistakes.
setSidebarView("none");
requestAnimationFrame(() => {
requestAnimationFrame(() => {
scrollToChatSection();
});
});
Comment on lines +467 to +471
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nested requestAnimationFrame calls make the scroll timing hard to reason about and maintain. Consider using a single scheduling mechanism (e.g., one requestAnimationFrame, or triggering the scroll in an effect that runs after sidebarView becomes "none") and add a brief comment explaining why the extra frame is needed if you keep it.

Copilot uses AI. Check for mistakes.

try {
const res = await fetch("/api/chat", {
Expand Down Expand Up @@ -599,13 +622,13 @@
// ─── Render ───────────────────────────────────────────────────────────────

return (
<div className="min-h-screen bg-gradient-to-br from-background via-background to-zinc-100 dark:to-zinc-950 text-foreground">
<div className="flex h-dvh flex-col overflow-hidden bg-gradient-to-br from-amber-50 via-orange-50/70 to-amber-100/80 text-foreground dark:from-slate-950 dark:via-slate-900 dark:to-slate-950">
{/* Header */}
<header className="relative overflow-hidden border-b border-orange-200 dark:border-orange-900/20 bg-gradient-to-r from-white via-orange-50 to-white dark:from-slate-900 dark:via-slate-800 dark:to-slate-900 px-6 py-5 shadow-lg">
<header className="relative overflow-hidden border-b border-orange-200 dark:border-orange-900/20 bg-gradient-to-r from-amber-50 via-orange-50 to-amber-100 dark:from-slate-900 dark:via-slate-800 dark:to-slate-900 px-6 py-5 shadow-lg">
<div className="absolute inset-0 opacity-30 bg-gradient-to-r from-orange-400/10 via-red-400/10 to-purple-400/10 dark:from-orange-500/5 dark:to-purple-500/5 pointer-events-none" />
<div className="mx-auto flex w-full max-w-6xl items-center justify-between gap-4 relative z-10">
<div>
<h1 className="text-3xl font-black bg-gradient-to-r from-orange-600 via-red-600 to-purple-600 bg-clip-text text-transparent">🍽️ QuickBite</h1>
<h1 className="text-3xl font-black bg-gradient-to-r from-orange-600 via-orange-500 to-red-600 bg-clip-text text-transparent">🍽️ QuickBite</h1>
<div className="flex items-center gap-3 mt-1">
<p className="text-sm text-orange-600 dark:text-orange-400 font-medium">Welcome back, {user.name}</p>
{userLocation && (
Expand Down Expand Up @@ -647,7 +670,7 @@
</div>
</header>

<main className="flex h-[calc(100vh-73px)] overflow-hidden">
<main className="flex min-h-0 flex-1 overflow-hidden">
{/* ── Sidebar ── */}
<aside className="hidden w-56 shrink-0 flex-col border-r border-orange-200 dark:border-orange-900/20 bg-gradient-to-b from-white via-orange-50/50 to-white dark:from-slate-900 dark:via-slate-800 dark:to-slate-900 md:flex shadow-lg">
<div className="p-6">
Expand Down Expand Up @@ -711,9 +734,9 @@
</aside>

{/* ── Main content ── */}
<div className="flex flex-1 flex-col overflow-hidden">
<div className="flex-1 overflow-y-auto p-6">
<div className="mx-auto max-w-3xl space-y-6">
<div className="flex min-h-0 flex-1 flex-col overflow-hidden">
<div ref={contentScrollRef} className="min-h-0 flex-1 overflow-y-auto p-6 pb-5">
<div className="mx-auto w-full max-w-6xl space-y-6 xl:max-w-7xl">

{/* Past Orders */}
{sidebarView === "past-orders" && (
Expand Down Expand Up @@ -957,12 +980,12 @@
{/* Default: chat + AI views */}
{sidebarView === "none" && (
<>
<h2 className="text-center text-4xl font-black bg-gradient-to-r from-orange-600 via-red-600 to-purple-600 bg-clip-text text-transparent">
<h2 ref={chatSectionRef} className="text-center text-4xl font-black bg-gradient-to-r from-orange-600 via-red-600 to-orange-500 bg-clip-text text-transparent">
What are you craving today?
</h2>

{/* Chat messages */}
<div className="max-h-[50vh] space-y-4 overflow-y-auto pr-1">
<div ref={chatScrollRef} className="max-h-[42vh] space-y-4 overflow-y-auto pr-1">
{chatMessages.map((msg) => (
<div
key={msg.id}
Expand Down Expand Up @@ -1066,17 +1089,16 @@
))}
</div>
)}
<div ref={messagesEndRef} />
</div>

{/* AI restaurant grid - DoorDash/Uber style with many columns */}
{aiView === "restaurants" && (
{/* AI restaurant grid - show by default so restaurant images stay visible */}
{(aiView === "restaurants" || aiView === "none") && (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-bold text-orange-600">🏪 All Restaurants ({restaurants.length})</h2>
<span className="text-sm text-gray-500 dark:text-gray-400">Sorted by distance</span>
</div>
<div className="grid gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
<div className={restaurantGridClass}>
{restaurants.map((r, idx) => (
<div
key={r.id}
Expand Down Expand Up @@ -1258,7 +1280,7 @@
)}

{/* Chat input bar */}
<div className="border-t border-orange-200 dark:border-orange-900/20 bg-gradient-to-r from-white via-orange-50/50 to-white dark:from-slate-900 dark:via-slate-800 dark:to-slate-900 p-4 shadow-lg">
<div className="border-t border-orange-200 dark:border-orange-900/20 bg-gradient-to-r from-amber-50 via-orange-50 to-amber-100 dark:from-slate-900 dark:via-slate-800 dark:to-slate-900 p-4 shadow-lg">
<div className="mx-auto flex max-w-3xl gap-3">
<input
value={chatInput}
Expand All @@ -1282,7 +1304,7 @@

{/* ── Cart panel ── */}
{isOpen && (
<div className="flex w-96 shrink-0 flex-col border-l-2 border-orange-200 dark:border-orange-900/20 bg-gradient-to-b from-white via-orange-50/30 to-white dark:from-slate-900 dark:via-slate-800 dark:to-slate-900 shadow-2xl">
<div className="flex min-h-0 w-96 shrink-0 flex-col border-l-2 border-orange-200 dark:border-orange-900/20 bg-gradient-to-b from-amber-50 via-orange-50 to-amber-100 dark:from-slate-900 dark:via-slate-800 dark:to-slate-900 shadow-2xl">
<div className="flex items-center justify-between border-b-2 border-orange-200 dark:border-orange-900/20 bg-gradient-to-r from-orange-500 to-red-600 px-6 py-4 text-white shadow-lg">
<h2 className="text-lg font-bold">🛒 Your Cart</h2>
<button
Expand All @@ -1294,7 +1316,7 @@
</button>
</div>

<div className="flex-1 space-y-3 overflow-y-auto p-4">
<div className="min-h-0 flex-1 space-y-3 overflow-y-auto p-4">
{items.length === 0 ? (
<div className="mt-12 text-center space-y-3">
<p className="text-4xl">🍕</p>
Expand Down Expand Up @@ -1344,7 +1366,7 @@
</div>

{items.length > 0 && (
<div className="space-y-4 border-t-2 border-orange-200 dark:border-orange-900/20 bg-gradient-to-r from-orange-50 to-red-50 dark:from-slate-800 dark:to-slate-900 p-4">
<div className="shrink-0 space-y-4 border-t-2 border-orange-200 dark:border-orange-900/20 bg-gradient-to-r from-amber-100 via-orange-50 to-amber-100 dark:from-slate-800 dark:via-slate-800 dark:to-slate-900 p-4">
<div className="space-y-2">
<div className="flex justify-between text-sm font-medium">
<span className="text-gray-700 dark:text-gray-300">Subtotal</span>
Expand Down
Loading