Files
frontend/components/COMPONENTS.md
T
2026-03-30 01:24:59 +00:00

12 KiB

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

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

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