# Components Documentation

> Whenever you create a new component in the components/ directory,
> automatically append a new section following the template below.

---

## CartProduct

**File:** components/CartProduct.tsx **Description:** Product card component.
Displays product image, name, description, formatted price, and a Buy button.
Width is controlled by the parent grid (w-full), not the card itself.

### Props

| Prop        | Type             | Required | Default      | Description                                                 |
| ----------- | ---------------- | -------- | ------------ | ----------------------------------------------------------- |
| image       | string           | yes      | -            | URL/path to product image (Next.js Image)                   |
| imageAlt    | string           | no       | Anh san pham | Alt text for accessibility                                  |
| productName | string           | yes      | -            | Product display name                                        |
| price       | number or string | yes      | -            | If number: auto-formatted to VND. If string: rendered as-is |
| description | string           | yes      | -            | Short description, clamped to 2 lines                       |
| onBuy       | () => void       | no       | undefined    | Callback when Buy button is clicked                         |

### Internal Logic

- formattedPrice: number -> toLocaleString(vi-VN, { style: currency, currency:
  VND })
- Image fallback: fa-solid fa-mug-hot icon shown behind image; if image fails
  onError hides the img element

### Styling (CSS variables)

| Element      | Key classes                                                        |
| ------------ | ------------------------------------------------------------------ |
| Card wrapper | flex flex-col w-full rounded-2xl, shadow uses --color-shadow-sm/md |
| Image area   | relative w-full h-36, bg --color-border-light                      |
| Product name | font-bold text-sm, color --color-text-primary, line-clamp-1        |
| Description  | text-xs, color --color-text-muted, line-clamp-2                    |
| Price        | text-sm font-bold, color --color-primary                           |
| Buy button   | bg --color-primary, hover --color-primary-dark, active:scale-95    |

### Dependencies

- next/image
- Tailwind CSS + CSS custom properties from globals.css
- FontAwesome (fa-solid fa-mug-hot fallback, fa-cart-plus button icon)

### Notes

- Card width is w-full - controlled by parent grid in page.tsx
- available field on Product is checked in page.tsx before rendering
- onBuy currently logs to console - TODO: implement cart logic

---

## Navbar

**File:** components/Navbar.tsx **Description:** Left sidebar with collapsible
category filter. Sticky below header, full viewport height minus header.

### Props

| Prop             | Type                 | Required | Default   | Description                                       |
| ---------------- | -------------------- | -------- | --------- | ------------------------------------------------- |
| isOpen           | boolean              | yes      | -         | true = expanded (240px), false = collapsed (64px) |
| onToggle         | () => void           | yes      | -         | Toggle expand/collapse                            |
| activeCategory   | string               | no       | all       | Currently selected category id                    |
| onCategoryChange | (id: string) => void | no       | undefined | Fired when user clicks a category                 |

### Behavior

- Collapsed: 64px wide, icon only (w-16)
- Expanded: 240px wide, icon + label (w-60)
- Width transition: transition-all duration-250ms
- Active category: highlighted with --color-primary background
- Footer shows SHOP_INFO.openHours (icon only when collapsed)

### Styling

| Element       | Key classes                                                 |
| ------------- | ----------------------------------------------------------- |
| Aside         | sticky, border-r --color-border, bg --color-bg-sidebar      |
| Toggle button | w-8 h-8 rounded-lg, hover bg --color-border-light           |
| Active item   | bg --color-primary text-white shadow-sm                     |
| Inactive item | hover bg --color-border-light, color --color-text-secondary |

### Dependencies

- next/link
- lib/constants: MENU_CATEGORIES, SHOP_INFO
- lib/types: MenuCategory
- FontAwesome icons

---

## Header (layouts/header.tsx)

**File:** layouts/header.tsx **Description:** Sticky top bar. 2-column layout:
Brand (left) + Auth button (right). Auth cycles Guest -> Manager -> Staff ->
Guest for UI demo.

### Props

None - reads SHOP_INFO and MOCK_USERS from lib/constants directly.

### Internal State

| State | Type         | Description                     |
| ----- | ------------ | ------------------------------- |
| user  | User or null | Current demo user. null = guest |

### Auth States

| State        | Appearance                            |
| ------------ | ------------------------------------- |
| Guest (null) | Brown primary button, Dang nhap label |
| Manager      | Gold/caramel badge with user-tie icon |
| Staff        | Avatar circle + name, bordered button |

### Responsive

- Logo + shop name: always visible
- Tagline: hidden < md, shown md+
- Button label: hidden < sm, shown sm+

### Dependencies

- next/image, next/link
- lib/constants: SHOP_INFO, MOCK_USERS
- lib/types: User
- FontAwesome icons

---

## Footer (layouts/footer.tsx)

**File:** layouts/footer.tsx **Description:** Site footer with 12-column grid. 3
sections: Brand info, Social links, WiFi card.

### Props

None - reads SHOP_INFO and SOCIAL_LINKS from lib/constants directly.

### Layout

| Section       | Mobile      | md         | lg/xl        |
| ------------- | ----------- | ---------- | ------------ |
| Brand info    | col-span-12 | col-span-6 | col-span-8/6 |
| Social + WiFi | col-span-12 | col-span-6 | col-span-4/6 |

### Sections

1. Brand: logo, name, tagline, address, phone, email, open hours
2. Social: Facebook, TikTok, Website links
3. WiFi: network name + password in monospace styled box
4. Bottom bar: copyright + Made with heart in Vietnam

### Dependencies

- next/image, next/link
- lib/constants: SHOP_INFO, SOCIAL_LINKS
- FontAwesome icons

---

## CartFab

**File:** components/CartFab.tsx **Description:** Floating Action Button
displaying cart item count. Shows badge with number of items and total price on
hover.

### Props

| Prop    | Type       | Required | Default   | Description                  |
| ------- | ---------- | -------- | --------- | ---------------------------- |
| onClick | () => void | no       | undefined | Callback when FAB is clicked |

### Features

- Displays cart icon with item count badge
- Shows total price on hover in tooltip
- Sticky position (bottom-right)
- Uses cart context to get items and total

### Styling

| Element    | Key classes                                     |
| ---------- | ----------------------------------------------- |
| FAB button | fixed bottom-6 right-6, rounded-full, shadow-lg |
| Badge      | absolute top-0 right-0, red bg, small font      |
| Tooltip    | appears on hover, shows total price             |

### Dependencies

- lib/cart-context: useCart()
- FontAwesome icons

---

# Contexts Documentation

## AuthContext (lib/auth-context.tsx)

**File:** lib/auth-context.tsx **Description:** Manages user authentication
state including login, logout, and registration. Uses localStorage for
persistence.

### Provider Props

| Prop     | Type            | Required | Description      |
| -------- | --------------- | -------- | ---------------- |
| children | React.ReactNode | yes      | Child components |

### Hook: useAuth()

Returns `AuthContextType` with:

| Property             | Type                                            | Description                                       |
| -------------------- | ----------------------------------------------- | ------------------------------------------------- |
| user                 | User \| null                                    | Current logged-in user or null                    |
| login                | (username: string, password: string) => boolean | Login function; returns success status            |
| logout               | () => void                                      | Logout function; clears user and localStorage     |
| registerPhone        | string \| null                                  | Phone number during registration flow             |
| setRegisterPhone     | (phone: string \| null) => void                 | Update registerPhone state                        |
| completeRegistration | (phone: string) => void                         | Complete registration and create customer account |

### Mock Database

Pre-configured accounts:

- Manager: `admin / admin`
- Staff: `Nguyễn Văn An / Nguyễn Văn An`, `Trần Thị Bình / Trần Thị Bình`, etc.
- Customer: Phone number as username, `user1` as password

### Storage

- Key: `coffee-shop-user`
- Format: JSON serialized User object

---

## CartContext (lib/cart-context.tsx)

**File:** lib/cart-context.tsx **Description:** Manages shopping cart state with
localStorage persistence. Tracks items, quantities, and totals.

### Provider Props

| Prop     | Type            | Required | Description      |
| -------- | --------------- | -------- | ---------------- |
| children | React.ReactNode | yes      | Child components |

### Hook: useCart()

Returns `CartContextValue` with:

| Property       | Type                                   | Description                                               |
| -------------- | -------------------------------------- | --------------------------------------------------------- |
| items          | CartItem[]                             | Array of items in cart                                    |
| totalItems     | number                                 | Total quantity of items                                   |
| totalPrice     | number                                 | Total price in VND                                        |
| addToCart      | (product: Product) => void             | Add or increase product quantity                          |
| increaseQty    | (id: number) => void                   | Increase product quantity by 1                            |
| decreaseQty    | (id: number) => void                   | Decrease product quantity by 1 (removes if qty reaches 0) |
| removeFromCart | (id: number) => void                   | Remove product from cart                                  |
| setQuantity    | (id: number, quantity: number) => void | Set exact quantity (removes if 0)                         |

### CartItem Interface

```typescript
interface CartItem {
  id: number;
  name: string;
  description: string;
  price: number;
  quantity: number;
}
```

### Storage

- Key: `coffee-shop-cart`
- Format: JSON serialized CartItem[]
- Auto-loads on mount and auto-saves on change

---

## MenuContext (lib/menu-context.tsx)

**File:** lib/menu-context.tsx **Description:** Provides shared category/menu
state across components. Synchronizes Header mobile menu and Navbar sidebar
selection.

### Provider Props

| Prop     | Type            | Required | Description      |
| -------- | --------------- | -------- | ---------------- |
| children | React.ReactNode | yes      | Child components |

### Hook: useMenu()

Returns `MenuContextType` with:

| Property          | Type                 | Description                                     |
| ----------------- | -------------------- | ----------------------------------------------- |
| activeCategory    | string               | Currently selected category id (default: "all") |
| setActiveCategory | (id: string) => void | Update active category                          |

### Use Cases

- Sync Navbar sidebar and Header mobile menu category selection
- Clear search query when category changes (implemented in main page)
- Pass selected category to product filter logic

### Default Value

- `activeCategory: "all"` - Show all products by default
