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
1,855 changes: 1,292 additions & 563 deletions openapi/swagger.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ const Button: React.FC<ButtonProps> = ({
onClick,
className,
}) => {
const base = 'px-4 py-2 rounded-md font-semibold transition-colors';
const base = 'px-4 py-2 rounded-full font-semibold transition-all duration-200 active:scale-95';
const styles = {
primary:
'bg-[var(--brand-primary)] text-white hover:bg-[var(--brand-secondary)]',
'bg-gray-900 text-white shadow-sm hover:bg-[var(--brand-primary)] hover:shadow-md',
secondary:
'bg-[var(--brand-secondary)] text-white hover:bg-[var(--brand-tertiary)]',
'bg-gray-700 text-white shadow-sm hover:bg-gray-900',
outline:
'border border-[var(--brand-secondary)] text-[var(--brand-secondary)] hover:bg-[var(--brand-secondary)] hover:text-white',
'border border-gray-200 text-gray-600 hover:bg-gray-50 hover:border-gray-300',
};
return (
<button
Expand Down
19 changes: 11 additions & 8 deletions src/components/CategoryFilterBar.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
type CategoryOption = { id: string; label: string; count?: number };
import { ICON_MAP } from '../utils/iconMap';
type CategoryOption = { id: string; label: string; count?: number; icon?: string };

interface CategoryFilterBarProps {
categories: CategoryOption[];
Expand Down Expand Up @@ -89,26 +90,28 @@ const CategoryFilterBar: React.FC<CategoryFilterBarProps> = ({ categories }) =>
onClick={() => handleClick(cat.id)}
onMouseEnter={() => setHovered(cat.id)}
onMouseLeave={() => setHovered('')}
className="px-4 py-2 rounded-full text-sm font-semibold transition-colors"
className="inline-flex items-center gap-1 px-4 py-2 rounded-full text-sm font-semibold transition-all duration-200"
style={
selected === cat.id
? {
backgroundColor: 'var(--brand-primary)',
color: '#fff',
boxShadow: '0 4px 12px rgba(0,0,0,0.15)',
}
: hovered === cat.id
? {
backgroundColor: 'var(--brand-tertiary)',
color: '#fff',
border: '1px solid var(--brand-tertiary)',
backgroundColor: '#f3f4f6',
color: '#111827',
border: '1px solid #d1d5db',
}
: {
backgroundColor: 'var(--brand-background)',
color: 'var(--brand-secondary)',
border: '1px solid var(--brand-tertiary)',
backgroundColor: '#fff',
color: '#6b7280',
border: '1px solid #e5e7eb',
}
}
>
{cat.icon && (() => { const IC = ICON_MAP[cat.icon]; return IC ? <IC className="w-3.5 h-3.5" /> : null; })()}
<span>{cat.label}</span>
{typeof cat.count === 'number' ? (
<span className="ml-2 text-xs opacity-80">{cat.count}</span>
Expand Down
21 changes: 5 additions & 16 deletions src/components/HeroSection.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,17 @@
import React from 'react';
import type { BrandColors } from '../utils/branding';

interface HeroSectionProps {
heroImageUrl: string;
colors: BrandColors;
}

const HeroSection: React.FC<HeroSectionProps> = ({ heroImageUrl, colors }) => {
const HeroSection: React.FC<HeroSectionProps> = ({ heroImageUrl }) => {
return (
<section
className="relative h-72 md:h-96 bg-center bg-cover flex flex-col items-center justify-center"
style={{
backgroundImage: `url('${heroImageUrl}')`,
}}
className="relative h-72 md:h-96 bg-center bg-cover"
style={{ backgroundImage: `url('${heroImageUrl}')` }}
>
{/* Content */}
<div className="relative z-10 text-center text-white space-y-6">
<h1
className="text-3xl md:text-5xl font-extrabold tracking-wide inline-block px-6 py-2 rounded-md shadow-lg"
style={{ backgroundColor: colors.primary }}
>
Our Menu
</h1>
</div>
<div className="absolute inset-0 bg-gradient-to-b from-black/20 via-transparent to-transparent" />
<div className="absolute bottom-0 left-0 right-0 h-24 bg-gradient-to-t from-gray-50/60 to-transparent" />
</section>
);
};
Expand Down
50 changes: 40 additions & 10 deletions src/components/ItemCard.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
// ItemCard.tsx - Concise presentational card that derives shopId from URL and opens ProductModal
import React, { useEffect, useMemo, useState } from 'react';
import { UtensilsCrossed } from 'lucide-react';
import Button from './Button';
import ProductModal, { Product as ModalProduct } from './ProductModal';
import { formatDollars } from '../utils/money';
import { ICON_MAP } from '../utils/iconMap';
import { SPECIAL_INFO_COLORS, DEFAULT_BADGE } from '../utils/badgeColors';

export type Product = ModalProduct;

Expand Down Expand Up @@ -62,32 +65,59 @@ const ItemCard: React.FC<ItemCardProps> = ({ product, onAddToCart }) => {
return (
<>
<div
className="rounded-xl shadow-md bg-white overflow-hidden flex flex-col"
className="group/card rounded-2xl bg-white border border-gray-100 shadow-[0_2px_12px_rgba(0,0,0,0.06)] hover:shadow-[0_12px_40px_rgba(0,0,0,0.13)] hover:-translate-y-1 transition-all duration-300 flex flex-col h-full overflow-hidden"
>
<button
type="button"
className="w-full aspect-[4/3] overflow-hidden cursor-pointer"
className="relative w-full aspect-[4/3] overflow-hidden cursor-pointer"
onClick={openModal}
aria-label={`View details for ${product.label}`}
>
<img
src={product.imageURL || 'https://via.placeholder.com/320x180'}
alt={product.label}
className="w-full h-full object-cover pointer-events-none select-none"
draggable={false}
/>
{product.imageURL ? (
<img
src={product.imageURL}
alt={product.label}
className="w-full h-full object-cover pointer-events-none select-none group-hover/card:scale-105 transition-transform duration-500"
draggable={false}
/>
) : (
<div className="absolute inset-0 flex items-center justify-center bg-gradient-to-br from-gray-100 to-gray-50">
<UtensilsCrossed className="w-12 h-12 text-gray-300" />
</div>
)}
</button>
<div className="p-3 sm:p-4 flex flex-col justify-between flex-1 space-y-2">
<h3 className="text-base sm:text-lg font-bold text-[var(--brand-secondary)] line-clamp-1">
<h3 className="text-base sm:text-lg font-bold text-gray-900 line-clamp-1">
{product.label}
</h3>
<p className="text-gray-600 text-xs sm:text-sm md:text-base">
<p className="text-sm font-bold text-gray-900">
{formatDollars(displayPrice)}
</p>
<p className="text-gray-500 text-xs sm:text-sm line-clamp-2">
{product.description}
</p>

{product.specialInfo && product.specialInfo.length > 0 && (
<div className="flex flex-wrap gap-1">
{product.specialInfo.map((item, i) => {
const IC = item.icon ? ICON_MAP[item.icon] : null;
return (
<span
key={i}
className={`relative group/badge inline-flex items-center justify-center rounded-full p-1.5 ${
item.icon ? (SPECIAL_INFO_COLORS[item.icon] ?? DEFAULT_BADGE) : DEFAULT_BADGE
}`}
>
{IC ? <IC className="h-3.5 w-3.5" /> : <span className="text-xs">{item.name}</span>}
<span className={`pointer-events-none absolute bottom-full mb-1.5 whitespace-nowrap rounded bg-gray-800 px-2 py-0.5 text-xs text-white opacity-0 transition-opacity group-hover/badge:opacity-100 z-10 ${i === 0 ? 'left-0' : 'left-1/2 -translate-x-1/2'}`}>
{item.name}
</span>
</span>
);
})}
</div>
)}

<div className="flex gap-2 mt-2">
<Button
variant="outline"
Expand Down
49 changes: 15 additions & 34 deletions src/components/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,18 @@ import {
clearCart,
} from '../store/slices/cartSlice';
import { formatDollars } from '../utils/money';
import type { BrandColors } from '../utils/branding';

interface NavBarProps {
shopName: string;
shopId: string;
logoUrl: string;
colors: BrandColors;
onCheckout?: () => void;
}

const NavBar: React.FC<NavBarProps> = ({
shopName,
shopId,
logoUrl,
colors,
onCheckout,
}) => {
const [open, setOpen] = useState(false);
Expand Down Expand Up @@ -102,12 +99,7 @@ const NavBar: React.FC<NavBarProps> = ({

return (
<>
<header
className="shadow-sm"
style={{
backgroundImage: `linear-gradient(to right, #ffffff, ${colors.background})`,
}}
>
<header className="bg-white/80 backdrop-blur-md border-b border-gray-100 shadow-sm">
<div className="max-w-7xl mx-auto px-6 flex justify-between items-center h-16">
<button
className="flex items-center gap-2 hover:opacity-80 transition-opacity"
Expand All @@ -117,23 +109,17 @@ const NavBar: React.FC<NavBarProps> = ({
<img
src={logoUrl}
alt={shopName}
className="h-8 w-8 rounded object-cover"
className="h-8 w-8 rounded object-cover ring-2 ring-[var(--brand-primary)]"
/>
<span
className="font-bold text-xl"
style={{ color: colors.primary }}
>
{shopName}
</span>
<span className="font-bold text-xl text-gray-900">{shopName}</span>
</button>

<div className="flex items-center gap-4 relative">
<div className="relative group">
<button
className="relative"
className="relative text-gray-700"
onClick={() => navigate(`/shops/${shopId}/my-orders`)}
aria-label="My Orders"
style={{ color: colors.primary }}
>
{Icon(Receipt, { className: 'w-5 h-5' })}
</button>
Expand All @@ -144,16 +130,12 @@ const NavBar: React.FC<NavBarProps> = ({
<div className="relative group">
<button
ref={buttonRef}
className="relative"
className="relative text-gray-700"
onClick={() => setOpen((v) => !v)}
aria-label="Cart"
style={{ color: colors.primary }}
>
{Icon(ShoppingBag, { className: '' })}
<span
className="absolute -top-2 -right-2 text-white text-xs rounded-full px-1"
style={{ backgroundColor: colors.primary }}
>
<span className="absolute -top-2 -right-2 text-white text-xs rounded-full px-1 bg-gray-900">
{cartCount}
</span>
</button>
Expand All @@ -178,8 +160,8 @@ const NavBar: React.FC<NavBarProps> = ({
zIndex: 9999,
}}
>
<div className="bg-white shadow-lg rounded-md p-3">
<h4 className="font-semibold mb-2">Your Order</h4>
<div className="bg-white/95 backdrop-blur-sm shadow-2xl rounded-2xl border border-gray-100 p-4">
<h4 className="font-semibold mb-3 text-gray-900">Your Order</h4>
{cartItems.length === 0 ? (
<div className="text-sm text-gray-500">No items yet</div>
) : (
Expand All @@ -188,9 +170,9 @@ const NavBar: React.FC<NavBarProps> = ({
{cartItems.map((it) => (
<div
key={it.key}
className="flex items-center gap-3 text-sm py-2 px-1 rounded hover:bg-gray-50"
className="flex items-center gap-3 text-sm py-2 px-2 rounded-xl hover:bg-gray-50 transition-colors"
>
<div className="w-14 h-14 bg-gray-100 rounded flex items-center justify-center overflow-hidden">
<div className="w-14 h-14 bg-gray-100 rounded-xl flex items-center justify-center overflow-hidden flex-shrink-0">
{it.imageUrl ? (
<img
src={it.imageUrl}
Expand All @@ -215,18 +197,18 @@ const NavBar: React.FC<NavBarProps> = ({
<div className="flex flex-col items-end gap-2">
<div className="flex items-center gap-1">
<button
className="px-2 py-1 text-xs bg-gray-100 rounded"
className="w-7 h-7 flex items-center justify-center text-sm bg-gray-100 rounded-full hover:bg-gray-200 transition-colors"
onClick={() =>
dispatch(decrementItem({ key: it.key }))
}
>
-
</button>
<div className="text-xs px-2">
<div className="text-xs px-2 font-medium">
{it.quantity}
</div>
<button
className="px-2 py-1 text-xs bg-gray-100 rounded"
className="w-7 h-7 flex items-center justify-center text-sm bg-gray-100 rounded-full hover:bg-gray-200 transition-colors"
onClick={() =>
dispatch(incrementItem({ key: it.key }))
}
Expand Down Expand Up @@ -256,7 +238,7 @@ const NavBar: React.FC<NavBarProps> = ({

<div className="flex gap-2 mt-3">
<button
className="flex-1 bg-[var(--brand-primary)] hover:bg-[var(--brand-secondary)] text-white px-3 py-2 rounded transition-colors"
className="flex-1 bg-gray-900 hover:bg-[var(--brand-primary)] text-white px-4 py-2 rounded-full font-semibold shadow-sm hover:shadow-md transition-all duration-200"
onClick={() => {
setOpen(false);
onCheckout?.();
Expand All @@ -265,7 +247,7 @@ const NavBar: React.FC<NavBarProps> = ({
Checkout
</button>
<button
className="px-3 py-2 border rounded"
className="px-4 py-2 border border-gray-200 rounded-full text-sm text-gray-600 hover:bg-gray-50 transition-colors"
onClick={() => dispatch(clearCart())}
>
Clear
Expand All @@ -280,7 +262,6 @@ const NavBar: React.FC<NavBarProps> = ({
</div>
</div>
</header>

</>
);
};
Expand Down
Loading
Loading