commit bd885cf0fa9ab0c0d5ed7fa3acc6987b212a3007 Author: admin Date: Fri Jun 19 15:46:24 2026 +0000 Initial commit: Kottgard testing website code diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5e10cc7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a1c21f2 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +# Kött Gård — Premium Halal Meat Delivery + +A modern, premium e-commerce website for 100% Halal meat delivery. Built with Next.js, TypeScript, and Tailwind CSS. + +## Features + +- **Premium Design** — Green, white, and gold color palette with luxurious typography +- **Multi-language** — Swedish (default), English, and Urdu with RTL support +- **Product Customization** + - Chicken: Choose quantity in pieces (4, 8, 10, 12, 14) + - Beef & Lamb: Choose cutting style (Nihari, Karahi, Qeema, Boneless, Steak) + - Fish: Standard professional cut +- **Full E-commerce Flow** — Shop, product details, cart, secure checkout +- **User Accounts** — Registration, login, order history +- **Wishlist** — Save favorite products +- **Responsive** — Mobile-first design +- **SEO Optimized** — Meta tags, semantic HTML, Open Graph + +## Getting Started + +```bash +npm install +npm run dev +``` + +Open [http://localhost:3000](http://localhost:3000). + +### Demo Account + +- Email: `demo@kottgard.se` +- Password: `demo123` + +### Languages + +Use the language bar or globe icon to switch between: +- **Svenska** (Swedish) — default +- **English** +- **اردو** (Urdu) — RTL +- **العربية** (Arabic) — RTL +- **فارسی** (Persian) — RTL +- **Türkçe** (Turkish) + +## Tech Stack + +- **Framework:** Next.js 13 (App Router) +- **Language:** TypeScript +- **Styling:** Tailwind CSS +- **State:** Zustand (persisted cart, auth, wishlist, locale) +- **i18n:** Custom translation system with locale store +- **Icons:** Lucide React + +## Project Structure (Clean Architecture) + +``` +src/ +├── domain/ # Entities + business rules (no React/Next) +├── application/ # Use cases, ports, container.ts +├── infrastructure/ # Zustand, product data, i18n adapters +├── presentation/ # useCart, useAuth, useCatalog hooks +├── app/ # Next.js pages (thin UI) +├── components/ # React UI components +├── i18n/ # Translation dictionaries (en, sv, ur) +├── hooks/ # useTranslation +├── lib/ # Backward-compatible facades +├── store/ # Backward-compatible re-exports +└── types/ # Re-exports domain entities +``` + +See **[docs/CLEAN-ARCHITECTURE.md](docs/CLEAN-ARCHITECTURE.md)** for the full step-by-step guide. \ No newline at end of file diff --git a/docs/CLEAN-ARCHITECTURE.md b/docs/CLEAN-ARCHITECTURE.md new file mode 100644 index 0000000..05d551e --- /dev/null +++ b/docs/CLEAN-ARCHITECTURE.md @@ -0,0 +1,365 @@ +# Kött Gård — Clean Architecture Guide + +This document explains **what we changed**, **why**, and **how to work in the new structure**. + +--- + +## What is Clean Architecture? + +Clean Architecture splits code into **layers**. Inner layers hold business rules. Outer layers hold frameworks (React, Next.js, Zustand, localStorage). + +**Golden rule:** Dependencies point **inward only**. + +``` +Presentation → Application → Domain +Infrastructure → Application → Domain + +Domain imports NOTHING from outside itself. +``` + +--- + +## Before vs After + +### Before (mixed responsibilities) + +``` +page.tsx → filter products, calculate delivery fee, create orders +store/cart.ts → persistence + merge logic + totals +lib/products.ts → data + queries in one file +``` + +Problems: +- Delivery fee `total > 500 ? 0 : 49` was copy-pasted in cart **and** checkout +- Shop filtering logic lived inside the React page +- Hard to swap localStorage for a real API later + +### After (separated layers) + +| Layer | Folder | Job | +|-------|--------|-----| +| **Domain** | `src/domain/` | Business rules — cart math, delivery fee, order IDs | +| **Application** | `src/application/` | Use cases + ports (interfaces) | +| **Infrastructure** | `src/infrastructure/` | Zustand, product data file, i18n adapter | +| **Presentation** | `src/app/`, `src/components/`, `src/presentation/hooks/` | UI only — calls use cases | + +--- + +## Layer diagram + +```mermaid +flowchart TB + subgraph Presentation + P1[app/pages] + P2[components] + P3[presentation/hooks] + end + + subgraph Application + A1[use-cases] + A2[ports interfaces] + A3[container.ts] + end + + subgraph Domain + D1[entities] + D2[domain services] + end + + subgraph Infrastructure + I1[Zustand stores] + I2[InMemoryProductRepository] + I3[products.data.ts] + end + + P1 --> P3 + P3 --> A3 + A3 --> A1 + A1 --> A2 + A1 --> D2 + A2 -.implemented by.- I1 + A2 -.implemented by.- I2 + I2 --> I3 + D2 --> D1 +``` + +--- + +## Step-by-step: what we did + +### Step 1 — Domain entities (`src/domain/entities/`) + +**What:** Moved `Product`, `CartItem`, `Order`, `User`, etc. from `types/` into the domain. + +**Why:** Entities are the core vocabulary of the business. They must not depend on React or Next.js. + +**Example:** `Product` describes a meat item (price, category, slug) — not how it is rendered. + +``` +src/domain/entities/index.ts +``` + +`src/types/index.ts` now **re-exports** domain entities so old imports still work. + +--- + +### Step 2 — Domain services (`src/domain/services/`) + +**What:** Pure functions / classes for business rules. + +| Service | Rule extracted from | +|---------|---------------------| +| `CartDomainService` | `store/cart.ts` — add item, merge duplicates, totals | +| `DeliveryFeeService` | `cart/page.tsx` + `checkout/page.tsx` — 49 kr fee, free over 500 kr | +| `CustomizationDomainService` | `lib/customization.ts` — cut styles, cart line keys | +| `OrderDomainService` | `checkout/page.tsx` — order ID format, order object shape | +| `AuthDomainService` | `store/auth.ts` — demo password check, user creation | +| `CatalogDomainService` | `shop/page.tsx` — filter by category, search, sort | + +**Why:** One place per rule. Change delivery threshold once → cart and checkout both update. + +**Example — delivery fee (single source of truth):** + +```typescript +// src/domain/constants/commerce.ts +export const FREE_DELIVERY_THRESHOLD_SEK = 500; +export const STANDARD_DELIVERY_FEE_SEK = 49; + +// src/domain/services/DeliveryFeeService.ts +static calculateDeliveryFee(subtotal: number): number { + return subtotal > FREE_DELIVERY_THRESHOLD_SEK ? 0 : STANDARD_DELIVERY_FEE_SEK; +} +``` + +--- + +### Step 3 — Application ports (`src/application/ports/`) + +**What:** TypeScript **interfaces** describing what the app needs — not how it is stored. + +| Port | Contract | +|------|----------| +| `IProductRepository` | `findAll()`, `findBySlug()`, `findFeatured()` | +| `ICartRepository` | `getItems()`, `addItem()`, `clearCart()` | +| `IAuthRepository` | `login()`, `register()`, `addOrder()` | +| `IWishlistRepository` | `toggleItem()`, `isInWishlist()` | +| `ITranslationService` | `translate(key)` | + +**Why:** Today products live in a static array. Tomorrow they might come from Shopify or a database. **Only the infrastructure adapter changes** — use cases stay the same. + +--- + +### Step 4 — Use cases (`src/application/use-cases/`) + +**What:** One class per user action. Orchestrates domain + ports. + +| Use case | Replaces logic in | +|----------|-------------------| +| `FilterProductsUseCase` | Shop page filtering/sorting | +| `GetProductBySlugUseCase` | `getProductBySlug()` calls | +| `LocalizeProductUseCase` | `lib/product-i18n.ts` | +| `AddToCartUseCase` | Cart add button | +| `GetCartSummaryUseCase` | Cart totals + delivery | +| `PlaceOrderUseCase` | Checkout submit handler | +| `LoginUseCase` / `RegisterUseCase` | Login page | + +**Example — checkout flow:** + +``` +User clicks Pay + → PlaceOrderUseCase.execute() + → OrderDomainService.createOrder() [domain] + → authRepository.addOrder() [port] + → cartRepository.clearCart() [port] +``` + +File: `src/application/use-cases/checkout/PlaceOrder.ts` + +--- + +### Step 5 — Infrastructure (`src/infrastructure/`) + +**What:** Concrete implementations of ports + framework code. + +| File | Role | +|------|------| +| `data/products.data.ts` | Static 16-product catalog | +| `repositories/InMemoryProductRepository.ts` | Implements `IProductRepository` | +| `persistence/zustand/cartStore.ts` | Zustand + `ZustandCartRepository` | +| `persistence/zustand/authStore.ts` | Auth persistence + demo user | +| `persistence/zustand/wishlistStore.ts` | Wishlist persistence | +| `i18n/I18nTranslationService.ts` | Wraps existing `i18n/` dictionaries | + +**Why Zustand stays:** It is an **infrastructure detail** (browser storage). The application layer only sees `ICartRepository`. + +--- + +### Step 6 — Composition root (`src/application/container.ts`) + +**What:** One file that wires everything together. + +```typescript +export const container = new ApplicationContainer(); +// container.addToCart.execute(...) +// container.getCartSummary.execute() +// container.placeOrder.execute(...) +``` + +**Why:** Pages and hooks do not `new InMemoryProductRepository()` themselves. That would couple UI to infrastructure. The container is the **only** place that knows concrete classes. + +--- + +### Step 7 — Presentation hooks (`src/presentation/hooks/`) + +**What:** React-friendly API on top of the container. + +| Hook | Use in | +|------|--------| +| `useCart()` | Cart page, Header, product page | +| `useAuth()` | Login, account, checkout | +| `useWishlist()` | Product card, wishlist page | +| `useCheckout()` | Checkout submit | +| `useCatalogFilter()` | Shop page | + +**Example:** + +```typescript +// Old (page knew about Zustand internals + business math) +const { getTotal } = useCartStore(); +const deliveryFee = total > 500 ? 0 : 49; + +// New (page uses use case result) +const { summary } = useCart(); +const { subtotal, deliveryFee, grandTotal } = summary; +``` + +--- + +### Step 8 — Backward-compatible facades + +Old paths still work so nothing breaks silently: + +| Old import | Now points to | +|------------|---------------| +| `@/types` | `@/domain/entities` | +| `@/lib/customization` | `CustomizationDomainService` | +| `@/lib/products` | `container.productRepository` | +| `@/store/cart` | infrastructure + `useCart` hook | + +--- + +## New folder map + +``` +src/ +├── domain/ +│ ├── entities/ # Product, Order, CartItem… +│ ├── constants/ # FREE_DELIVERY_THRESHOLD_SEK +│ └── services/ # CartDomainService, DeliveryFeeService… +│ +├── application/ +│ ├── ports/ # IProductRepository, ICartRepository… +│ ├── use-cases/ # AddToCart, PlaceOrder, FilterProducts… +│ ├── dtos/ # LocalizedProduct, CartSummary +│ └── container.ts # ★ wires everything +│ +├── infrastructure/ +│ ├── data/ # products.data.ts, demo-orders.data.ts +│ ├── repositories/ # InMemoryProductRepository +│ ├── persistence/zustand/ # Stores + repository adapters +│ └── i18n/ # I18nTranslationService +│ +├── presentation/ +│ └── hooks/ # useCart, useAuth, useCatalog… +│ +├── app/ # Thin pages (UI + hooks) +├── components/ # Visual components +├── hooks/useTranslation.ts # Still used for t('key') +├── lib/ # Facades (backward compat) +├── store/ # Re-exports (backward compat) +└── types/ # Re-exports (backward compat) +``` + +--- + +## How to add a feature (cheat sheet) + +### Change delivery fee rule +1. Edit `src/domain/constants/commerce.ts` +2. Done — `GetCartSummaryUseCase` and `PlaceOrderUseCase` pick it up automatically + +### Add a new product +1. Add entry in `src/infrastructure/data/products.data.ts` +2. Add translations in `src/i18n/locales/*.ts` + +### Add a new page action (e.g. "apply coupon") +1. Add rule in `src/domain/services/` if it is business logic +2. Add port method if it needs storage +3. Create `src/application/use-cases/cart/ApplyCoupon.ts` +4. Register in `container.ts` +5. Expose via `useCart()` or new hook +6. Call from page/component + +### Replace static products with an API +1. Create `src/infrastructure/repositories/ApiProductRepository.ts` implementing `IProductRepository` +2. In `container.ts`, swap `InMemoryProductRepository` → `ApiProductRepository` +3. **No changes** to shop page, use cases, or domain + +--- + +## Dependency rules (memorize this) + +| Layer | Can import | +|-------|------------| +| Domain | Only domain | +| Application | Domain + its own ports/DTOs | +| Infrastructure | Application ports + Domain | +| Presentation | Application container + hooks + React | + +| Layer | Cannot import | +|-------|---------------| +| Domain | React, Next, Zustand, `app/`, `components/` | +| Application | Zustand, `page.tsx`, JSX | +| Use cases | Concrete repositories (only interfaces) | + +--- + +## Request flow example: Add to cart + +```mermaid +sequenceDiagram + participant Page as product/page.tsx + participant Hook as useCart() + participant UC as AddToCartUseCase + participant Repo as ZustandCartRepository + participant Domain as CartDomainService + participant Store as Zustand persist + + Page->>Hook: addItem(product, customization, label) + Hook->>UC: execute(...) + UC->>Repo: addItem(...) + Repo->>Domain: addItem(items, product, ...) + Domain-->>Repo: newItems[] + Repo->>Store: setItems(newItems) + Store-->>Page: React re-render +``` + +--- + +## Verify the project + +```bash +cd /Users/apple/Desktop/code/Kottgard +npm run build # must pass +npm run dev # test at http://localhost:3000 +``` + +Demo login: `demo@kottgard.se` / `demo123` + +--- + +## Further reading + +- Uncle Bob — Clean Architecture (concentric circles diagram) +- This project's annotated code: `docs/annotated/src/` +- Beginner folder map: `~/Desktop/Kottgard-Project-Learn/` \ No newline at end of file diff --git a/docs/Kottgard-Website-Guide.docx b/docs/Kottgard-Website-Guide.docx new file mode 100644 index 0000000..52037ae Binary files /dev/null and b/docs/Kottgard-Website-Guide.docx differ diff --git a/docs/annotate-all.mjs b/docs/annotate-all.mjs new file mode 100644 index 0000000..c1e6eea --- /dev/null +++ b/docs/annotate-all.mjs @@ -0,0 +1,195 @@ +/** + * Generates line-by-line annotated copies of every src/ file. + * Output: docs/annotated/src/** (mirrors src/ structure) + * Run: node docs/annotate-all.mjs + */ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.join(__dirname, '..'); +const SRC = path.join(ROOT, 'src'); +const OUT = path.join(__dirname, 'annotated', 'src'); + +function explainLine(line, lineNum, relPath) { + const t = line.trim(); + const indent = line.match(/^(\s*)/)[1]; + + if (t === '') return `${indent}// (blank line — separates logical blocks for readability)`; + if (t.startsWith('/**') || t.startsWith('*') || t.startsWith('*/')) + return `${indent}// Block comment — documents the file or function below`; + if (t.startsWith('//')) return line; + + if (t === "'use client';" || t === '"use client";') + return `${indent}// Next.js: this file runs in the BROWSER (needs useState, onClick, localStorage, etc.)`; + if (t.startsWith('import type ')) + return `${indent}// Type-only import — erased at compile time; no JavaScript bundle cost`; + if (t.startsWith('import ')) { + if (t.includes("from 'react'") || t.includes('from "react"')) + return `${indent}// Import React — core UI library (components, hooks, JSX)`; + if (t.includes('next/link')) + return `${indent}// Next.js Link — fast client-side navigation without full page reload`; + if (t.includes('next/image')) + return `${indent}// Next.js Image — optimized images (lazy load, WebP/AVIF)`; + if (t.includes('next/navigation')) + return `${indent}// Next.js App Router hooks — useRouter, useParams, useSearchParams`; + if (t.includes('next/font')) + return `${indent}// Self-hosted Google fonts — better performance than external CSS`; + if (t.includes('lucide-react')) + return `${indent}// Lucide icons — lightweight SVG icon components`; + if (t.includes('zustand')) + return `${indent}// Zustand — simple global state store (cart, auth, locale)`; + if (t.includes('@/')) + return `${indent}// Import project module (@/ alias = src/ folder in tsconfig)`; + if (t.includes("'./") || t.includes('"./')) + return `${indent}// Import from a relative file in the same project`; + return `${indent}// Import external package or local module`; + } + + if (t.startsWith('export type ') || t.startsWith('export interface ')) + return `${indent}// Export TypeScript type — defines data shape used across the app`; + if (t.startsWith('interface ')) + return `${indent}// TypeScript interface — contract for object properties and methods`; + if (t.startsWith('type ')) + return `${indent}// TypeScript type alias — union or shorthand for complex types`; + if (t.startsWith('export default function')) + return `${indent}// Default export — main component/page Next.js or other files import`; + if (t.startsWith('export function')) + return `${indent}// Named export — utility function other files can import`; + if (t.startsWith('export const ')) + return `${indent}// Named export constant — shared config/data imported elsewhere`; + if (t.startsWith('const ') && t.includes('= create<')) + return `${indent}// Create Zustand store — global state hook (useXxxStore)`; + if (t.startsWith('const ') && t.includes('useState')) + return `${indent}// React useState — local component state that triggers re-render on change`; + if (t.startsWith('const ') && t.includes('useMemo')) + return `${indent}// React useMemo — cache expensive computed value until dependencies change`; + if (t.startsWith('const ') && t.includes('useCallback')) + return `${indent}// React useCallback — stable function reference for useEffect/useMemo deps`; + if (t.startsWith('const ') && t.includes('useEffect')) + return `${indent}// React useEffect — run side effect after render (sync URL, redirect, etc.)`; + if (t.startsWith('const ') && t.includes('useRouter')) + return `${indent}// Next.js router — programmatic navigation (router.push)`; + if (t.startsWith('const ') && t.includes('useSearchParams')) + return `${indent}// Read URL query string (?category=beef&q=steak)`; + if (t.startsWith('const ') && t.includes('useParams')) + return `${indent}// Read dynamic route segment ([slug] from URL)`; + if (t.startsWith('const ') && t.includes('useTranslation')) + return `${indent}// Custom hook — returns t() translator and current locale`; + if (t.startsWith('const ') && t.includes('useCartStore') || t.startsWith('const ') && t.includes('useAuthStore') || t.startsWith('const ') && t.includes('useWishlistStore') || t.startsWith('const ') && t.includes('useLocaleStore')) + return `${indent}// Zustand selector — subscribe to slice of global store`; + if (t.startsWith('function ')) + return `${indent}// Function declaration — reusable logic in this file`; + if (t.startsWith('return (')) + return `${indent}// Return JSX — describes UI tree React renders to the DOM`; + if (t.startsWith('return ')) + return `${indent}// Return value from function`; + if (t.startsWith('if (') || t.startsWith('} else if (')) + return `${indent}// Conditional branch — different behavior based on runtime value`; + if (t.startsWith('switch (')) + return `${indent}// Switch — multiple branches on one variable (e.g. sort order)`; + if (t.startsWith('case ')) + return `${indent}// Switch case — handle one specific value`; + if (t.startsWith('default:')) + return `${indent}// Switch default — fallback when no case matches`; + if (t.startsWith('useEffect(')) + return `${indent}// Side effect hook — runs after paint; deps array controls when it re-runs`; + if (t.includes('.map(')) + return `${indent}// Array.map — transform each item (often render a list of components)`; + if (t.includes('.filter(')) + return `${indent}// Array.filter — keep items matching condition (search, category)`; + if (t.includes('.sort(')) + return `${indent}// Array.sort — reorder items (price, name, featured)`; + if (t.includes('.find(')) + return `${indent}// Array.find — get first matching item or undefined`; + if (t.includes('.reduce(')) + return `${indent}// Array.reduce — accumulate single value (cart total, item count)`; + if (t.startsWith('<') && !t.startsWith('<>')) + return `${indent}// JSX element — HTML-like tag becomes React component in browser`; + if (t.startsWith('<>') || t === '') + return `${indent}// React Fragment — group elements without extra wrapper DOM node`; + if (t.startsWith('{/*')) + return `${indent}// JSX comment — not visible in browser`; + if (t.startsWith('className=')) + return `${indent}// Tailwind CSS utility classes — styling (colors, spacing, layout)`; + if (t.startsWith('href=')) + return `${indent}// Link target URL — internal route or external https://`; + if (t.startsWith('onClick=')) + return `${indent}// Click handler — runs when user clicks (must be client component)`; + if (t.startsWith('onChange=')) + return `${indent}// Change handler — runs when input/select value changes`; + if (t.startsWith('aria-')) + return `${indent}// Accessibility attribute — screen readers and assistive tech`; + if (t.startsWith('}')) + return `${indent}// Closing brace — end of block (function, if, object, JSX)`; + if (t.startsWith(']') || t.startsWith('];')) + return `${indent}// End of array literal`; + if (t.endsWith(',') && !t.includes('//')) + return `${indent}// Property or array item — trailing comma allowed in TypeScript`; + if (relPath.includes('locales/') && t.match(/^\s{2}\w+:/)) + return `${indent}// i18n translation section — keys used by t('section.key') in components`; + if (relPath.includes('locales/') && t.includes("name:") || relPath.includes('locales/') && t.includes("title:")) + return `${indent}// Translated UI string for current language (sv / en / ur)`; + if (relPath.includes('products.ts')) + return `${indent}// Product catalog entry — demo data shown in shop and product pages`; + if (t.includes('persist(')) + return `${indent}// Zustand persist — save store to localStorage between visits`; + + return `${indent}// Line ${lineNum}: ${t.length > 60 ? t.slice(0, 57) + '...' : t || 'code'}`; +} + +function annotateFile(content, relPath) { + const lines = content.split('\n'); + const ext = relPath.endsWith('.tsx') ? 'tsx' : 'ts'; + const header = [ + `/**`, + ` * ANNOTATED COPY — every line explained`, + ` * Source: src/${relPath}`, + ` * NOT used by the app — read this to learn how the real file works`, + ` */`, + '', + ]; + + const body = lines.map((line, i) => { + const comment = explainLine(line, i + 1, relPath); + if (line.trim().startsWith('//') && comment === line) return line; + if (line.trim() === '' && comment.includes('blank')) return comment; + return `${comment}\n${line}`; + }); + + return header.join('\n') + body.join('\n') + '\n'; +} + +function walk(dir, base = '') { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + let count = 0; + for (const e of entries) { + const rel = base ? `${base}/${e.name}` : e.name; + const full = path.join(dir, e.name); + if (e.isDirectory()) { + count += walk(full, rel); + } else if (/\.(ts|tsx)$/.test(e.name)) { + const content = fs.readFileSync(full, 'utf8'); + const annotated = annotateFile(content, rel); + const outName = e.name.replace(/\.(tsx?)$/, '.annotated.$1'); + const outDir = path.join(OUT, base); + fs.mkdirSync(outDir, { recursive: true }); + fs.writeFileSync(path.join(outDir, outName), annotated); + count++; + } + } + return count; +} + +// Remove old flat annotated files (replaced by src/ mirror) +const oldFlat = path.join(__dirname, 'annotated'); +for (const f of fs.readdirSync(oldFlat)) { + if (f.endsWith('.annotated.ts') || f.endsWith('.annotated.tsx')) { + if (!f.startsWith('0')) continue; + try { fs.unlinkSync(path.join(oldFlat, f)); } catch (_) {} + } +} + +const n = walk(SRC); +console.log(`Annotated ${n} files → docs/annotated/src/`); \ No newline at end of file diff --git a/docs/annotated/README.md b/docs/annotated/README.md new file mode 100644 index 0000000..3e892f2 --- /dev/null +++ b/docs/annotated/README.md @@ -0,0 +1,63 @@ +# Annotated Source Code — Every Line Explained + +This folder contains **annotated copies** of **every** file in `src/`. Each line of the original code is followed or preceded by a comment explaining: + +- **What** the syntax means (TypeScript, React, Next.js) +- **Why** it exists in the Kött Gård project + +Production code in `src/` stays clean. Learning comments live here only. + +## Regenerate all annotations + +```bash +node docs/annotate-all.mjs +``` + +Run this after you change source files to refresh the annotated copies. + +## Folder mirror + +``` +docs/annotated/src/ ← you are here (annotated) +src/ ← real app code (no line comments) +``` + +| Annotated path | Original path | +|----------------|---------------| +| `docs/annotated/src/app/page.annotated.tsx` | `src/app/page.tsx` | +| `docs/annotated/src/lib/products.annotated.ts` | `src/lib/products.ts` | +| `docs/annotated/src/store/cart.annotated.ts` | `src/store/cart.ts` | +| … | (52 files total) | + +## File categories + +| Folder | What it contains | +|--------|------------------| +| `app/` | Pages and routes (Next.js App Router) | +| `components/` | Reusable UI (home sections, layout, product cards) | +| `lib/` | Data, images, constants, helpers | +| `store/` | Zustand global state (cart, auth, wishlist, locale) | +| `i18n/` | Translations (Swedish, English, Urdu) | +| `hooks/` | Custom React hooks | +| `types/` | TypeScript interfaces | + +## How to read + +1. Open the **original** file in `src/` in your editor. +2. Open the matching **`.annotated.ts`** or **`.annotated.tsx`** file side-by-side. +3. Read the gray `//` comment above each line, then the code. + +## Programming languages in this project + +| Language | Role | +|----------|------| +| **TypeScript** | Types catch errors before run; interfaces for Product, Cart, Order | +| **React 18** | Components + JSX UI; hooks for state and effects | +| **Next.js 13** | File-based routing, layouts, metadata, image optimization | +| **Tailwind CSS** | Utility classes for burgundy/cream/gold design | +| **Zustand** | Global stores with `persist` → localStorage | + +## Related documents + +- Word guide: `docs/Kottgard-Website-Guide.docx` +- Visual guide: `../kottgard documentation/16-website-visual-and-code-guide.md` \ No newline at end of file diff --git a/docs/annotated/src/app/about/page.annotated.tsx b/docs/annotated/src/app/about/page.annotated.tsx new file mode 100644 index 0000000..011e6e1 --- /dev/null +++ b/docs/annotated/src/app/about/page.annotated.tsx @@ -0,0 +1,249 @@ +/** + * ANNOTATED COPY — every line explained + * Source: src/app/about/page.tsx + * NOT used by the app — read this to learn how the real file works + */ +// Next.js: this file runs in the BROWSER (needs useState, onClick, localStorage, etc.) +'use client'; +// (blank line — separates logical blocks for readability) +// Next.js Link — fast client-side navigation without full page reload +import Link from 'next/link'; +// Lucide icons — lightweight SVG icon components +import { ShieldCheck, Leaf, Award, Truck, Phone, Mail, MapPin } from 'lucide-react'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { useTranslation } from '@/hooks/useTranslation'; +// Import external package or local module +import { + // Property or array item — trailing comma allowed in TypeScript + SITE_ADDRESS, + // Property or array item — trailing comma allowed in TypeScript + SITE_EMAIL, + // Property or array item — trailing comma allowed in TypeScript + SITE_PHONE_DISPLAY, + // Property or array item — trailing comma allowed in TypeScript + SITE_HOURS, +// Closing brace — end of block (function, if, object, JSX) +} from '@/lib/constants'; +// (blank line — separates logical blocks for readability) +// Default export — main component/page Next.js or other files import +export default function AboutPage() { + // Custom hook — returns t() translator and current locale + const { t } = useTranslation(); + // Line 15: const telHref = `tel:${SITE_PHONE_DISPLAY.replace(/\s/g, ... + const telHref = `tel:${SITE_PHONE_DISPLAY.replace(/\s/g, '')}`; +// (blank line — separates logical blocks for readability) + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 22: {t('about.title', { name: t('site.name') })} + {t('about.title', { name: t('site.name') })} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.subtitle')}

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.ourStory')}

+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 32: {t('about.storyP1', { name: t('site.name') })} + {t('about.storyP1', { name: t('site.name') })} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.storyP2')}

+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +

{t('about.halalTitle')}

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 43: {t('about.halalDesc', { name: t('site.name') })} + {t('about.halalDesc', { name: t('site.name') })} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 49: {[ + {[ + // Property or array item — trailing comma allowed in TypeScript + { icon: Leaf, title: t('about.freshDaily'), desc: t('about.freshDailyDesc') }, + // Line 51: { + { + // Property or array item — trailing comma allowed in TypeScript + icon: Award, + // Property or array item — trailing comma allowed in TypeScript + title: t('about.premiumQuality'), + // Property or array item — trailing comma allowed in TypeScript + desc: t('about.premiumQualityDesc'), + // Closing brace — end of block (function, if, object, JSX) + }, + // Line 56: { + { + // Property or array item — trailing comma allowed in TypeScript + icon: Truck, + // Property or array item — trailing comma allowed in TypeScript + title: t('about.fastDelivery'), + // Property or array item — trailing comma allowed in TypeScript + desc: t('about.fastDeliveryDesc'), + // Closing brace — end of block (function, if, object, JSX) + }, + // Array.map — transform each item (often render a list of components) + ].map((item) => ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +

{item.title}

+ // JSX element — HTML-like tag becomes React component in browser +

{item.desc}

+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 67: ))} + ))} + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.deliveryTitle')}

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.delivery1')}

+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.delivery2')}

+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.delivery3')}

+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.delivery4')}

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.contactTitle')}

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // Line 87: {SITE_PHONE_DISPLAY} + {SITE_PHONE_DISPLAY} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // Line 93: {SITE_EMAIL} + {SITE_EMAIL} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // Line 99: {SITE_ADDRESS} + {SITE_ADDRESS} + // JSX element — HTML-like tag becomes React component in browser +
+ // Line 101: {t('footer.hours', { hours: SITE_HOURS })} + {t('footer.hours', { hours: SITE_HOURS })} + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + > + // Line 110: {t('cta.button')} → + {t('cta.button')} → + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.privacyTitle')}

+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.privacyText')}

+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.termsTitle')}

+ // JSX element — HTML-like tag becomes React component in browser +

{t('about.termsText')}

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 126: ); + ); +// Closing brace — end of block (function, if, object, JSX) +} diff --git a/docs/annotated/src/app/account/page.annotated.tsx b/docs/annotated/src/app/account/page.annotated.tsx new file mode 100644 index 0000000..1d2073c --- /dev/null +++ b/docs/annotated/src/app/account/page.annotated.tsx @@ -0,0 +1,321 @@ +/** + * ANNOTATED COPY — every line explained + * Source: src/app/account/page.tsx + * NOT used by the app — read this to learn how the real file works + */ +// Next.js: this file runs in the BROWSER (needs useState, onClick, localStorage, etc.) +'use client'; +// (blank line — separates logical blocks for readability) +// Import React — core UI library (components, hooks, JSX) +import { useEffect } from 'react'; +// Next.js Link — fast client-side navigation without full page reload +import Link from 'next/link'; +// Next.js App Router hooks — useRouter, useParams, useSearchParams +import { useRouter } from 'next/navigation'; +// Lucide icons — lightweight SVG icon components +import { User, Package, MapPin, Phone, Mail, LogOut, Heart } from 'lucide-react'; +// Import project module (@/ alias = src/ folder in tsconfig) +import Button from '@/components/ui/Button'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { useAuthStore } from '@/store/auth'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { formatPrice, formatDate, getFormatLocale } from '@/lib/utils'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { useTranslation } from '@/hooks/useTranslation'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { localizeProduct } from '@/lib/product-i18n'; +// (blank line — separates logical blocks for readability) +// Default export — main component/page Next.js or other files import +export default function AccountPage() { + // Next.js router — programmatic navigation (router.push) + const router = useRouter(); + // Zustand selector — subscribe to slice of global store + const { user, isAuthenticated, orders, logout } = useAuthStore(); + // Custom hook — returns t() translator and current locale + const { t, locale } = useTranslation(); +// (blank line — separates logical blocks for readability) + // Side effect hook — runs after paint; deps array controls when it re-runs + useEffect(() => { + // Conditional branch — different behavior based on runtime value + if (!isAuthenticated) { + // Line 20: router.push('/login'); + router.push('/login'); + // Closing brace — end of block (function, if, object, JSX) + } + // Closing brace — end of block (function, if, object, JSX) + }, [isAuthenticated, router]); +// (blank line — separates logical blocks for readability) + // Conditional branch — different behavior based on runtime value + if (!isAuthenticated || !user) { + // Return value from function + return null; + // Closing brace — end of block (function, if, object, JSX) + } +// (blank line — separates logical blocks for readability) + // Line 28: const handleLogout = () => { + const handleLogout = () => { + // Line 29: logout(); + logout(); + // Line 30: router.push('/'); + router.push('/'); + // Closing brace — end of block (function, if, object, JSX) + }; +// (blank line — separates logical blocks for readability) + // Line 33: const fmt = (n: number) => formatPrice(n, getFormatLocale... + const fmt = (n: number) => formatPrice(n, getFormatLocale(locale)); +// (blank line — separates logical blocks for readability) + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{t('account.title')}

+ // JSX element — HTML-like tag becomes React component in browser +

{t('account.welcome', { name: user.name })}

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{user.name}

+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 55: {t('account.memberSince', { date: formatDate(user.created... + {t('account.memberSince', { date: formatDate(user.createdAt) })} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // Line 63: {user.email} + {user.email} + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // Line 67: {user.phone} + {user.phone} + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // Line 71: {user.address.street}, {user.address.city}, {user.address... + {user.address.street}, {user.address.city}, {user.address.state}{' '} + // Line 72: {user.address.zip} + {user.address.zip} + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + > + // JSX element — HTML-like tag becomes React component in browser + + // Line 83: {t('account.myWishlist')} + {t('account.myWishlist')} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +

+ // Line 100: {t('account.orderHistory')} + {t('account.orderHistory')} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // Line 104: {orders.length === 0 ? ( + {orders.length === 0 ? ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{t('account.noOrders')}

+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // Line 111: ) : ( + ) : ( + // JSX element — HTML-like tag becomes React component in browser +
+ // Array.map — transform each item (often render a list of components) + {orders.map((order) => ( + // JSX element — HTML-like tag becomes React component in browser +
+ > + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{order.id}

+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 122: {formatDate(order.createdAt)} + {formatDate(order.createdAt)} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{fmt(order.total)}

+ // JSX element — HTML-like tag becomes React component in browser + + // Line 128: {t(`orderStatus.${order.status}`)} + {t(`orderStatus.${order.status}`)} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Array.map — transform each item (often render a list of components) + {order.items.map((item) => { + // Line 134: const localized = localizeProduct(item.product, t); + const localized = localizeProduct(item.product, t); + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser +
+ > + // JSX element — HTML-like tag becomes React component in browser + + > + // Line 144: {localized.name} × {item.quantity} + {localized.name} × {item.quantity} + // JSX element — HTML-like tag becomes React component in browser + + // Line 146: ({item.customizationLabel}) + ({item.customizationLabel}) + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + {fmt(item.product.price * item.quantity)} + // JSX element — HTML-like tag becomes React component in browser +
+ // Line 151: ); + ); + // Closing brace — end of block (function, if, object, JSX) + })} + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 155: ))} + ))} + // JSX element — HTML-like tag becomes React component in browser +
+ // Line 157: )} + )} + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 163: ); + ); +// Closing brace — end of block (function, if, object, JSX) +} diff --git a/docs/annotated/src/app/cart/page.annotated.tsx b/docs/annotated/src/app/cart/page.annotated.tsx new file mode 100644 index 0000000..710a3cb --- /dev/null +++ b/docs/annotated/src/app/cart/page.annotated.tsx @@ -0,0 +1,348 @@ +/** + * ANNOTATED COPY — every line explained + * Source: src/app/cart/page.tsx + * NOT used by the app — read this to learn how the real file works + */ +// Next.js: this file runs in the BROWSER (needs useState, onClick, localStorage, etc.) +'use client'; +// (blank line — separates logical blocks for readability) +// Import project module (@/ alias = src/ folder in tsconfig) +import AppImage from '@/components/ui/AppImage'; +// Next.js Link — fast client-side navigation without full page reload +import Link from 'next/link'; +// Lucide icons — lightweight SVG icon components +import { Minus, Plus, Trash2, ShoppingBag, ArrowRight } from 'lucide-react'; +// Import project module (@/ alias = src/ folder in tsconfig) +import Button from '@/components/ui/Button'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { useCartStore } from '@/store/cart'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { formatPrice, getFormatLocale } from '@/lib/utils'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { useTranslation } from '@/hooks/useTranslation'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { localizeProduct } from '@/lib/product-i18n'; +// (blank line — separates logical blocks for readability) +// Default export — main component/page Next.js or other files import +export default function CartPage() { + // Zustand selector — subscribe to slice of global store + const { items, updateQuantity, removeItem, getTotal } = useCartStore(); + // Custom hook — returns t() translator and current locale + const { t, locale } = useTranslation(); + // Line 15: const total = getTotal(); + const total = getTotal(); + // Line 16: const deliveryFee = total > 500 ? 0 : 49; + const deliveryFee = total > 500 ? 0 : 49; + // Line 17: const grandTotal = total + deliveryFee; + const grandTotal = total + deliveryFee; + // Line 18: const fmt = (n: number) => formatPrice(n, getFormatLocale... + const fmt = (n: number) => formatPrice(n, getFormatLocale(locale)); +// (blank line — separates logical blocks for readability) + // Conditional branch — different behavior based on runtime value + if (items.length === 0) { + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 27: {t('cart.empty')} + {t('cart.empty')} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +

{t('cart.emptyHint')}

+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // Line 36: ); + ); + // Closing brace — end of block (function, if, object, JSX) + } +// (blank line — separates logical blocks for readability) + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{t('cart.title')}

+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 45: {t('cart.itemsCount', { count: items.length })} + {t('cart.itemsCount', { count: items.length })} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Array.map — transform each item (often render a list of components) + {items.map((item) => { + // Line 54: const localized = localizeProduct(item.product, t); + const localized = localizeProduct(item.product, t); + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + /> + // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + > + // Line 74: {localized.name} + {localized.name} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +

+ // Line 77: {t(`categories.${item.product.category}.name`)} + {t(`categories.${item.product.category}.name`)} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // Line 91: {t('cart.customization')} + {t('cart.customization')} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // Line 94: {item.customizationLabel} + {item.customizationLabel} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // Line 108: {item.quantity} + {item.quantity} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // Line 119: {fmt(item.product.price * item.quantity)} + {fmt(item.product.price * item.quantity)} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 124: ); + ); + // Closing brace — end of block (function, if, object, JSX) + })} + // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 131: {t('cart.orderSummary')} + {t('cart.orderSummary')} + // JSX element — HTML-like tag becomes React component in browser +

+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + {t('cart.subtotal')} + // JSX element — HTML-like tag becomes React component in browser + {fmt(total)} + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + {t('cart.delivery')} + // JSX element — HTML-like tag becomes React component in browser + + // Line 142: {deliveryFee === 0 ? ( + {deliveryFee === 0 ? ( + // JSX element — HTML-like tag becomes React component in browser + {t('cart.free')} + // Line 144: ) : ( + ) : ( + // Line 145: fmt(deliveryFee) + fmt(deliveryFee) + // Line 146: )} + )} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // Line 149: {deliveryFee > 0 && ( + {deliveryFee > 0 && ( + // JSX element — HTML-like tag becomes React component in browser +

{t('cart.freeDeliveryHint')}

+ // Line 151: )} + )} + // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + {t('cart.total')} + // JSX element — HTML-like tag becomes React component in browser + {fmt(grandTotal)} + // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + +// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser + + > + // Line 170: {t('cart.continueShopping')} + {t('cart.continueShopping')} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 177: ); + ); +// Closing brace — end of block (function, if, object, JSX) +} diff --git a/docs/annotated/src/app/checkout/page.annotated.tsx b/docs/annotated/src/app/checkout/page.annotated.tsx new file mode 100644 index 0000000..cbfc348 --- /dev/null +++ b/docs/annotated/src/app/checkout/page.annotated.tsx @@ -0,0 +1,701 @@ +/** + * ANNOTATED COPY — every line explained + * Source: src/app/checkout/page.tsx + * NOT used by the app — read this to learn how the real file works + */ +// Next.js: this file runs in the BROWSER (needs useState, onClick, localStorage, etc.) +'use client'; +// (blank line — separates logical blocks for readability) +// Import React — core UI library (components, hooks, JSX) +import { useState } from 'react'; +// Next.js App Router hooks — useRouter, useParams, useSearchParams +import { useRouter } from 'next/navigation'; +// Import project module (@/ alias = src/ folder in tsconfig) +import AppImage from '@/components/ui/AppImage'; +// Next.js Link — fast client-side navigation without full page reload +import Link from 'next/link'; +// Lucide icons — lightweight SVG icon components +import { Lock, CreditCard, Truck, CheckCircle, ChevronLeft } from 'lucide-react'; +// Import project module (@/ alias = src/ folder in tsconfig) +import Button from '@/components/ui/Button'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { useCartStore } from '@/store/cart'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { useAuthStore } from '@/store/auth'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { formatPrice, getFormatLocale } from '@/lib/utils'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { useTranslation } from '@/hooks/useTranslation'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { localizeProduct } from '@/lib/product-i18n'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { Order } from '@/types'; +// (blank line — separates logical blocks for readability) +// Default export — main component/page Next.js or other files import +export default function CheckoutPage() { + // Next.js router — programmatic navigation (router.push) + const router = useRouter(); + // Zustand selector — subscribe to slice of global store + const { items, getTotal, clearCart } = useCartStore(); + // Zustand selector — subscribe to slice of global store + const { user, isAuthenticated, addOrder } = useAuthStore(); + // Custom hook — returns t() translator and current locale + const { t, locale } = useTranslation(); +// (blank line — separates logical blocks for readability) + // React useState — local component state that triggers re-render on change + const [paymentMethod, setPaymentMethod] = useState('card'); + // React useState — local component state that triggers re-render on change + const [isProcessing, setIsProcessing] = useState(false); + // React useState — local component state that triggers re-render on change + const [orderComplete, setOrderComplete] = useState(false); + // React useState — local component state that triggers re-render on change + const [orderId, setOrderId] = useState(''); +// (blank line — separates logical blocks for readability) + // React useState — local component state that triggers re-render on change + const [form, setForm] = useState({ + // Property or array item — trailing comma allowed in TypeScript + name: user?.name || '', + // Property or array item — trailing comma allowed in TypeScript + email: user?.email || '', + // Property or array item — trailing comma allowed in TypeScript + phone: user?.phone || '', + // Property or array item — trailing comma allowed in TypeScript + street: user?.address.street || '', + // Property or array item — trailing comma allowed in TypeScript + city: user?.address.city || '', + // Property or array item — trailing comma allowed in TypeScript + state: user?.address.state || '', + // Property or array item — trailing comma allowed in TypeScript + zip: user?.address.zip || '', + // Property or array item — trailing comma allowed in TypeScript + cardNumber: '', + // Property or array item — trailing comma allowed in TypeScript + expiry: '', + // Property or array item — trailing comma allowed in TypeScript + cvv: '', + // Closing brace — end of block (function, if, object, JSX) + }); +// (blank line — separates logical blocks for readability) + // Line 40: const total = getTotal(); + const total = getTotal(); + // Line 41: const deliveryFee = total > 500 ? 0 : 49; + const deliveryFee = total > 500 ? 0 : 49; + // Line 42: const grandTotal = total + deliveryFee; + const grandTotal = total + deliveryFee; + // Line 43: const fmt = (n: number) => formatPrice(n, getFormatLocale... + const fmt = (n: number) => formatPrice(n, getFormatLocale(locale)); +// (blank line — separates logical blocks for readability) + // Conditional branch — different behavior based on runtime value + if (items.length === 0 && !orderComplete) { + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

{t('checkout.noItems')}

+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // Line 53: ); + ); + // Closing brace — end of block (function, if, object, JSX) + } +// (blank line — separates logical blocks for readability) + // Conditional branch — different behavior based on runtime value + if (orderComplete) { + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 63: {t('checkout.orderConfirmed')} + {t('checkout.orderConfirmed')} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +

{t('checkout.thankYou')}

+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 67: {t('checkout.orderId', { id: orderId })} + {t('checkout.orderId', { id: orderId })} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 78: ); + ); + // Closing brace — end of block (function, if, object, JSX) + } +// (blank line — separates logical blocks for readability) + // Line 81: const handleSubmit = async (e: React.FormEvent) => { + const handleSubmit = async (e: React.FormEvent) => { + // Line 82: e.preventDefault(); + e.preventDefault(); + // Line 83: setIsProcessing(true); + setIsProcessing(true); +// (blank line — separates logical blocks for readability) + // Line 85: await new Promise((resolve) => setTimeout(resolve, 1500)); + await new Promise((resolve) => setTimeout(resolve, 1500)); +// (blank line — separates logical blocks for readability) + // Line 87: const newOrderId = `KG-${Date.now().toString(36).toUpperC... + const newOrderId = `KG-${Date.now().toString(36).toUpperCase()}`; + // Line 88: const order: Order = { + const order: Order = { + // Property or array item — trailing comma allowed in TypeScript + id: newOrderId, + // Property or array item — trailing comma allowed in TypeScript + items: [...items], + // Property or array item — trailing comma allowed in TypeScript + total: grandTotal, + // Property or array item — trailing comma allowed in TypeScript + status: 'confirmed', + // Property or array item — trailing comma allowed in TypeScript + createdAt: new Date().toISOString(), + // Line 94: deliveryAddress: { + deliveryAddress: { + // Property or array item — trailing comma allowed in TypeScript + street: form.street, + // Property or array item — trailing comma allowed in TypeScript + city: form.city, + // Property or array item — trailing comma allowed in TypeScript + state: form.state, + // Property or array item — trailing comma allowed in TypeScript + zip: form.zip, + // Closing brace — end of block (function, if, object, JSX) + }, + // Property or array item — trailing comma allowed in TypeScript + paymentMethod, + // Closing brace — end of block (function, if, object, JSX) + }; +// (blank line — separates logical blocks for readability) + // Line 103: addOrder(order); + addOrder(order); + // Line 104: clearCart(); + clearCart(); + // Line 105: setOrderId(newOrderId); + setOrderId(newOrderId); + // Line 106: setOrderComplete(true); + setOrderComplete(true); + // Line 107: setIsProcessing(false); + setIsProcessing(false); + // Closing brace — end of block (function, if, object, JSX) + }; +// (blank line — separates logical blocks for readability) + // Line 110: const updateField = (field: string, value: string) => { + const updateField = (field: string, value: string) => { + // Line 111: setForm((prev) => ({ ...prev, [field]: value })); + setForm((prev) => ({ ...prev, [field]: value })); + // Closing brace — end of block (function, if, object, JSX) + }; +// (blank line — separates logical blocks for readability) + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + > + // JSX element — HTML-like tag becomes React component in browser + + // Line 122: {t('checkout.backToCart')} + {t('checkout.backToCart')} + // JSX element — HTML-like tag becomes React component in browser + +// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +

{t('checkout.title')}

+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 130: {!isAuthenticated && ( + {!isAuthenticated && ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 133: {t('checkout.haveAccount')}{' '} + {t('checkout.haveAccount')}{' '} + // JSX element — HTML-like tag becomes React component in browser + + // Line 135: {t('checkout.signIn')} + {t('checkout.signIn')} + // JSX element — HTML-like tag becomes React component in browser + {' '} + // Line 137: {t('checkout.fasterCheckout')} + {t('checkout.fasterCheckout')} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 140: )} + )} +// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +

+ // Line 146: {t('checkout.deliveryDetails')} + {t('checkout.deliveryDetails')} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + updateField('name', e.target.value)} + // Line 157: /> + /> + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + updateField('email', e.target.value)} + // Line 167: /> + /> + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + updateField('phone', e.target.value)} + // Line 177: /> + /> + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + updateField('street', e.target.value)} + // Line 186: /> + /> + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + updateField('city', e.target.value)} + // Line 195: /> + /> + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + updateField('state', e.target.value)} + // Line 204: /> + /> + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + updateField('zip', e.target.value)} + // Line 213: /> + /> + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +

{t('checkout.payment')}

+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // Array.map — transform each item (often render a list of components) + {['card', 'cod'].map((method) => ( + // JSX element — HTML-like tag becomes React component in browser + + // Line 240: ))} + ))} + // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // Line 243: {paymentMethod === 'card' && ( + {paymentMethod === 'card' && ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + updateField('cardNumber', e.target.value)} + // Line 253: /> + /> + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + updateField('expiry', e.target.value)} + // Line 263: /> + /> + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + updateField('cvv', e.target.value)} + // Line 273: /> + /> + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 276: )} + )} + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 283: {t('cart.orderSummary')} + {t('cart.orderSummary')} + // JSX element — HTML-like tag becomes React component in browser +

+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // Array.map — transform each item (often render a list of components) + {items.map((item) => { + // Line 288: const localized = localizeProduct(item.product, t); + const localized = localizeProduct(item.product, t); + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + /> + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 302: {localized.name} + {localized.name} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 305: {item.customizationLabel} + {item.customizationLabel} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +

+ // Line 308: {t('checkout.qty', { count: item.quantity })} + {t('checkout.qty', { count: item.quantity })} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // Line 312: {fmt(item.product.price * item.quantity)} + {fmt(item.product.price * item.quantity)} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // Line 315: ); + ); + // Closing brace — end of block (function, if, object, JSX) + })} + // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + {t('cart.subtotal')} + // JSX element — HTML-like tag becomes React component in browser + {fmt(total)} + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + {t('cart.delivery')} + // JSX element — HTML-like tag becomes React component in browser + + // Line 327: {deliveryFee === 0 ? t('cart.free') : fmt(deliveryFee)} + {deliveryFee === 0 ? t('cart.free') : fmt(deliveryFee)} + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + {t('cart.total')} + // JSX element — HTML-like tag becomes React component in browser + {fmt(grandTotal)} + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser + +// (blank line — separates logical blocks for readability) + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser + + // Line 351: {t('checkout.secure')} + {t('checkout.secure')} + // JSX element — HTML-like tag becomes React component in browser +

+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // Line 359: ); + ); +// Closing brace — end of block (function, if, object, JSX) +} diff --git a/docs/annotated/src/app/layout.annotated.tsx b/docs/annotated/src/app/layout.annotated.tsx new file mode 100644 index 0000000..f7bdd0c --- /dev/null +++ b/docs/annotated/src/app/layout.annotated.tsx @@ -0,0 +1,174 @@ +/** + * ANNOTATED COPY — every line explained + * Source: src/app/layout.tsx + * NOT used by the app — read this to learn how the real file works + */ +// Type-only import — erased at compile time; no JavaScript bundle cost +import type { Metadata } from 'next'; +// Self-hosted Google fonts — better performance than external CSS +import { Inter, Playfair_Display, Noto_Nastaliq_Urdu } from 'next/font/google'; +// Import project module (@/ alias = src/ folder in tsconfig) +import LanguageBanner from '@/components/layout/LanguageBanner'; +// Import project module (@/ alias = src/ folder in tsconfig) +import Header from '@/components/layout/Header'; +// Import project module (@/ alias = src/ folder in tsconfig) +import Footer from '@/components/layout/Footer'; +// Import project module (@/ alias = src/ folder in tsconfig) +import LocaleAttributes from '@/components/layout/LocaleAttributes'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { SITE_NAME } from '@/lib/constants'; +// Import from a relative file in the same project +import './globals.css'; +// (blank line — separates logical blocks for readability) +// Line 10: const inter = Inter({ +const inter = Inter({ + // Property or array item — trailing comma allowed in TypeScript + subsets: ['latin'], + // Property or array item — trailing comma allowed in TypeScript + variable: '--font-inter', + // Property or array item — trailing comma allowed in TypeScript + display: 'swap', +// Closing brace — end of block (function, if, object, JSX) +}); +// (blank line — separates logical blocks for readability) +// Line 16: const playfair = Playfair_Display({ +const playfair = Playfair_Display({ + // Property or array item — trailing comma allowed in TypeScript + subsets: ['latin'], + // Property or array item — trailing comma allowed in TypeScript + variable: '--font-playfair', + // Property or array item — trailing comma allowed in TypeScript + display: 'swap', +// Closing brace — end of block (function, if, object, JSX) +}); +// (blank line — separates logical blocks for readability) +// Line 22: const notoUrdu = Noto_Nastaliq_Urdu({ +const notoUrdu = Noto_Nastaliq_Urdu({ + // Property or array item — trailing comma allowed in TypeScript + subsets: ['arabic'], + // Property or array item — trailing comma allowed in TypeScript + variable: '--font-urdu', + // Property or array item — trailing comma allowed in TypeScript + weight: ['400', '500', '600', '700'], + // Property or array item — trailing comma allowed in TypeScript + display: 'swap', +// Closing brace — end of block (function, if, object, JSX) +}); +// (blank line — separates logical blocks for readability) +// Line 29: const siteDescription = +const siteDescription = + // Line 30: 'Premium 100% Halal meat delivery. Fresh and frozen chick... + 'Premium 100% Halal meat delivery. Fresh and frozen chicken, beef, lamb, and fish — customized to your preference and delivered to your door.'; +// (blank line — separates logical blocks for readability) +// Named export constant — shared config/data imported elsewhere +export const viewport = { + // Property or array item — trailing comma allowed in TypeScript + themeColor: '#8B1F1F', +// Closing brace — end of block (function, if, object, JSX) +}; +// (blank line — separates logical blocks for readability) +// Named export constant — shared config/data imported elsewhere +export const metadata: Metadata = { + // Line 37: title: { + title: { + // Switch default — fallback when no case matches + default: `${SITE_NAME} — Premium Halal Meat Delivery`, + // Property or array item — trailing comma allowed in TypeScript + template: `%s | ${SITE_NAME}`, + // Closing brace — end of block (function, if, object, JSX) + }, + // Property or array item — trailing comma allowed in TypeScript + description: siteDescription, + // Line 42: keywords: [ + keywords: [ + // Property or array item — trailing comma allowed in TypeScript + 'halal meat', + // Property or array item — trailing comma allowed in TypeScript + 'halal chicken', + // Property or array item — trailing comma allowed in TypeScript + 'halal beef', + // Property or array item — trailing comma allowed in TypeScript + 'halal lamb', + // Property or array item — trailing comma allowed in TypeScript + 'meat delivery', + // Property or array item — trailing comma allowed in TypeScript + 'fresh meat', + // Property or array item — trailing comma allowed in TypeScript + 'Kött Gård', + // Property or array item — trailing comma allowed in TypeScript + 'kottgard', + // End of array literal + ], + // Line 52: openGraph: { + openGraph: { + // Property or array item — trailing comma allowed in TypeScript + title: `${SITE_NAME} — Premium Halal Meat Delivery`, + // Property or array item — trailing comma allowed in TypeScript + description: siteDescription, + // Property or array item — trailing comma allowed in TypeScript + type: 'website', + // Property or array item — trailing comma allowed in TypeScript + locale: 'sv_SE', + // Property or array item — trailing comma allowed in TypeScript + siteName: SITE_NAME, + // Closing brace — end of block (function, if, object, JSX) + }, + // Line 59: robots: { + robots: { + // Property or array item — trailing comma allowed in TypeScript + index: true, + // Property or array item — trailing comma allowed in TypeScript + follow: true, + // Closing brace — end of block (function, if, object, JSX) + }, +// Closing brace — end of block (function, if, object, JSX) +}; +// (blank line — separates logical blocks for readability) +// Default export — main component/page Next.js or other files import +export default function RootLayout({ + // Property or array item — trailing comma allowed in TypeScript + children, +// Closing brace — end of block (function, if, object, JSX) +}: { + // Line 68: children: React.ReactNode; + children: React.ReactNode; +// Closing brace — end of block (function, if, object, JSX) +}) { + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser + + > + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser + + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
{children}
+ // JSX element — HTML-like tag becomes React component in browser +