Initial commit: Kottgard production website code

This commit is contained in:
2026-06-19 15:46:23 +00:00
commit e0273c46d7
196 changed files with 29799 additions and 0 deletions
+34
View File
@@ -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
+69
View File
@@ -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.
+365
View File
@@ -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/`
Binary file not shown.
+195
View File
@@ -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/`);
+63
View File
@@ -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`
@@ -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
<div>
// JSX element — HTML-like tag becomes React component in browser
<div className="bg-gradient-to-br from-brand-900 to-brand-950 px-4 py-20 text-white sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-3xl text-center">
// JSX element — HTML-like tag becomes React component in browser
<h1 className="mb-4 font-display text-4xl font-bold">
// 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
</h1>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-lg text-brand-100">{t('about.subtitle')}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-4xl px-4 py-16 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<section className="mb-16">
// JSX element — HTML-like tag becomes React component in browser
<h2 className="section-heading mb-4">{t('about.ourStory')}</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-4 leading-relaxed text-gray-600">
// 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
</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="leading-relaxed text-gray-600">{t('about.storyP2')}</p>
// JSX element — HTML-like tag becomes React component in browser
</section>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<section id="halal" className="mb-16">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-6 flex items-center gap-3">
// JSX element — HTML-like tag becomes React component in browser
<ShieldCheck className="h-8 w-8 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<h2 className="section-heading">{t('about.halalTitle')}</h2>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<p className="leading-relaxed text-gray-600">
// 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
</p>
// JSX element — HTML-like tag becomes React component in browser
</section>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<section className="mb-16">
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-6 sm:grid-cols-3">
// 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
<div key={item.title} className="card-premium p-6 text-center">
// JSX element — HTML-like tag becomes React component in browser
<item.icon className="mx-auto mb-3 h-8 w-8 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<h3 className="mb-1 font-semibold text-brand-900">{item.title}</h3>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm text-gray-500">{item.desc}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 67: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<section id="delivery" className="mb-16">
// JSX element — HTML-like tag becomes React component in browser
<h2 className="section-heading mb-4">{t('about.deliveryTitle')}</h2>
// JSX element — HTML-like tag becomes React component in browser
<div className="space-y-3 text-gray-600">
// JSX element — HTML-like tag becomes React component in browser
<p>{t('about.delivery1')}</p>
// JSX element — HTML-like tag becomes React component in browser
<p>{t('about.delivery2')}</p>
// JSX element — HTML-like tag becomes React component in browser
<p>{t('about.delivery3')}</p>
// JSX element — HTML-like tag becomes React component in browser
<p>{t('about.delivery4')}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<section id="contact" className="card-premium mb-16 p-8">
// JSX element — HTML-like tag becomes React component in browser
<h2 className="section-heading mb-6">{t('about.contactTitle')}</h2>
// JSX element — HTML-like tag becomes React component in browser
<div className="space-y-4">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center gap-3 text-gray-600">
// JSX element — HTML-like tag becomes React component in browser
<Phone className="h-5 w-5 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<a href={telHref} className="hover:text-brand-800">
// Line 87: {SITE_PHONE_DISPLAY}
{SITE_PHONE_DISPLAY}
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center gap-3 text-gray-600">
// JSX element — HTML-like tag becomes React component in browser
<Mail className="h-5 w-5 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<a href={`mailto:${SITE_EMAIL}`} className="hover:text-brand-800">
// Line 93: {SITE_EMAIL}
{SITE_EMAIL}
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-start gap-3 text-gray-600">
// JSX element — HTML-like tag becomes React component in browser
<MapPin className="mt-0.5 h-5 w-5 shrink-0 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<span>
// Line 99: {SITE_ADDRESS}
{SITE_ADDRESS}
// JSX element — HTML-like tag becomes React component in browser
<br />
// Line 101: {t('footer.hours', { hours: SITE_HOURS })}
{t('footer.hours', { hours: SITE_HOURS })}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="mt-6">
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href="/shop"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="text-sm font-semibold text-brand-700 hover:text-brand-900"
// Line 109: >
>
// Line 110: {t('cta.button')} →
{t('cta.button')}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<section id="privacy" className="mb-16">
// JSX element — HTML-like tag becomes React component in browser
<h2 className="section-heading mb-4">{t('about.privacyTitle')}</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="leading-relaxed text-gray-600">{t('about.privacyText')}</p>
// JSX element — HTML-like tag becomes React component in browser
</section>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<section id="terms" className="mb-8">
// JSX element — HTML-like tag becomes React component in browser
<h2 className="section-heading mb-4">{t('about.termsTitle')}</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="leading-relaxed text-gray-600">{t('about.termsText')}</p>
// JSX element — HTML-like tag becomes React component in browser
</section>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 126: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -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
<div className="bg-gray-50">
// JSX element — HTML-like tag becomes React component in browser
<div className="border-b border-gray-100 bg-white">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<h1 className="section-heading">{t('account.title')}</h1>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-gray-500">{t('account.welcome', { name: user.name })}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 py-10 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-8 lg:grid-cols-3">
// JSX element — HTML-like tag becomes React component in browser
<div className="space-y-6">
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium p-6">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-4 flex items-center gap-3">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-brand-100">
// JSX element — HTML-like tag becomes React component in browser
<User className="h-6 w-6 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<h2 className="font-display text-lg font-semibold">{user.name}</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm text-gray-500">
// 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
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="space-y-3 border-t border-gray-100 pt-4">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center gap-2 text-sm text-gray-600">
// JSX element — HTML-like tag becomes React component in browser
<Mail className="h-4 w-4 text-brand-600" />
// Line 63: {user.email}
{user.email}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center gap-2 text-sm text-gray-600">
// JSX element — HTML-like tag becomes React component in browser
<Phone className="h-4 w-4 text-brand-600" />
// Line 67: {user.phone}
{user.phone}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-start gap-2 text-sm text-gray-600">
// JSX element — HTML-like tag becomes React component in browser
<MapPin className="mt-0.5 h-4 w-4 shrink-0 text-brand-600" />
// 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
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium p-4">
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href="/wishlist"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-brand-50 hover:text-brand-700"
// Line 81: >
>
// JSX element — HTML-like tag becomes React component in browser
<Heart className="h-4 w-4" />
// Line 83: {t('account.myWishlist')}
{t('account.myWishlist')}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
<button
// Click handler — runs when user clicks (must be client component)
onClick={handleLogout}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium text-red-500 transition-colors hover:bg-red-50"
// Line 88: >
>
// JSX element — HTML-like tag becomes React component in browser
<LogOut className="h-4 w-4" />
// Line 90: {t('account.signOut')}
{t('account.signOut')}
// JSX element — HTML-like tag becomes React component in browser
</button>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="lg:col-span-2" id="orders">
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium p-6">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-6 flex items-center gap-2">
// JSX element — HTML-like tag becomes React component in browser
<Package className="h-5 w-5 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<h2 className="font-display text-lg font-semibold">
// Line 100: {t('account.orderHistory')}
{t('account.orderHistory')}
// JSX element — HTML-like tag becomes React component in browser
</h2>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (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
<div className="py-12 text-center">
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-2 text-gray-500">{t('account.noOrders')}</p>
// JSX element — HTML-like tag becomes React component in browser
<Link href="/shop">
// JSX element — HTML-like tag becomes React component in browser
<Button variant="primary">{t('account.startShopping')}</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 111: ) : (
) : (
// JSX element — HTML-like tag becomes React component in browser
<div className="space-y-4">
// Array.map — transform each item (often render a list of components)
{orders.map((order) => (
// JSX element — HTML-like tag becomes React component in browser
<div
// Line 115: key={order.id}
key={order.id}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="rounded-xl border border-gray-100 p-4 transition-colors hover:border-brand-200"
// Line 117: >
>
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-3 flex items-center justify-between">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm font-semibold text-brand-900">{order.id}</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs text-gray-400">
// Line 122: {formatDate(order.createdAt)}
{formatDate(order.createdAt)}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="text-end">
// JSX element — HTML-like tag becomes React component in browser
<p className="font-bold text-brand-800">{fmt(order.total)}</p>
// JSX element — HTML-like tag becomes React component in browser
<span className="inline-block rounded-full bg-brand-50 px-2 py-0.5 text-xs font-medium capitalize text-brand-700">
// Line 128: {t(`orderStatus.${order.status}`)}
{t(`orderStatus.${order.status}`)}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="space-y-1">
// 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
<div
// Line 137: key={item.id}
key={item.id}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="flex justify-between text-sm text-gray-600"
// Line 139: >
>
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href={`/product/${item.product.slug}`}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="hover:text-brand-700"
// Line 143: >
>
// Line 144: {localized.name} × {item.quantity}
{localized.name} × {item.quantity}
// JSX element — HTML-like tag becomes React component in browser
<span className="ms-2 text-xs text-gold-600">
// Line 146: ({item.customizationLabel})
({item.customizationLabel})
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
<span>{fmt(item.product.price * item.quantity)}</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 151: );
);
// Closing brace — end of block (function, if, object, JSX)
})}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 155: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 157: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 163: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -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
<div className="mx-auto max-w-7xl px-4 py-20 text-center sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-brand-50">
// JSX element — HTML-like tag becomes React component in browser
<ShoppingBag className="h-10 w-10 text-brand-300" />
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<h1 className="mb-2 font-display text-2xl font-bold text-brand-900">
// Line 27: {t('cart.empty')}
{t('cart.empty')}
// JSX element — HTML-like tag becomes React component in browser
</h1>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-8 text-gray-500">{t('cart.emptyHint')}</p>
// JSX element — HTML-like tag becomes React component in browser
<Link href="/shop">
// JSX element — HTML-like tag becomes React component in browser
<Button variant="primary" size="lg">
// Line 32: {t('cart.startShopping')}
{t('cart.startShopping')}
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// 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
<div className="bg-gray-50">
// JSX element — HTML-like tag becomes React component in browser
<div className="border-b border-gray-100 bg-white">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<h1 className="section-heading">{t('cart.title')}</h1>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-gray-500">
// Line 45: {t('cart.itemsCount', { count: items.length })}
{t('cart.itemsCount', { count: items.length })}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 py-10 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-8 lg:grid-cols-3">
// JSX element — HTML-like tag becomes React component in browser
<div className="space-y-4 lg:col-span-2">
// 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
<div key={item.id} className="card-premium flex gap-4 p-4 sm:p-6">
// JSX element — HTML-like tag becomes React component in browser
<div className="relative h-24 w-24 shrink-0 overflow-hidden rounded-xl sm:h-28 sm:w-28">
// JSX element — HTML-like tag becomes React component in browser
<AppImage
// Line 59: src={item.product.image}
src={item.product.image}
// Line 60: alt={localized.name}
alt={localized.name}
// Line 61: fill
fill
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="object-cover"
// Line 63: sizes="(max-width: 640px) 96px, 112px"
sizes="(max-width: 640px) 96px, 112px"
// Line 64: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-1 flex-col">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-start justify-between">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href={`/product/${item.product.slug}`}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="font-display text-lg font-semibold text-brand-900 hover:text-brand-700"
// Line 73: >
>
// Line 74: {localized.name}
{localized.name}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
<p className="mt-1 text-xs uppercase tracking-wider text-brand-600">
// Line 77: {t(`categories.${item.product.category}.name`)}
{t(`categories.${item.product.category}.name`)}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<button
// Click handler — runs when user clicks (must be client component)
onClick={() => removeItem(item.id)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="rounded-lg p-2 text-gray-400 transition-colors hover:bg-red-50 hover:text-red-500"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('cart.removeItem')}
// Line 84: >
>
// JSX element — HTML-like tag becomes React component in browser
<Trash2 className="h-4 w-4" />
// JSX element — HTML-like tag becomes React component in browser
</button>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mt-2 inline-flex items-center gap-1.5 rounded-full bg-gold-50 px-3 py-1">
// JSX element — HTML-like tag becomes React component in browser
<span className="text-xs font-medium text-gold-700">
// Line 91: {t('cart.customization')}
{t('cart.customization')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<span className="text-xs font-semibold text-gold-800">
// Line 94: {item.customizationLabel}
{item.customizationLabel}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mt-auto flex items-center justify-between pt-3">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center rounded-lg border border-gray-200">
// JSX element — HTML-like tag becomes React component in browser
<button
// Click handler — runs when user clicks (must be client component)
onClick={() => updateQuantity(item.id, item.quantity - 1)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="flex h-9 w-9 items-center justify-center text-gray-500 hover:text-brand-700"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('product.decreaseQty')}
// Line 104: >
>
// JSX element — HTML-like tag becomes React component in browser
<Minus className="h-3 w-3" />
// JSX element — HTML-like tag becomes React component in browser
</button>
// JSX element — HTML-like tag becomes React component in browser
<span className="w-8 text-center text-sm font-semibold">
// Line 108: {item.quantity}
{item.quantity}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<button
// Click handler — runs when user clicks (must be client component)
onClick={() => updateQuantity(item.id, item.quantity + 1)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="flex h-9 w-9 items-center justify-center text-gray-500 hover:text-brand-700"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('product.increaseQty')}
// Line 114: >
>
// JSX element — HTML-like tag becomes React component in browser
<Plus className="h-3 w-3" />
// JSX element — HTML-like tag becomes React component in browser
</button>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<span className="text-lg font-bold text-brand-800">
// Line 119: {fmt(item.product.price * item.quantity)}
{fmt(item.product.price * item.quantity)}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 124: );
);
// Closing brace — end of block (function, if, object, JSX)
})}
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium sticky top-24 p-6">
// JSX element — HTML-like tag becomes React component in browser
<h2 className="mb-4 font-display text-lg font-semibold text-brand-900">
// Line 131: {t('cart.orderSummary')}
{t('cart.orderSummary')}
// JSX element — HTML-like tag becomes React component in browser
</h2>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="space-y-3 border-b border-gray-100 pb-4">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex justify-between text-sm">
// JSX element — HTML-like tag becomes React component in browser
<span className="text-gray-500">{t('cart.subtotal')}</span>
// JSX element — HTML-like tag becomes React component in browser
<span className="font-medium">{fmt(total)}</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex justify-between text-sm">
// JSX element — HTML-like tag becomes React component in browser
<span className="text-gray-500">{t('cart.delivery')}</span>
// JSX element — HTML-like tag becomes React component in browser
<span className="font-medium">
// Line 142: {deliveryFee === 0 ? (
{deliveryFee === 0 ? (
// JSX element — HTML-like tag becomes React component in browser
<span className="text-brand-700">{t('cart.free')}</span>
// Line 144: ) : (
) : (
// Line 145: fmt(deliveryFee)
fmt(deliveryFee)
// Line 146: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 149: {deliveryFee > 0 && (
{deliveryFee > 0 && (
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs text-gray-400">{t('cart.freeDeliveryHint')}</p>
// Line 151: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="flex justify-between py-4">
// JSX element — HTML-like tag becomes React component in browser
<span className="font-semibold text-brand-900">{t('cart.total')}</span>
// JSX element — HTML-like tag becomes React component in browser
<span className="text-xl font-bold text-brand-800">{fmt(grandTotal)}</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Link href="/checkout">
// JSX element — HTML-like tag becomes React component in browser
<Button variant="gold" size="lg" className="w-full">
// Line 161: {t('cart.proceedCheckout')}
{t('cart.proceedCheckout')}
// JSX element — HTML-like tag becomes React component in browser
<ArrowRight className="h-4 w-4 rtl:rotate-180" />
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href="/shop"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="mt-3 block text-center text-sm font-medium text-brand-600 hover:text-brand-800"
// Line 169: >
>
// Line 170: {t('cart.continueShopping')}
{t('cart.continueShopping')}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 177: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -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
<div className="mx-auto max-w-7xl px-4 py-20 text-center">
// JSX element — HTML-like tag becomes React component in browser
<h1 className="mb-4 font-display text-2xl font-bold">{t('checkout.noItems')}</h1>
// JSX element — HTML-like tag becomes React component in browser
<Link href="/shop">
// JSX element — HTML-like tag becomes React component in browser
<Button>{t('checkout.goToShop')}</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// 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
<div className="mx-auto max-w-lg px-4 py-20 text-center">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-brand-50">
// JSX element — HTML-like tag becomes React component in browser
<CheckCircle className="h-12 w-12 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<h1 className="mb-2 font-display text-3xl font-bold text-brand-900">
// Line 63: {t('checkout.orderConfirmed')}
{t('checkout.orderConfirmed')}
// JSX element — HTML-like tag becomes React component in browser
</h1>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-2 text-gray-500">{t('checkout.thankYou')}</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-8 text-sm font-medium text-brand-700">
// Line 67: {t('checkout.orderId', { id: orderId })}
{t('checkout.orderId', { id: orderId })}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-col gap-3 sm:flex-row sm:justify-center">
// JSX element — HTML-like tag becomes React component in browser
<Link href="/account">
// JSX element — HTML-like tag becomes React component in browser
<Button variant="primary">{t('checkout.viewOrders')}</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
<Link href="/shop">
// JSX element — HTML-like tag becomes React component in browser
<Button variant="secondary">{t('cart.continueShopping')}</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// 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
<div className="bg-gray-50">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href="/cart"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="mb-6 inline-flex items-center gap-1 text-sm font-medium text-gray-500 hover:text-brand-700"
// Line 120: >
>
// JSX element — HTML-like tag becomes React component in browser
<ChevronLeft className="h-4 w-4 rtl:rotate-180" />
// Line 122: {t('checkout.backToCart')}
{t('checkout.backToCart')}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<h1 className="section-heading mb-8">{t('checkout.title')}</h1>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<form onSubmit={handleSubmit}>
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-8 lg:grid-cols-3">
// JSX element — HTML-like tag becomes React component in browser
<div className="space-y-6 lg:col-span-2">
// Line 130: {!isAuthenticated && (
{!isAuthenticated && (
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium p-6">
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm text-gray-600">
// Line 133: {t('checkout.haveAccount')}{' '}
{t('checkout.haveAccount')}{' '}
// JSX element — HTML-like tag becomes React component in browser
<Link href="/login" className="font-semibold text-brand-700 hover:underline">
// Line 135: {t('checkout.signIn')}
{t('checkout.signIn')}
// JSX element — HTML-like tag becomes React component in browser
</Link>{' '}
// Line 137: {t('checkout.fasterCheckout')}
{t('checkout.fasterCheckout')}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 140: )}
)}
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium p-6">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-4 flex items-center gap-2">
// JSX element — HTML-like tag becomes React component in browser
<Truck className="h-5 w-5 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<h2 className="font-display text-lg font-semibold">
// Line 146: {t('checkout.deliveryDetails')}
{t('checkout.deliveryDetails')}
// JSX element — HTML-like tag becomes React component in browser
</h2>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-4 sm:grid-cols-2">
// JSX element — HTML-like tag becomes React component in browser
<div className="sm:col-span-2">
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('checkout.fullName')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 153: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 155: value={form.name}
value={form.name}
// Change handler — runs when input/select value changes
onChange={(e) => updateField('name', e.target.value)}
// Line 157: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('checkout.email')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 162: required
required
// Line 163: type="email"
type="email"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 165: value={form.email}
value={form.email}
// Change handler — runs when input/select value changes
onChange={(e) => updateField('email', e.target.value)}
// Line 167: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('checkout.phone')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 172: required
required
// Line 173: type="tel"
type="tel"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 175: value={form.phone}
value={form.phone}
// Change handler — runs when input/select value changes
onChange={(e) => updateField('phone', e.target.value)}
// Line 177: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="sm:col-span-2">
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('checkout.street')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 182: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 184: value={form.street}
value={form.street}
// Change handler — runs when input/select value changes
onChange={(e) => updateField('street', e.target.value)}
// Line 186: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('checkout.city')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 191: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 193: value={form.city}
value={form.city}
// Change handler — runs when input/select value changes
onChange={(e) => updateField('city', e.target.value)}
// Line 195: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('checkout.state')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 200: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 202: value={form.state}
value={form.state}
// Change handler — runs when input/select value changes
onChange={(e) => updateField('state', e.target.value)}
// Line 204: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('checkout.zip')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 209: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 211: value={form.zip}
value={form.zip}
// Change handler — runs when input/select value changes
onChange={(e) => updateField('zip', e.target.value)}
// Line 213: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium p-6">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-4 flex items-center gap-2">
// JSX element — HTML-like tag becomes React component in browser
<CreditCard className="h-5 w-5 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<h2 className="font-display text-lg font-semibold">{t('checkout.payment')}</h2>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-4 flex gap-3">
// 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
<button
// Line 227: key={method}
key={method}
// Line 228: type="button"
type="button"
// Click handler — runs when user clicks (must be client component)
onClick={() => setPaymentMethod(method)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={`rounded-lg border-2 px-4 py-2 text-sm font-medium transition-all ${
// Line 231: paymentMethod === method
paymentMethod === method
// Line 232: ? 'border-brand-700 bg-brand-700 text-white'
? 'border-brand-700 bg-brand-700 text-white'
// Line 233: : 'border-gray-200 text-gray-600 hover:border-brand-300'
: 'border-gray-200 text-gray-600 hover:border-brand-300'
// Closing brace — end of block (function, if, object, JSX)
}`}
// Line 235: >
>
// Line 236: {method === 'card'
{method === 'card'
// Line 237: ? t('checkout.creditCard')
? t('checkout.creditCard')
// Line 238: : t('checkout.cashOnDelivery')}
: t('checkout.cashOnDelivery')}
// JSX element — HTML-like tag becomes React component in browser
</button>
// Line 240: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// Line 243: {paymentMethod === 'card' && (
{paymentMethod === 'card' && (
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-4 sm:grid-cols-2">
// JSX element — HTML-like tag becomes React component in browser
<div className="sm:col-span-2">
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('checkout.cardNumber')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 248: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 250: placeholder="1234 5678 9012 3456"
placeholder="1234 5678 9012 3456"
// Line 251: value={form.cardNumber}
value={form.cardNumber}
// Change handler — runs when input/select value changes
onChange={(e) => updateField('cardNumber', e.target.value)}
// Line 253: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('checkout.expiry')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 258: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 260: placeholder="MM/YY"
placeholder="MM/YY"
// Line 261: value={form.expiry}
value={form.expiry}
// Change handler — runs when input/select value changes
onChange={(e) => updateField('expiry', e.target.value)}
// Line 263: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('checkout.cvv')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 268: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 270: placeholder="123"
placeholder="123"
// Line 271: value={form.cvv}
value={form.cvv}
// Change handler — runs when input/select value changes
onChange={(e) => updateField('cvv', e.target.value)}
// Line 273: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 276: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium sticky top-24 p-6">
// JSX element — HTML-like tag becomes React component in browser
<h2 className="mb-4 font-display text-lg font-semibold">
// Line 283: {t('cart.orderSummary')}
{t('cart.orderSummary')}
// JSX element — HTML-like tag becomes React component in browser
</h2>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-4 max-h-60 space-y-3 overflow-y-auto">
// 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
<div key={item.id} className="flex gap-3">
// JSX element — HTML-like tag becomes React component in browser
<div className="relative h-12 w-12 shrink-0 overflow-hidden rounded-lg">
// JSX element — HTML-like tag becomes React component in browser
<AppImage
// Line 293: src={item.product.image}
src={item.product.image}
// Line 294: alt={localized.name}
alt={localized.name}
// Line 295: fill
fill
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="object-cover"
// Line 297: sizes="48px"
sizes="48px"
// Line 298: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex-1">
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm font-medium text-brand-900">
// Line 302: {localized.name}
{localized.name}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs text-gold-700">
// Line 305: {item.customizationLabel}
{item.customizationLabel}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs text-gray-400">
// Line 308: {t('checkout.qty', { count: item.quantity })}
{t('checkout.qty', { count: item.quantity })}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<span className="text-sm font-medium">
// Line 312: {fmt(item.product.price * item.quantity)}
{fmt(item.product.price * item.quantity)}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 315: );
);
// Closing brace — end of block (function, if, object, JSX)
})}
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="space-y-2 border-t border-gray-100 pt-4">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex justify-between text-sm">
// JSX element — HTML-like tag becomes React component in browser
<span className="text-gray-500">{t('cart.subtotal')}</span>
// JSX element — HTML-like tag becomes React component in browser
<span>{fmt(total)}</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex justify-between text-sm">
// JSX element — HTML-like tag becomes React component in browser
<span className="text-gray-500">{t('cart.delivery')}</span>
// JSX element — HTML-like tag becomes React component in browser
<span>
// 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
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex justify-between pt-2 text-lg font-bold">
// JSX element — HTML-like tag becomes React component in browser
<span>{t('cart.total')}</span>
// JSX element — HTML-like tag becomes React component in browser
<span className="text-brand-800">{fmt(grandTotal)}</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Button
// Line 337: type="submit"
type="submit"
// Line 338: variant="gold"
variant="gold"
// Line 339: size="lg"
size="lg"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="mt-6 w-full"
// Line 341: disabled={isProcessing}
disabled={isProcessing}
// Line 342: >
>
// JSX element — HTML-like tag becomes React component in browser
<Lock className="h-4 w-4" />
// Line 344: {isProcessing
{isProcessing
// Line 345: ? t('checkout.processing')
? t('checkout.processing')
// Line 346: : t('checkout.pay', { amount: fmt(grandTotal) })}
: t('checkout.pay', { amount: fmt(grandTotal) })}
// JSX element — HTML-like tag becomes React component in browser
</Button>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<p className="mt-3 flex items-center justify-center gap-1 text-xs text-gray-400">
// JSX element — HTML-like tag becomes React component in browser
<Lock className="h-3 w-3" />
// Line 351: {t('checkout.secure')}
{t('checkout.secure')}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</form>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 359: );
);
// Closing brace — end of block (function, if, object, JSX)
}
+174
View File
@@ -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
<html
// Line 72: lang="sv"
lang="sv"
// Line 73: dir="ltr"
dir="ltr"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={`${inter.variable} ${playfair.variable} ${notoUrdu.variable}`}
// Line 75: suppressHydrationWarning
suppressHydrationWarning
// Line 76: >
>
// JSX element — HTML-like tag becomes React component in browser
<body className="flex min-h-screen flex-col font-sans">
// JSX element — HTML-like tag becomes React component in browser
<LocaleAttributes />
// JSX element — HTML-like tag becomes React component in browser
<div className="sticky top-0 z-50">
// JSX element — HTML-like tag becomes React component in browser
<LanguageBanner />
// JSX element — HTML-like tag becomes React component in browser
<Header />
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<main className="flex-1">{children}</main>
// JSX element — HTML-like tag becomes React component in browser
<Footer />
// JSX element — HTML-like tag becomes React component in browser
</body>
// JSX element — HTML-like tag becomes React component in browser
</html>
// Line 87: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,384 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/app/login/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 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';
// 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 { useTranslation } from '@/hooks/useTranslation';
// Import project module (@/ alias = src/ folder in tsconfig)
import { DEMO_EMAIL } from '@/lib/constants';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function LoginPage() {
// Next.js router — programmatic navigation (router.push)
const router = useRouter();
// Zustand selector — subscribe to slice of global store
const { login, register } = useAuthStore();
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// React useState — local component state that triggers re-render on change
const [isRegister, setIsRegister] = useState(false);
// React useState — local component state that triggers re-render on change
const [error, setError] = useState('');
// 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: '',
// Property or array item — trailing comma allowed in TypeScript
email: '',
// Property or array item — trailing comma allowed in TypeScript
password: '',
// Property or array item — trailing comma allowed in TypeScript
phone: '',
// Property or array item — trailing comma allowed in TypeScript
street: '',
// Property or array item — trailing comma allowed in TypeScript
city: '',
// Property or array item — trailing comma allowed in TypeScript
state: '',
// Property or array item — trailing comma allowed in TypeScript
zip: '',
// Closing brace — end of block (function, if, object, JSX)
});
// (blank line — separates logical blocks for readability)
// Line 28: const handleSubmit = (e: React.FormEvent) => {
const handleSubmit = (e: React.FormEvent) => {
// Line 29: e.preventDefault();
e.preventDefault();
// Line 30: setError('');
setError('');
// (blank line — separates logical blocks for readability)
// Conditional branch — different behavior based on runtime value
if (isRegister) {
// Line 33: const success = register({
const success = register({
// Property or array item — trailing comma allowed in TypeScript
name: form.name,
// Property or array item — trailing comma allowed in TypeScript
email: form.email,
// Property or array item — trailing comma allowed in TypeScript
password: form.password,
// Property or array item — trailing comma allowed in TypeScript
phone: form.phone,
// Line 38: address: {
address: {
// 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)
},
// Closing brace — end of block (function, if, object, JSX)
});
// Conditional branch — different behavior based on runtime value
if (success) router.push('/account');
// Closing brace — end of block (function, if, object, JSX)
} else {
// Line 47: const success = login(form.email, form.password);
const success = login(form.email, form.password);
// Conditional branch — different behavior based on runtime value
if (success) {
// Line 49: router.push('/account');
router.push('/account');
// Closing brace — end of block (function, if, object, JSX)
} else {
// Line 51: setError(t('auth.invalidCredentials', { email: DEMO_EMAIL...
setError(t('auth.invalidCredentials', { email: DEMO_EMAIL }));
// Closing brace — end of block (function, if, object, JSX)
}
// 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)
// Return JSX — describes UI tree React renders to the DOM
return (
// JSX element — HTML-like tag becomes React component in browser
<div className="flex min-h-[70vh] items-center justify-center bg-gray-50 px-4 py-12">
// JSX element — HTML-like tag becomes React component in browser
<div className="w-full max-w-md">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-8 text-center">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-brand-700">
// JSX element — HTML-like tag becomes React component in browser
<span className="font-display text-xl font-bold text-gold-400">
// Line 62: {t('site.initials')}
{t('site.initials')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<h1 className="font-display text-2xl font-bold text-brand-900">
// Line 66: {isRegister ? t('auth.createAccount') : t('auth.welcomeBa...
{isRegister ? t('auth.createAccount') : t('auth.welcomeBack')}
// JSX element — HTML-like tag becomes React component in browser
</h1>
// JSX element — HTML-like tag becomes React component in browser
<p className="mt-1 text-sm text-gray-500">
// Line 69: {isRegister
{isRegister
// Line 70: ? t('auth.joinTagline', { name: t('site.name') })
? t('auth.joinTagline', { name: t('site.name') })
// Line 71: : t('auth.signInTagline', { name: t('site.name') })}
: t('auth.signInTagline', { name: t('site.name') })}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium p-8">
// JSX element — HTML-like tag becomes React component in browser
<form onSubmit={handleSubmit} className="space-y-4">
// Line 77: {isRegister && (
{isRegister && (
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('auth.fullName')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 81: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 83: value={form.name}
value={form.name}
// Change handler — runs when input/select value changes
onChange={(e) => setForm({ ...form, name: e.target.value })}
// Line 85: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 87: )}
)}
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('auth.email')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 92: required
required
// Line 93: type="email"
type="email"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 95: value={form.email}
value={form.email}
// Change handler — runs when input/select value changes
onChange={(e) => setForm({ ...form, email: e.target.value })}
// Line 97: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('auth.password')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 103: required
required
// Line 104: type="password"
type="password"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 106: value={form.password}
value={form.password}
// Change handler — runs when input/select value changes
onChange={(e) => setForm({ ...form, password: e.target.value })}
// Line 108: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// Line 111: {isRegister && (
{isRegister && (
// React Fragment — group elements without extra wrapper DOM node
<>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('auth.phone')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 116: required
required
// Line 117: type="tel"
type="tel"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 119: value={form.phone}
value={form.phone}
// Change handler — runs when input/select value changes
onChange={(e) => setForm({ ...form, phone: e.target.value })}
// Line 121: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('auth.street')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 126: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 128: value={form.street}
value={form.street}
// Change handler — runs when input/select value changes
onChange={(e) => setForm({ ...form, street: e.target.value })}
// Line 130: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="grid grid-cols-2 gap-3">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('auth.city')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 136: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 138: value={form.city}
value={form.city}
// Change handler — runs when input/select value changes
onChange={(e) => setForm({ ...form, city: e.target.value })}
// Line 140: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('auth.state')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 145: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 147: value={form.state}
value={form.state}
// Change handler — runs when input/select value changes
onChange={(e) => setForm({ ...form, state: e.target.value })}
// Line 149: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label className="label-text">{t('auth.zip')}</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 155: required
required
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 157: value={form.zip}
value={form.zip}
// Change handler — runs when input/select value changes
onChange={(e) => setForm({ ...form, zip: e.target.value })}
// Line 159: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</>
// Line 162: )}
)}
// (blank line — separates logical blocks for readability)
// Line 164: {error && (
{error && (
// JSX element — HTML-like tag becomes React component in browser
<p className="rounded-lg bg-red-50 px-4 py-2 text-sm text-red-600">{error}</p>
// Line 166: )}
)}
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Button type="submit" variant="primary" size="lg" className="w-full">
// Line 169: {isRegister ? t('auth.register') : t('auth.signIn')}
{isRegister ? t('auth.register') : t('auth.signIn')}
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</form>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mt-6 text-center">
// JSX element — HTML-like tag becomes React component in browser
<button
// Click handler — runs when user clicks (must be client component)
onClick={() => {
// Line 176: setIsRegister(!isRegister);
setIsRegister(!isRegister);
// Line 177: setError('');
setError('');
// Closing brace — end of block (function, if, object, JSX)
}}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="text-sm font-medium text-brand-600 hover:text-brand-800"
// Line 180: >
>
// Line 181: {isRegister ? t('auth.hasAccount') : t('auth.noAccount')}
{isRegister ? t('auth.hasAccount') : t('auth.noAccount')}
// JSX element — HTML-like tag becomes React component in browser
</button>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// Line 185: {!isRegister && (
{!isRegister && (
// JSX element — HTML-like tag becomes React component in browser
<div className="mt-4 rounded-lg bg-brand-50 px-4 py-3 text-center">
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs text-brand-700">
// Line 188: {t('auth.demo', { email: DEMO_EMAIL })}
{t('auth.demo', { email: DEMO_EMAIL })}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 191: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 195: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,44 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/app/not-found.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';
// Import project module (@/ alias = src/ folder in tsconfig)
import Button from '@/components/ui/Button';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function NotFound() {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (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
<div className="flex min-h-[60vh] flex-col items-center justify-center px-4 text-center">
// JSX element — HTML-like tag becomes React component in browser
<h1 className="mb-2 font-display text-6xl font-bold text-brand-900">
// Line 13: {t('notFound.title')}
{t('notFound.title')}
// JSX element — HTML-like tag becomes React component in browser
</h1>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-6 text-lg text-gray-500">{t('notFound.message')}</p>
// JSX element — HTML-like tag becomes React component in browser
<Link href="/">
// JSX element — HTML-like tag becomes React component in browser
<Button>{t('notFound.goHome')}</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 20: );
);
// Closing brace — end of block (function, if, object, JSX)
}
+58
View File
@@ -0,0 +1,58 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/app/page.tsx
* NOT used by the app — read this to learn how the real file works
*/
// Import project module (@/ alias = src/ folder in tsconfig)
import Hero from '@/components/home/Hero';
// Import project module (@/ alias = src/ folder in tsconfig)
import TrustBadges from '@/components/home/TrustBadges';
// Import project module (@/ alias = src/ folder in tsconfig)
import AboutPreview from '@/components/home/AboutPreview';
// Import project module (@/ alias = src/ folder in tsconfig)
import CategoryGrid from '@/components/home/CategoryGrid';
// Import project module (@/ alias = src/ folder in tsconfig)
import FeaturedProducts from '@/components/home/FeaturedProducts';
// Import project module (@/ alias = src/ folder in tsconfig)
import HowItWorks from '@/components/home/HowItWorks';
// Import project module (@/ alias = src/ folder in tsconfig)
import WeeklyOffers from '@/components/home/WeeklyOffers';
// Import project module (@/ alias = src/ folder in tsconfig)
import SocialFollow from '@/components/home/SocialFollow';
// Import project module (@/ alias = src/ folder in tsconfig)
import ContactPreview from '@/components/home/ContactPreview';
// Import project module (@/ alias = src/ folder in tsconfig)
import CTA from '@/components/home/CTA';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function HomePage() {
// Return JSX — describes UI tree React renders to the DOM
return (
// React Fragment — group elements without extra wrapper DOM node
<>
// JSX element — HTML-like tag becomes React component in browser
<Hero />
// JSX element — HTML-like tag becomes React component in browser
<TrustBadges />
// JSX element — HTML-like tag becomes React component in browser
<AboutPreview />
// JSX element — HTML-like tag becomes React component in browser
<CategoryGrid />
// JSX element — HTML-like tag becomes React component in browser
<FeaturedProducts />
// JSX element — HTML-like tag becomes React component in browser
<HowItWorks />
// JSX element — HTML-like tag becomes React component in browser
<WeeklyOffers />
// JSX element — HTML-like tag becomes React component in browser
<CTA />
// JSX element — HTML-like tag becomes React component in browser
<SocialFollow />
// JSX element — HTML-like tag becomes React component in browser
<ContactPreview />
// JSX element — HTML-like tag becomes React component in browser
</>
// Line 26: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,434 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/app/product/[slug]/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';
// 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';
// Next.js App Router hooks — useRouter, useParams, useSearchParams
import { notFound, useParams } from 'next/navigation';
// Import external package or local module
import {
// Property or array item — trailing comma allowed in TypeScript
ShoppingCart,
// Property or array item — trailing comma allowed in TypeScript
Heart,
// Property or array item — trailing comma allowed in TypeScript
ChevronLeft,
// Property or array item — trailing comma allowed in TypeScript
Minus,
// Property or array item — trailing comma allowed in TypeScript
Plus,
// Property or array item — trailing comma allowed in TypeScript
Check,
// Property or array item — trailing comma allowed in TypeScript
ShieldCheck,
// Closing brace — end of block (function, if, object, JSX)
} from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import CustomizationSelector from '@/components/product/CustomizationSelector';
// Import project module (@/ alias = src/ folder in tsconfig)
import Button from '@/components/ui/Button';
// Import project module (@/ alias = src/ folder in tsconfig)
import { getProductBySlug } from '@/lib/products';
// Import project module (@/ alias = src/ folder in tsconfig)
import { getDefaultCustomization, getCustomizationLabel } from '@/lib/customization';
// Import project module (@/ alias = src/ folder in tsconfig)
import { localizeProduct } from '@/lib/product-i18n';
// Import project module (@/ alias = src/ folder in tsconfig)
import { formatPrice, getFormatLocale } from '@/lib/utils';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useCartStore } from '@/store/cart';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useWishlistStore } from '@/store/wishlist';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// Import project module (@/ alias = src/ folder in tsconfig)
import { ProductCustomization } from '@/types';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function ProductPage() {
// Read dynamic route segment ([slug] from URL)
const params = useParams();
// Line 29: const slug = params.slug as string;
const slug = params.slug as string;
// Line 30: const rawProduct = getProductBySlug(slug);
const rawProduct = getProductBySlug(slug);
// 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 [customization, setCustomization] = useState<ProductCustomization>(
// Line 34: rawProduct ? getDefaultCustomization(rawProduct.category)...
rawProduct ? getDefaultCustomization(rawProduct.category) : { type: 'fish' }
// Line 35: );
);
// React useState — local component state that triggers re-render on change
const [quantity, setQuantity] = useState(1);
// React useState — local component state that triggers re-render on change
const [selectedImage, setSelectedImage] = useState(0);
// React useState — local component state that triggers re-render on change
const [added, setAdded] = useState(false);
// (blank line — separates logical blocks for readability)
// Zustand selector — subscribe to slice of global store
const addItem = useCartStore((s) => s.addItem);
// Zustand selector — subscribe to slice of global store
const { isInWishlist, toggleItem } = useWishlistStore();
// (blank line — separates logical blocks for readability)
// Conditional branch — different behavior based on runtime value
if (!rawProduct) {
// Line 44: notFound();
notFound();
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Line 47: const product = localizeProduct(rawProduct, t);
const product = localizeProduct(rawProduct, t);
// Line 48: const inWishlist = isInWishlist(product.id);
const inWishlist = isInWishlist(product.id);
// (blank line — separates logical blocks for readability)
// Line 50: const handleAddToCart = () => {
const handleAddToCart = () => {
// Line 51: const label = getCustomizationLabel(customization, t);
const label = getCustomizationLabel(customization, t);
// Line 52: addItem(rawProduct, customization, label, quantity);
addItem(rawProduct, customization, label, quantity);
// Line 53: setAdded(true);
setAdded(true);
// Line 54: setTimeout(() => setAdded(false), 2000);
setTimeout(() => setAdded(false), 2000);
// 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
<div className="bg-white">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href="/shop"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="mb-6 inline-flex items-center gap-1 text-sm font-medium text-gray-500 transition-colors hover:text-brand-700"
// Line 63: >
>
// JSX element — HTML-like tag becomes React component in browser
<ChevronLeft className="h-4 w-4 rtl:rotate-180" />
// Line 65: {t('product.backToShop')}
{t('product.backToShop')}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-10 lg:grid-cols-2">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<div className="relative mb-4 aspect-square overflow-hidden rounded-2xl bg-gray-50">
// JSX element — HTML-like tag becomes React component in browser
<AppImage
// Line 72: src={product.images[selectedImage] || product.image}
src={product.images[selectedImage] || product.image}
// Line 73: alt={product.name}
alt={product.name}
// Line 74: fill
fill
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="object-cover"
// Line 76: priority
priority
// Line 77: sizes="(max-width: 1024px) 100vw, 640px"
sizes="(max-width: 1024px) 100vw, 640px"
// Line 78: />
/>
// Line 79: {product.badge && (
{product.badge && (
// JSX element — HTML-like tag becomes React component in browser
<span className="absolute start-4 top-4 rounded-full bg-gold-500 px-4 py-1.5 text-sm font-semibold text-white shadow-gold">
// Line 81: {product.badge}
{product.badge}
// JSX element — HTML-like tag becomes React component in browser
</span>
// Line 83: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 85: {product.images.length > 1 && (
{product.images.length > 1 && (
// JSX element — HTML-like tag becomes React component in browser
<div className="flex gap-3">
// Array.map — transform each item (often render a list of components)
{product.images.map((img, i) => (
// JSX element — HTML-like tag becomes React component in browser
<button
// Line 89: key={img}
key={img}
// Click handler — runs when user clicks (must be client component)
onClick={() => setSelectedImage(i)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={`relative h-20 w-20 overflow-hidden rounded-lg border-2 transition-all ${
// Line 92: selectedImage === i
selectedImage === i
// Line 93: ? 'border-brand-700'
? 'border-brand-700'
// Line 94: : 'border-transparent opacity-60 hover:opacity-100'
: 'border-transparent opacity-60 hover:opacity-100'
// Closing brace — end of block (function, if, object, JSX)
}`}
// Line 96: >
>
// JSX element — HTML-like tag becomes React component in browser
<AppImage src={img} alt="" fill className="object-cover" sizes="80px" />
// JSX element — HTML-like tag becomes React component in browser
</button>
// Line 99: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 101: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-2 flex items-center gap-2">
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href={`/shop?category=${product.category}`}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="rounded-full bg-brand-50 px-3 py-1 text-xs font-semibold uppercase tracking-wider text-brand-700 transition-colors hover:bg-brand-100"
// Line 109: >
>
// Line 110: {t(`categories.${product.category}.name`)}
{t(`categories.${product.category}.name`)}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// Line 112: {product.inStock ? (
{product.inStock ? (
// JSX element — HTML-like tag becomes React component in browser
<span className="flex items-center gap-1 text-xs font-medium text-brand-700">
// JSX element — HTML-like tag becomes React component in browser
<Check className="h-3 w-3" /> {t('product.inStock')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// Line 116: ) : (
) : (
// JSX element — HTML-like tag becomes React component in browser
<span className="text-xs font-medium text-red-500">
// Line 118: {t('product.outOfStock')}
{t('product.outOfStock')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// Line 120: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<h1 className="mb-2 font-display text-3xl font-bold text-brand-900 lg:text-4xl">
// Line 124: {product.name}
{product.name}
// JSX element — HTML-like tag becomes React component in browser
</h1>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-4 text-gray-500">{product.description}</p>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-6 flex items-baseline gap-2">
// JSX element — HTML-like tag becomes React component in browser
<span className="text-3xl font-bold text-brand-800">
// Line 131: {formatPrice(product.price, getFormatLocale(locale))}
{formatPrice(product.price, getFormatLocale(locale))}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<span className="text-sm text-gray-400">{product.priceUnit}</span>
// Line 134: {product.weight && (
{product.weight && (
// JSX element — HTML-like tag becomes React component in browser
<span className="text-sm text-gray-400">· {product.weight}</span>
// Line 136: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-6 flex items-center gap-2 rounded-lg border border-brand-100 bg-brand-50/50 px-4 py-3">
// JSX element — HTML-like tag becomes React component in browser
<ShieldCheck className="h-5 w-5 shrink-0 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<span className="text-sm text-brand-800">{t('product.halalTrust')}</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-6 rounded-2xl border border-gray-100 bg-gray-50 p-6">
// JSX element — HTML-like tag becomes React component in browser
<CustomizationSelector
// Line 146: category={product.category}
category={product.category}
// Line 147: customization={customization}
customization={customization}
// Change handler — runs when input/select value changes
onChange={setCustomization}
// Line 149: />
/>
// JSX element — HTML-like tag becomes React component in browser
<div className="mt-4 rounded-lg bg-white px-4 py-3">
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs text-gray-500">{t('product.yourSelection')}</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm font-semibold text-brand-800">
// Line 153: {getCustomizationLabel(customization, t)}
{getCustomizationLabel(customization, t)}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-6 flex items-center gap-4">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center rounded-lg border border-gray-200">
// JSX element — HTML-like tag becomes React component in browser
<button
// Click handler — runs when user clicks (must be client component)
onClick={() => setQuantity(Math.max(1, quantity - 1))}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="flex h-12 w-12 items-center justify-center text-gray-500 transition-colors hover:text-brand-700"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('product.decreaseQty')}
// Line 164: >
>
// JSX element — HTML-like tag becomes React component in browser
<Minus className="h-4 w-4" />
// JSX element — HTML-like tag becomes React component in browser
</button>
// JSX element — HTML-like tag becomes React component in browser
<span className="w-12 text-center font-semibold">{quantity}</span>
// JSX element — HTML-like tag becomes React component in browser
<button
// Click handler — runs when user clicks (must be client component)
onClick={() => setQuantity(quantity + 1)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="flex h-12 w-12 items-center justify-center text-gray-500 transition-colors hover:text-brand-700"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('product.increaseQty')}
// Line 172: >
>
// JSX element — HTML-like tag becomes React component in browser
<Plus className="h-4 w-4" />
// JSX element — HTML-like tag becomes React component in browser
</button>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Button
// Click handler — runs when user clicks (must be client component)
onClick={handleAddToCart}
// Line 179: disabled={!product.inStock}
disabled={!product.inStock}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="flex-1"
// Line 181: size="lg"
size="lg"
// Line 182: >
>
// Line 183: {added ? (
{added ? (
// React Fragment — group elements without extra wrapper DOM node
<>
// JSX element — HTML-like tag becomes React component in browser
<Check className="h-4 w-4" />
// Line 186: {t('product.addedToCart')}
{t('product.addedToCart')}
// JSX element — HTML-like tag becomes React component in browser
</>
// Line 188: ) : (
) : (
// React Fragment — group elements without extra wrapper DOM node
<>
// JSX element — HTML-like tag becomes React component in browser
<ShoppingCart className="h-4 w-4" />
// Line 191: {t('product.addToCart')}
{t('product.addToCart')}
// JSX element — HTML-like tag becomes React component in browser
</>
// Line 193: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</Button>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<button
// Click handler — runs when user clicks (must be client component)
onClick={() => toggleItem(rawProduct)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={`flex h-12 w-12 items-center justify-center rounded-lg border-2 transition-all ${
// Line 199: inWishlist
inWishlist
// Line 200: ? 'border-red-200 bg-red-50 text-red-500'
? 'border-red-200 bg-red-50 text-red-500'
// Line 201: : 'border-gray-200 text-gray-400 hover:border-brand-300 h...
: 'border-gray-200 text-gray-400 hover:border-brand-300 hover:text-brand-700'
// Closing brace — end of block (function, if, object, JSX)
}`}
// Accessibility attribute — screen readers and assistive tech
aria-label={
// Line 204: inWishlist ? t('product.removeWishlist') : t('product.add...
inWishlist ? t('product.removeWishlist') : t('product.addWishlist')
// Closing brace — end of block (function, if, object, JSX)
}
// Line 206: >
>
// JSX element — HTML-like tag becomes React component in browser
<Heart className={`h-5 w-5 ${inWishlist ? 'fill-current' : ''}`} />
// JSX element — HTML-like tag becomes React component in browser
</button>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="border-t border-gray-100 pt-6">
// JSX element — HTML-like tag becomes React component in browser
<h2 className="mb-3 font-display text-lg font-semibold text-brand-900">
// Line 213: {t('product.aboutProduct')}
{t('product.aboutProduct')}
// JSX element — HTML-like tag becomes React component in browser
</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm leading-relaxed text-gray-600">
// Line 216: {product.longDescription}
{product.longDescription}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 223: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,48 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/app/shop/layout.tsx
* NOT used by the app — read this to learn how the real file works
*/
// Import React — core UI library (components, hooks, JSX)
import { Suspense } from 'react';
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const metadata = {
// Property or array item — trailing comma allowed in TypeScript
title: 'Shop',
// Line 5: description:
description:
// Property or array item — trailing comma allowed in TypeScript
'Browse premium halal chicken, beef, lamb, and fish. Customized cuts delivered fresh to your door.',
// 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 ShopLayout({
// Property or array item — trailing comma allowed in TypeScript
children,
// Closing brace — end of block (function, if, object, JSX)
}: {
// Line 12: children: React.ReactNode;
children: React.ReactNode;
// Closing brace — end of block (function, if, object, JSX)
}) {
// Return value from function
return <Suspense fallback={<ShopLoading />}>{children}</Suspense>;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Function declaration — reusable logic in this file
function ShopLoading() {
// Return JSX — describes UI tree React renders to the DOM
return (
// JSX element — HTML-like tag becomes React component in browser
<div className="flex min-h-[50vh] items-center justify-center">
// JSX element — HTML-like tag becomes React component in browser
<div className="h-8 w-8 animate-spin rounded-full border-4 border-brand-200 border-t-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 22: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,275 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/app/shop/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 { useCallback, useEffect, useMemo, useState } from 'react';
// Next.js App Router hooks — useRouter, useParams, useSearchParams
import { useRouter, useSearchParams } from 'next/navigation';
// Import project module (@/ alias = src/ folder in tsconfig)
import ProductCard from '@/components/product/ProductCard';
// Import project module (@/ alias = src/ folder in tsconfig)
import ShopFilters from '@/components/shop/ShopFilters';
// Import project module (@/ alias = src/ folder in tsconfig)
import { products } from '@/lib/products';
// Import project module (@/ alias = src/ folder in tsconfig)
import { localizeProduct } from '@/lib/product-i18n';
// Import project module (@/ alias = src/ folder in tsconfig)
import { Category, SortOption } from '@/types';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// (blank line — separates logical blocks for readability)
// Line 12: const CATEGORIES: Category[] = ['chicken', 'beef', 'lamb'...
const CATEGORIES: Category[] = ['chicken', 'beef', 'lamb', 'fish'];
// (blank line — separates logical blocks for readability)
// Function declaration — reusable logic in this file
function parseCategory(value: string | null): Category | 'all' {
// Return value from function
return CATEGORIES.includes(value as Category) ? (value as Category) : 'all';
// 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 ShopPage() {
// Next.js router — programmatic navigation (router.push)
const router = useRouter();
// Read URL query string (?category=beef&q=steak)
const searchParams = useSearchParams();
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (blank line — separates logical blocks for readability)
// React useState — local component state that triggers re-render on change
const [selectedCategory, setSelectedCategory] = useState<Category | 'all'>(
// Line 24: parseCategory(searchParams.get('category'))
parseCategory(searchParams.get('category'))
// Line 25: );
);
// React useState — local component state that triggers re-render on change
const [sortBy, setSortBy] = useState<SortOption>('featured');
// React useState — local component state that triggers re-render on change
const [searchQuery, setSearchQuery] = useState(searchParams.get('q') || '');
// (blank line — separates logical blocks for readability)
// Side effect hook — runs after paint; deps array controls when it re-runs
useEffect(() => {
// Line 30: setSelectedCategory(parseCategory(searchParams.get('categ...
setSelectedCategory(parseCategory(searchParams.get('category')));
// Line 31: setSearchQuery(searchParams.get('q') || '');
setSearchQuery(searchParams.get('q') || '');
// Closing brace — end of block (function, if, object, JSX)
}, [searchParams]);
// (blank line — separates logical blocks for readability)
// React useCallback — stable function reference for useEffect/useMemo deps
const pushParams = useCallback(
// Line 35: (updates: { category?: Category | 'all'; q?: string }) => {
(updates: { category?: Category | 'all'; q?: string }) => {
// Line 36: const params = new URLSearchParams(searchParams.toString());
const params = new URLSearchParams(searchParams.toString());
// (blank line — separates logical blocks for readability)
// Conditional branch — different behavior based on runtime value
if (updates.category !== undefined) {
// Conditional branch — different behavior based on runtime value
if (updates.category === 'all') params.delete('category');
// Line 40: else params.set('category', updates.category);
else params.set('category', updates.category);
// 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 (updates.q !== undefined) {
// Conditional branch — different behavior based on runtime value
if (updates.q) params.set('q', updates.q);
// Line 45: else params.delete('q');
else params.delete('q');
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Line 48: const qs = params.toString();
const qs = params.toString();
// Line 49: router.push(qs ? `/shop?${qs}` : '/shop', { scroll: false...
router.push(qs ? `/shop?${qs}` : '/shop', { scroll: false });
// Closing brace — end of block (function, if, object, JSX)
},
// Line 51: [router, searchParams]
[router, searchParams]
// Line 52: );
);
// (blank line — separates logical blocks for readability)
// Line 54: const handleCategoryChange = (category: Category | 'all')...
const handleCategoryChange = (category: Category | 'all') => {
// Line 55: setSelectedCategory(category);
setSelectedCategory(category);
// Line 56: pushParams({ category });
pushParams({ category });
// Closing brace — end of block (function, if, object, JSX)
};
// (blank line — separates logical blocks for readability)
// Line 59: const handleSearchChange = (query: string) => {
const handleSearchChange = (query: string) => {
// Line 60: setSearchQuery(query);
setSearchQuery(query);
// Line 61: pushParams({ q: query });
pushParams({ q: query });
// Closing brace — end of block (function, if, object, JSX)
};
// (blank line — separates logical blocks for readability)
// React useMemo — cache expensive computed value until dependencies change
const filteredProducts = useMemo(() => {
// Array.map — transform each item (often render a list of components)
let result = products.map((p) => localizeProduct(p, t));
// (blank line — separates logical blocks for readability)
// Conditional branch — different behavior based on runtime value
if (selectedCategory !== 'all') {
// Array.filter — keep items matching condition (search, category)
result = result.filter((p) => p.category === selectedCategory);
// 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 (searchQuery.trim()) {
// Line 72: const query = searchQuery.toLowerCase();
const query = searchQuery.toLowerCase();
// Array.filter — keep items matching condition (search, category)
result = result.filter(
// Line 74: (p) =>
(p) =>
// Line 75: p.name.toLowerCase().includes(query) ||
p.name.toLowerCase().includes(query) ||
// Line 76: p.description.toLowerCase().includes(query) ||
p.description.toLowerCase().includes(query) ||
// Line 77: p.tags.some((tag) => tag.includes(query))
p.tags.some((tag) => tag.includes(query))
// Line 78: );
);
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Switch — multiple branches on one variable (e.g. sort order)
switch (sortBy) {
// Switch case — handle one specific value
case 'price-asc':
// Array.sort — reorder items (price, name, featured)
result.sort((a, b) => a.price - b.price);
// Line 84: break;
break;
// Switch case — handle one specific value
case 'price-desc':
// Array.sort — reorder items (price, name, featured)
result.sort((a, b) => b.price - a.price);
// Line 87: break;
break;
// Switch case — handle one specific value
case 'name':
// Array.sort — reorder items (price, name, featured)
result.sort((a, b) => a.name.localeCompare(b.name));
// Line 90: break;
break;
// Switch case — handle one specific value
case 'featured':
// Switch default — fallback when no case matches
default:
// Array.sort — reorder items (price, name, featured)
result.sort((a, b) => (b.featured ? 1 : 0) - (a.featured ? 1 : 0));
// Line 94: break;
break;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Return value from function
return result;
// Closing brace — end of block (function, if, object, JSX)
}, [selectedCategory, sortBy, searchQuery, t]);
// (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
<div className="bg-gray-50">
// JSX element — HTML-like tag becomes React component in browser
<div className="border-b border-gray-100 bg-white">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<h1 className="section-heading mb-2">{t('shop.title')}</h1>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-gray-500">{t('shop.subtitle')}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 py-10 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-col gap-8 lg:flex-row">
// JSX element — HTML-like tag becomes React component in browser
<aside className="lg:w-64 lg:shrink-0">
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium sticky top-24 p-6">
// JSX element — HTML-like tag becomes React component in browser
<ShopFilters
// Line 114: selectedCategory={selectedCategory}
selectedCategory={selectedCategory}
// Line 115: onCategoryChange={handleCategoryChange}
onCategoryChange={handleCategoryChange}
// Line 116: sortBy={sortBy}
sortBy={sortBy}
// Line 117: onSortChange={setSortBy}
onSortChange={setSortBy}
// Line 118: searchQuery={searchQuery}
searchQuery={searchQuery}
// Line 119: onSearchChange={handleSearchChange}
onSearchChange={handleSearchChange}
// Line 120: totalResults={filteredProducts.length}
totalResults={filteredProducts.length}
// Line 121: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</aside>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="flex-1">
// Line 126: {filteredProducts.length === 0 ? (
{filteredProducts.length === 0 ? (
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium flex flex-col items-center justify-center p-16 text-center">
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-2 text-lg font-semibold text-brand-900">
// Line 129: {t('shop.noProducts')}
{t('shop.noProducts')}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm text-gray-500">{t('shop.noProductsHint')}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 133: ) : (
) : (
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-6 sm:grid-cols-2 xl:grid-cols-3">
// Array.map — transform each item (often render a list of components)
{filteredProducts.map((product) => (
// JSX element — HTML-like tag becomes React component in browser
<ProductCard key={product.id} product={product} />
// Line 137: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 139: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 144: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,103 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/app/wishlist/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 { Heart } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import ProductCard from '@/components/product/ProductCard';
// Import project module (@/ alias = src/ folder in tsconfig)
import Button from '@/components/ui/Button';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useWishlistStore } from '@/store/wishlist';
// 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 WishlistPage() {
// Zustand selector — subscribe to slice of global store
const items = useWishlistStore((s) => s.items);
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// Array.map — transform each item (often render a list of components)
const localizedItems = items.map((p) => localizeProduct(p, t));
// (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
<div className="bg-gray-50">
// JSX element — HTML-like tag becomes React component in browser
<div className="border-b border-gray-100 bg-white">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<h1 className="section-heading">{t('wishlist.title')}</h1>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-gray-500">
// Line 22: {t('wishlist.saved', { count: items.length })}
{t('wishlist.saved', { count: items.length })}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 py-10 sm:px-6 lg:px-8">
// Line 28: {items.length === 0 ? (
{items.length === 0 ? (
// JSX element — HTML-like tag becomes React component in browser
<div className="card-premium flex flex-col items-center justify-center p-16 text-center">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-red-50">
// JSX element — HTML-like tag becomes React component in browser
<Heart className="h-8 w-8 text-red-300" />
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<h2 className="mb-2 text-lg font-semibold text-brand-900">
// Line 34: {t('wishlist.empty')}
{t('wishlist.empty')}
// JSX element — HTML-like tag becomes React component in browser
</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-6 text-sm text-gray-500">{t('wishlist.emptyHint')}</p>
// JSX element — HTML-like tag becomes React component in browser
<Link href="/shop">
// JSX element — HTML-like tag becomes React component in browser
<Button>{t('wishlist.browse')}</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 41: ) : (
) : (
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
// Array.map — transform each item (often render a list of components)
{localizedItems.map((product) => (
// JSX element — HTML-like tag becomes React component in browser
<ProductCard key={product.id} product={product} />
// Line 45: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 47: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 50: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,132 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/home/AboutPreview.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 { 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 { useTranslation } from '@/hooks/useTranslation';
// Import project module (@/ alias = src/ folder in tsconfig)
import { IMAGES } from '@/lib/images';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function AboutPreview() {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (blank line — separates logical blocks for readability)
// Line 13: const stats = [
const stats = [
// Property or array item — trailing comma allowed in TypeScript
{ value: t('aboutPreview.statHalal'), label: t('aboutPreview.statHalalLabel') },
// Property or array item — trailing comma allowed in TypeScript
{ value: t('aboutPreview.statDays'), label: t('aboutPreview.statDaysLabel') },
// Property or array item — trailing comma allowed in TypeScript
{ value: t('aboutPreview.statDelivery'), label: t('aboutPreview.statDeliveryLabel') },
// Property or array item — trailing comma allowed in TypeScript
{ value: t('aboutPreview.statFresh'), label: t('aboutPreview.statFreshLabel') },
// End of array literal
];
// (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
<section className="bg-white py-20">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="grid items-center gap-12 lg:grid-cols-2 lg:gap-16">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<span className="mb-2 inline-block text-sm font-semibold uppercase tracking-wider text-gold-600">
// Line 26: {t('aboutPreview.label')}
{t('aboutPreview.label')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<h2 className="section-heading mb-6">{t('aboutPreview.title')}</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-4 leading-relaxed text-gray-600">{t('aboutPreview.p1')}</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-4 leading-relaxed text-gray-600">{t('aboutPreview.p2')}</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-8 leading-relaxed text-gray-600">{t('aboutPreview.p3')}</p>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-8 grid grid-cols-2 gap-4 sm:grid-cols-4">
// Array.map — transform each item (often render a list of components)
{stats.map((stat) => (
// JSX element — HTML-like tag becomes React component in browser
<div
// Line 36: key={stat.label}
key={stat.label}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="rounded-xl border border-cream-300/80 bg-cream-50 p-4 text-center"
// Line 38: >
>
// JSX element — HTML-like tag becomes React component in browser
<p className="font-display text-2xl font-bold text-brand-800">{stat.value}</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="mt-1 text-xs font-medium text-gray-500">{stat.label}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 42: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Link href="/about">
// JSX element — HTML-like tag becomes React component in browser
<Button variant="secondary">
// Line 47: {t('aboutPreview.readMore')}
{t('aboutPreview.readMore')}
// JSX element — HTML-like tag becomes React component in browser
<ArrowRight className="h-4 w-4 rtl:rotate-180" />
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="relative aspect-[4/5] overflow-hidden rounded-2xl shadow-premium-lg">
// JSX element — HTML-like tag becomes React component in browser
<AppImage
// Line 55: src={IMAGES.aboutMeat}
src={IMAGES.aboutMeat}
// Line 56: alt={t('aboutPreview.imageAlt')}
alt={t('aboutPreview.imageAlt')}
// Line 57: fill
fill
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="object-cover"
// Line 59: sizes="(max-width: 1024px) 100vw, 640px"
sizes="(max-width: 1024px) 100vw, 640px"
// Line 60: />
/>
// JSX element — HTML-like tag becomes React component in browser
<div className="absolute inset-0 bg-gradient-to-t from-brand-950/30 via-transparent to-transparent" />
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// Line 66: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,95 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/home/CTA.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';
// Import project module (@/ alias = src/ folder in tsconfig)
import Button from '@/components/ui/Button';
// Lucide icons — lightweight SVG icon components
import { ArrowRight, MessageCircle } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// Import project module (@/ alias = src/ folder in tsconfig)
import { WHATSAPP_URL } from '@/lib/constants';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function CTA() {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (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
<section className="py-20">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="relative overflow-hidden rounded-3xl bg-gradient-to-br from-brand-700 to-brand-900 px-8 py-16 text-center sm:px-16">
// JSX element — HTML-like tag becomes React component in browser
<div className="absolute -right-20 -top-20 h-60 w-60 rounded-full bg-gold-500/10" />
// JSX element — HTML-like tag becomes React component in browser
<div className="absolute -bottom-16 -left-16 h-48 w-48 rounded-full bg-gold-500/10" />
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="relative">
// JSX element — HTML-like tag becomes React component in browser
<h2 className="mb-4 font-display text-3xl font-bold text-white md:text-4xl">
// Line 21: {t('cta.title')}
{t('cta.title')}
// JSX element — HTML-like tag becomes React component in browser
</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="mx-auto mb-8 max-w-xl text-brand-100">{t('cta.subtitle')}</p>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-col items-center justify-center gap-4 sm:flex-row">
// JSX element — HTML-like tag becomes React component in browser
<Link href="/shop">
// JSX element — HTML-like tag becomes React component in browser
<Button variant="gold" size="lg">
// Line 27: {t('cta.button')}
{t('cta.button')}
// JSX element — HTML-like tag becomes React component in browser
<ArrowRight className="h-4 w-4 rtl:rotate-180" />
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
<a href={WHATSAPP_URL} target="_blank" rel="noopener noreferrer">
// JSX element — HTML-like tag becomes React component in browser
<Button
// Line 33: variant="secondary"
variant="secondary"
// Line 34: size="lg"
size="lg"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="border-white/30 bg-white/10 text-white hover:border-gold-400/50 hover:bg-white/15"
// Line 36: >
>
// JSX element — HTML-like tag becomes React component in browser
<MessageCircle className="h-4 w-4" />
// Line 38: {t('cta.whatsapp')}
{t('cta.whatsapp')}
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// Line 46: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,110 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/home/CategoryGrid.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 { ArrowRight } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { CATEGORY_IDS, CATEGORY_IMAGES } from '@/lib/constants';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// Default export — main component/page Next.js or other files import
export default function CategoryGrid() {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (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
<section className="bg-cream-50 py-20">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-12 text-center">
// JSX element — HTML-like tag becomes React component in browser
<h2 className="section-heading mb-3">{t('categories.title')}</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="mx-auto max-w-2xl text-gray-500">{t('categories.subtitle')}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
// Array.map — transform each item (often render a list of components)
{CATEGORY_IDS.map((id) => (
// JSX element — HTML-like tag becomes React component in browser
<Link
// Line 22: key={id}
key={id}
// Link target URL — internal route or external https://
href={`/shop?category=${id}`}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="group relative overflow-hidden rounded-2xl shadow-premium transition-all duration-300 hover:shadow-premium-lg"
// Line 25: >
>
// JSX element — HTML-like tag becomes React component in browser
<div className="relative aspect-[3/4]">
// JSX element — HTML-like tag becomes React component in browser
<AppImage
// Line 28: src={CATEGORY_IMAGES[id]}
src={CATEGORY_IMAGES[id]}
// Line 29: alt={t(`categories.${id}.name`)}
alt={t(`categories.${id}.name`)}
// Line 30: fill
fill
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="object-cover transition-transform duration-500 group-hover:scale-110"
// Line 32: sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw...
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 320px"
// Line 33: />
/>
// JSX element — HTML-like tag becomes React component in browser
<div className="absolute inset-0 bg-gradient-to-t from-brand-950/92 via-brand-800/50 to-brand-700/10" />
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="absolute bottom-0 left-0 right-0 p-6">
// JSX element — HTML-like tag becomes React component in browser
<h3 className="mb-1 font-display text-2xl font-bold text-white">
// Line 38: {t(`categories.${id}.name`)}
{t(`categories.${id}.name`)}
// JSX element — HTML-like tag becomes React component in browser
</h3>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-3 text-sm text-brand-200">
// Line 41: {t(`categories.${id}.description`)}
{t(`categories.${id}.description`)}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
<span className="inline-flex items-center gap-1 text-sm font-semibold text-gold-400 transition-colors group-hover:text-gold-300">
// Line 44: {t('categories.shop', { name: t(`categories.${id}.name`) })}
{t('categories.shop', { name: t(`categories.${id}.name`) })}
// JSX element — HTML-like tag becomes React component in browser
<ArrowRight className="h-4 w-4 transition-transform group-hover:translate-x-1 rtl:rotate-180 rtl:group-hover:-translate-x-1" />
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// Line 49: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// Line 53: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,233 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/home/ContactPreview.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 { Phone, MapPin, Clock, MessageCircle, ExternalLink } 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 Logo from '@/components/ui/Logo';
// 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_PHONE_DISPLAY,
// Property or array item — trailing comma allowed in TypeScript
SITE_HOURS,
// Property or array item — trailing comma allowed in TypeScript
WHATSAPP_URL,
// Property or array item — trailing comma allowed in TypeScript
MAPS_URL,
// Closing brace — end of block (function, if, object, JSX)
} from '@/lib/constants';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function ContactPreview() {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// Line 18: const telHref = `tel:+46725855050`;
const telHref = `tel:+46725855050`;
// (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
<section className="bg-brand-950 py-20 text-white">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-12 lg:grid-cols-2 lg:gap-16">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<span className="mb-2 inline-block text-sm font-semibold uppercase tracking-wider text-gold-400">
// Line 26: {t('contact.label')}
{t('contact.label')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<h2 className="mb-8 font-display text-3xl font-bold md:text-4xl">
// Line 29: {t('contact.title')}
{t('contact.title')}
// JSX element — HTML-like tag becomes React component in browser
</h2>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<ul className="mb-8 space-y-5">
// JSX element — HTML-like tag becomes React component in browser
<li className="flex gap-4">
// JSX element — HTML-like tag becomes React component in browser
<MapPin className="mt-0.5 h-5 w-5 shrink-0 text-gold-400" />
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs font-semibold uppercase tracking-wider text-gold-400">
// Line 37: {t('contact.addressLabel')}
{t('contact.addressLabel')}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-brand-100">{SITE_ADDRESS}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</li>
// JSX element — HTML-like tag becomes React component in browser
<li className="flex gap-4">
// JSX element — HTML-like tag becomes React component in browser
<Phone className="mt-0.5 h-5 w-5 shrink-0 text-gold-400" />
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs font-semibold uppercase tracking-wider text-gold-400">
// Line 46: {t('contact.phoneLabel')}
{t('contact.phoneLabel')}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
<a href={telHref} className="text-brand-100 hover:text-white">
// Line 49: {SITE_PHONE_DISPLAY}
{SITE_PHONE_DISPLAY}
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</li>
// JSX element — HTML-like tag becomes React component in browser
<li className="flex gap-4">
// JSX element — HTML-like tag becomes React component in browser
<Clock className="mt-0.5 h-5 w-5 shrink-0 text-gold-400" />
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs font-semibold uppercase tracking-wider text-gold-400">
// Line 57: {t('contact.hoursLabel')}
{t('contact.hoursLabel')}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-brand-100">
// Line 60: {t('contact.hoursValue', { hours: SITE_HOURS })}
{t('contact.hoursValue', { hours: SITE_HOURS })}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</li>
// JSX element — HTML-like tag becomes React component in browser
</ul>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-col gap-3 sm:flex-row">
// JSX element — HTML-like tag becomes React component in browser
<Link href="/about#contact">
// JSX element — HTML-like tag becomes React component in browser
<Button
// Line 69: variant="secondary"
variant="secondary"
// Line 70: size="lg"
size="lg"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="w-full border-white/30 bg-white/10 text-white hover:border-gold-400/50 hover:bg-white/15 sm:w-auto"
// Line 72: >
>
// Line 73: {t('contact.learnMore')}
{t('contact.learnMore')}
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
<a href={WHATSAPP_URL} target="_blank" rel="noopener noreferrer">
// JSX element — HTML-like tag becomes React component in browser
<Button variant="gold" size="lg" className="w-full sm:w-auto">
// JSX element — HTML-like tag becomes React component in browser
<MessageCircle className="h-4 w-4" />
// Line 79: {t('contact.writeUs')}
{t('contact.writeUs')}
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
<a href={telHref}>
// JSX element — HTML-like tag becomes React component in browser
<Button
// Line 84: variant="secondary"
variant="secondary"
// Line 85: size="lg"
size="lg"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="w-full border-white/30 bg-white/10 text-white hover:border-gold-400/50 hover:bg-white/15 sm:w-auto"
// Line 87: >
>
// JSX element — HTML-like tag becomes React component in browser
<Phone className="h-4 w-4" />
// Line 89: {t('contact.callUs')}
{t('contact.callUs')}
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-col justify-center">
// JSX element — HTML-like tag becomes React component in browser
<a
// Link target URL — internal route or external https://
href={MAPS_URL}
// Line 98: target="_blank"
target="_blank"
// Line 99: rel="noopener noreferrer"
rel="noopener noreferrer"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="card-premium group flex flex-col items-center gap-4 bg-brand-900/50 p-8 text-center transition-all hover:bg-brand-900/70"
// Line 101: >
>
// JSX element — HTML-like tag becomes React component in browser
<Logo size="lg" className="ring-gold-400/30" />
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<p className="font-display text-xl font-bold">{t('site.name')}</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="mt-1 text-sm text-brand-200">{SITE_ADDRESS}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<span className="inline-flex items-center gap-2 text-sm font-semibold text-gold-400 transition-colors group-hover:text-gold-300">
// Line 108: {t('contact.openMaps')}
{t('contact.openMaps')}
// JSX element — HTML-like tag becomes React component in browser
<ExternalLink className="h-4 w-4" />
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// Line 116: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,89 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/home/FeaturedProducts.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';
// Import project module (@/ alias = src/ folder in tsconfig)
import ProductCard from '@/components/product/ProductCard';
// Import project module (@/ alias = src/ folder in tsconfig)
import { getFeaturedProducts } from '@/lib/products';
// Import project module (@/ alias = src/ folder in tsconfig)
import Button from '@/components/ui/Button';
// Lucide icons — lightweight SVG icon components
import { ArrowRight } from 'lucide-react';
// 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 FeaturedProducts() {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// Line 13: const featured = getFeaturedProducts()
const featured = getFeaturedProducts()
// Line 14: .slice(0, 4)
.slice(0, 4)
// Array.map — transform each item (often render a list of components)
.map((p) => localizeProduct(p, t));
// (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
<section className="py-20">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-12 flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-end">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<span className="mb-2 inline-block text-sm font-semibold uppercase tracking-wider text-gold-600">
// Line 23: {t('featured.label')}
{t('featured.label')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<h2 className="section-heading">{t('featured.title')}</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="mt-2 max-w-lg text-gray-500">{t('featured.subtitle')}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<Link href="/shop">
// JSX element — HTML-like tag becomes React component in browser
<Button variant="secondary">
// Line 30: {t('featured.viewAll')}
{t('featured.viewAll')}
// JSX element — HTML-like tag becomes React component in browser
<ArrowRight className="h-4 w-4 rtl:rotate-180" />
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
// Array.map — transform each item (often render a list of components)
{featured.map((product) => (
// JSX element — HTML-like tag becomes React component in browser
<ProductCard key={product.id} product={product} />
// Line 39: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// Line 43: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,184 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/home/Hero.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';
// Import project module (@/ alias = src/ folder in tsconfig)
import AppImage from '@/components/ui/AppImage';
// Import project module (@/ alias = src/ folder in tsconfig)
import Button from '@/components/ui/Button';
// Import project module (@/ alias = src/ folder in tsconfig)
import Logo from '@/components/ui/Logo';
// Lucide icons — lightweight SVG icon components
import { ArrowRight, ShieldCheck, MapPin, Clock } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// Import project module (@/ alias = src/ folder in tsconfig)
import { IMAGES } from '@/lib/images';
// Import project module (@/ alias = src/ folder in tsconfig)
import { SITE_LOCATION, SITE_HOURS, SITE_ADDRESS, WHATSAPP_URL } from '@/lib/constants';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function Hero() {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (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
<section className="relative min-h-[85vh] overflow-hidden bg-brand-950">
// JSX element — HTML-like tag becomes React component in browser
<div className="absolute inset-0">
// JSX element — HTML-like tag becomes React component in browser
<AppImage
// Line 19: src={IMAGES.hero}
src={IMAGES.hero}
// Line 20: alt=""
alt=""
// Line 21: fill
fill
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="object-cover"
// Line 23: priority
priority
// Line 24: sizes="100vw"
sizes="100vw"
// Line 25: placeholder="blur"
placeholder="blur"
// Line 26: blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAA...
blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAAUH/8QAIhAAAgEDBQAAAAAAAAAAAAAAAQIDAAQRBQYhIjFB/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAZEQACAwEAAAAAAAAAAAAAAAAAAQIRITH/2gAMAwEAAhEDEEA/ALextba0t0t7eJI4kHVEU4AqT/9k="
// Line 27: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="absolute inset-0 bg-gradient-to-r from-brand-950/95 via-brand-900/85 to-brand-900/40" />
// JSX element — HTML-like tag becomes React component in browser
<div className="absolute inset-0 bg-gradient-to-t from-brand-950/60 via-transparent to-transparent" />
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="relative mx-auto flex min-h-[85vh] max-w-7xl flex-col justify-center px-4 py-20 sm:px-6 lg:px-8 lg:py-28">
// JSX element — HTML-like tag becomes React component in browser
<div className="max-w-2xl animate-slide-up">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-8 flex items-center gap-4">
// JSX element — HTML-like tag becomes React component in browser
<Logo size="lg" className="ring-gold-400/40 shadow-premium-lg" />
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm font-medium text-gold-400">{SITE_LOCATION}</p>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs text-brand-200">{t('hero.taglineShort')}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-6 inline-flex items-center gap-2 rounded-full border border-gold-500/30 bg-brand-800/50 px-4 py-1.5 backdrop-blur-sm">
// JSX element — HTML-like tag becomes React component in browser
<ShieldCheck className="h-4 w-4 text-gold-400" />
// JSX element — HTML-like tag becomes React component in browser
<span className="text-xs font-semibold uppercase tracking-wider text-gold-300">
// Line 45: {t('hero.badge')}
{t('hero.badge')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<h1 className="mb-4 font-display text-4xl font-bold leading-tight text-white sm:text-5xl lg:text-6xl">
// Line 50: {t('hero.title')}
{t('hero.title')}
// JSX element — HTML-like tag becomes React component in browser
</h1>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-2 text-xl font-medium text-gold-300 sm:text-2xl">
// Line 54: {t('hero.subtitleShort')}
{t('hero.subtitleShort')}
// JSX element — HTML-like tag becomes React component in browser
</p>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-8 text-base leading-relaxed text-brand-100 sm:text-lg">
// Line 58: {t('hero.subtitle')}
{t('hero.subtitle')}
// JSX element — HTML-like tag becomes React component in browser
</p>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-8 flex flex-wrap gap-4 text-sm text-brand-200">
// JSX element — HTML-like tag becomes React component in browser
<span className="flex items-center gap-1.5">
// JSX element — HTML-like tag becomes React component in browser
<Clock className="h-4 w-4 text-gold-400" />
// Line 64: {t('hero.hours', { hours: SITE_HOURS })}
{t('hero.hours', { hours: SITE_HOURS })}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<span className="flex items-center gap-1.5">
// JSX element — HTML-like tag becomes React component in browser
<MapPin className="h-4 w-4 text-gold-400" />
// Line 68: {SITE_ADDRESS}
{SITE_ADDRESS}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-col gap-4 sm:flex-row">
// JSX element — HTML-like tag becomes React component in browser
<Link href="/shop">
// JSX element — HTML-like tag becomes React component in browser
<Button variant="gold" size="lg">
// Line 75: {t('hero.shopNow')}
{t('hero.shopNow')}
// JSX element — HTML-like tag becomes React component in browser
<ArrowRight className="h-4 w-4 rtl:rotate-180" />
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
<a href={WHATSAPP_URL} target="_blank" rel="noopener noreferrer">
// JSX element — HTML-like tag becomes React component in browser
<Button
// Line 81: variant="secondary"
variant="secondary"
// Line 82: size="lg"
size="lg"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="border-white/30 bg-white/10 text-white hover:border-gold-400/50 hover:bg-white/15"
// Line 84: >
>
// Line 85: {t('hero.whatsapp')}
{t('hero.whatsapp')}
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="absolute bottom-0 left-0 right-0 h-20 bg-gradient-to-t from-cream-50 to-transparent" />
// JSX element — HTML-like tag becomes React component in browser
</section>
// Line 94: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,139 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/home/HowItWorks.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)
// Lucide icons — lightweight SVG icon components
import { MessageCircle, ClipboardCheck, Store } 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 { WHATSAPP_URL } from '@/lib/constants';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function HowItWorks() {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (blank line — separates logical blocks for readability)
// Line 11: const steps = [
const steps = [
// Line 12: {
{
// Property or array item — trailing comma allowed in TypeScript
icon: MessageCircle,
// Property or array item — trailing comma allowed in TypeScript
step: '01',
// Property or array item — trailing comma allowed in TypeScript
title: t('howItWorks.step1Title'),
// Property or array item — trailing comma allowed in TypeScript
description: t('howItWorks.step1Desc'),
// Closing brace — end of block (function, if, object, JSX)
},
// Line 18: {
{
// Property or array item — trailing comma allowed in TypeScript
icon: ClipboardCheck,
// Property or array item — trailing comma allowed in TypeScript
step: '02',
// Property or array item — trailing comma allowed in TypeScript
title: t('howItWorks.step2Title'),
// Property or array item — trailing comma allowed in TypeScript
description: t('howItWorks.step2Desc'),
// Closing brace — end of block (function, if, object, JSX)
},
// Line 24: {
{
// Property or array item — trailing comma allowed in TypeScript
icon: Store,
// Property or array item — trailing comma allowed in TypeScript
step: '03',
// Property or array item — trailing comma allowed in TypeScript
title: t('howItWorks.step3Title'),
// Property or array item — trailing comma allowed in TypeScript
description: t('howItWorks.step3Desc'),
// Closing brace — end of block (function, if, object, JSX)
},
// End of array literal
];
// (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
<section className="bg-brand-950 py-20 text-white">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-12 text-center">
// JSX element — HTML-like tag becomes React component in browser
<span className="mb-2 inline-block text-sm font-semibold uppercase tracking-wider text-gold-400">
// Line 37: {t('howItWorks.label')}
{t('howItWorks.label')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<h2 className="font-display text-3xl font-bold md:text-4xl">
// Line 40: {t('howItWorks.title')}
{t('howItWorks.title')}
// JSX element — HTML-like tag becomes React component in browser
</h2>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-12 grid gap-8 md:grid-cols-3">
// Array.map — transform each item (often render a list of components)
{steps.map((step) => (
// JSX element — HTML-like tag becomes React component in browser
<div key={step.step} className="relative text-center">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-2xl bg-brand-800">
// JSX element — HTML-like tag becomes React component in browser
<step.icon className="h-7 w-7 text-gold-400" />
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<span className="mb-2 inline-block text-xs font-bold uppercase tracking-widest text-gold-500">
// Line 51: {t('howItWorks.step', { n: step.step })}
{t('howItWorks.step', { n: step.step })}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<h3 className="mb-2 font-display text-xl font-bold">{step.title}</h3>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm leading-relaxed text-brand-200">{step.description}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 56: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="text-center">
// JSX element — HTML-like tag becomes React component in browser
<a href={WHATSAPP_URL} target="_blank" rel="noopener noreferrer">
// JSX element — HTML-like tag becomes React component in browser
<Button variant="gold" size="lg">
// JSX element — HTML-like tag becomes React component in browser
<MessageCircle className="h-4 w-4" />
// Line 63: {t('howItWorks.whatsapp')}
{t('howItWorks.whatsapp')}
// JSX element — HTML-like tag becomes React component in browser
</Button>
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// Line 69: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,132 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/home/SocialFollow.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)
// Lucide icons — lightweight SVG icon components
import { Facebook, Instagram, MessageCircle } from 'lucide-react';
// Import external package or local module
import {
// Property or array item — trailing comma allowed in TypeScript
FACEBOOK_URL,
// Property or array item — trailing comma allowed in TypeScript
INSTAGRAM_URL,
// Property or array item — trailing comma allowed in TypeScript
WHATSAPP_URL,
// Closing brace — end of block (function, if, object, JSX)
} from '@/lib/constants';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// (blank line — separates logical blocks for readability)
// Line 11: const socialLinks = [
const socialLinks = [
// Line 12: {
{
// Property or array item — trailing comma allowed in TypeScript
key: 'facebook',
// Property or array item — trailing comma allowed in TypeScript
href: FACEBOOK_URL,
// Property or array item — trailing comma allowed in TypeScript
icon: Facebook,
// Line 16: className:
className:
// Property or array item — trailing comma allowed in TypeScript
'border-blue-600/20 bg-blue-600/10 text-blue-700 hover:border-blue-600/40 hover:bg-blue-600/15',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 19: {
{
// Property or array item — trailing comma allowed in TypeScript
key: 'instagram',
// Property or array item — trailing comma allowed in TypeScript
href: INSTAGRAM_URL,
// Property or array item — trailing comma allowed in TypeScript
icon: Instagram,
// Line 23: className:
className:
// Property or array item — trailing comma allowed in TypeScript
'border-pink-600/20 bg-pink-600/10 text-pink-700 hover:border-pink-600/40 hover:bg-pink-600/15',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 26: {
{
// Property or array item — trailing comma allowed in TypeScript
key: 'whatsapp',
// Property or array item — trailing comma allowed in TypeScript
href: WHATSAPP_URL,
// Property or array item — trailing comma allowed in TypeScript
icon: MessageCircle,
// Line 30: className:
className:
// Property or array item — trailing comma allowed in TypeScript
'border-green-600/20 bg-green-600/10 text-green-700 hover:border-green-600/40 hover:bg-green-600/15',
// Closing brace — end of block (function, if, object, JSX)
},
// End of array literal
] as const;
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function SocialFollow() {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (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
<section className="py-20">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="rounded-3xl border border-cream-300/80 bg-white px-8 py-12 text-center shadow-premium sm:px-16">
// JSX element — HTML-like tag becomes React component in browser
<span className="mb-2 inline-block text-sm font-semibold uppercase tracking-wider text-gold-600">
// Line 43: {t('social.label')}
{t('social.label')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<h2 className="section-heading mb-3">{t('social.title')}</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="mx-auto mb-8 max-w-xl text-gray-500">{t('social.subtitle')}</p>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-col items-center justify-center gap-4 sm:flex-row">
// Array.map — transform each item (often render a list of components)
{socialLinks.map(({ key, href, icon: Icon, className }) => (
// JSX element — HTML-like tag becomes React component in browser
<a
// Line 51: key={key}
key={key}
// Link target URL — internal route or external https://
href={href}
// Line 53: target="_blank"
target="_blank"
// Line 54: rel="noopener noreferrer"
rel="noopener noreferrer"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={`inline-flex items-center gap-2 rounded-xl border-2 px-6 py-3 text-sm font-semibold transition-all ${className}`}
// Line 56: >
>
// JSX element — HTML-like tag becomes React component in browser
<Icon className="h-5 w-5" />
// Line 58: {t(`social.${key}`)}
{t(`social.${key}`)}
// JSX element — HTML-like tag becomes React component in browser
</a>
// Line 60: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// Line 65: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,109 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/home/TrustBadges.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 } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function TrustBadges() {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (blank line — separates logical blocks for readability)
// Line 10: const badges = [
const badges = [
// Line 11: {
{
// Property or array item — trailing comma allowed in TypeScript
icon: ShieldCheck,
// Property or array item — trailing comma allowed in TypeScript
title: t('trust.halal'),
// Property or array item — trailing comma allowed in TypeScript
description: t('trust.halalDesc'),
// Property or array item — trailing comma allowed in TypeScript
href: '/about#halal',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 17: {
{
// Property or array item — trailing comma allowed in TypeScript
icon: Leaf,
// Property or array item — trailing comma allowed in TypeScript
title: t('trust.fresh'),
// Property or array item — trailing comma allowed in TypeScript
description: t('trust.freshDesc'),
// Property or array item — trailing comma allowed in TypeScript
href: '/about#delivery',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 23: {
{
// Property or array item — trailing comma allowed in TypeScript
icon: Award,
// Property or array item — trailing comma allowed in TypeScript
title: t('trust.premium'),
// Property or array item — trailing comma allowed in TypeScript
description: t('trust.premiumDesc'),
// Property or array item — trailing comma allowed in TypeScript
href: '/about',
// Closing brace — end of block (function, if, object, JSX)
},
// End of array literal
];
// (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
<section className="bg-cream-100 py-16">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-8 md:grid-cols-3">
// Array.map — transform each item (often render a list of components)
{badges.map((badge) => (
// JSX element — HTML-like tag becomes React component in browser
<Link
// Line 37: key={badge.title}
key={badge.title}
// Link target URL — internal route or external https://
href={badge.href}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="group rounded-2xl border border-cream-300/80 bg-white p-8 text-center shadow-premium transition-all duration-300 hover:border-brand-200 hover:shadow-premium-lg"
// Line 40: >
>
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-brand-50 transition-colors group-hover:bg-gold-50">
// JSX element — HTML-like tag becomes React component in browser
<badge.icon className="h-7 w-7 text-brand-700 transition-colors group-hover:text-gold-600" />
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<h3 className="mb-2 font-display text-xl font-bold text-brand-900">
// Line 45: {badge.title}
{badge.title}
// JSX element — HTML-like tag becomes React component in browser
</h3>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm leading-relaxed text-gray-500">{badge.description}</p>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// Line 49: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// Line 53: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,255 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/home/WeeklyOffers.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';
// Import project module (@/ alias = src/ folder in tsconfig)
import AppImage from '@/components/ui/AppImage';
// Lucide icons — lightweight SVG icon components
import { MessageCircle, ArrowRight } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { weeklyOffers } from '@/lib/offers';
// Import project module (@/ alias = src/ folder in tsconfig)
import { IMAGES } from '@/lib/images';
// Import project module (@/ alias = src/ folder in tsconfig)
import { whatsappOrderUrl } from '@/lib/constants';
// 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';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function WeeklyOffers() {
// Custom hook — returns t() translator and current locale
const { t, locale } = useTranslation();
// Line 14: 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
<section className="bg-cream-100 py-20">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="grid items-center gap-12 lg:grid-cols-2 lg:gap-16">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<span className="mb-2 inline-block text-sm font-semibold uppercase tracking-wider text-gold-600">
// Line 22: {t('offers.label')}
{t('offers.label')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<h2 className="section-heading mb-3">{t('offers.title')}</h2>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-8 text-gray-500">{t('offers.subtitle')}</p>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="space-y-4">
// Array.map — transform each item (often render a list of components)
{weeklyOffers.map((offer) => (
// JSX element — HTML-like tag becomes React component in browser
<div
// Line 30: key={offer.id}
key={offer.id}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="group flex gap-4 rounded-2xl border border-cream-300/80 bg-white p-4 shadow-premium transition-all duration-300 hover:border-brand-200 hover:shadow-premium-lg sm:p-5"
// Line 32: >
>
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href={`/product/${offer.productSlug}`}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="relative h-20 w-20 shrink-0 overflow-hidden rounded-xl sm:h-24 sm:w-24"
// Line 36: >
>
// JSX element — HTML-like tag becomes React component in browser
<AppImage
// Line 38: src={offer.image}
src={offer.image}
// Line 39: alt={t(`${offer.nameKey}.name`)}
alt={t(`${offer.nameKey}.name`)}
// Line 40: fill
fill
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="object-cover transition-transform duration-500 group-hover:scale-105"
// Line 42: sizes="(max-width: 640px) 96px, 128px"
sizes="(max-width: 640px) 96px, 128px"
// Line 43: />
/>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="min-w-0 flex-1">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-1 flex flex-wrap items-center gap-2">
// JSX element — HTML-like tag becomes React component in browser
<span
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={
// Line 50: offer.badgeKey === 'halal'
offer.badgeKey === 'halal'
// Line 51: ? 'rounded-full bg-brand-700 px-2.5 py-0.5 text-[10px] fo...
? 'rounded-full bg-brand-700 px-2.5 py-0.5 text-[10px] font-bold uppercase tracking-wider text-white'
// Line 52: : 'rounded-full bg-gold-500 px-2.5 py-0.5 text-[10px] fon...
: 'rounded-full bg-gold-500 px-2.5 py-0.5 text-[10px] font-bold uppercase tracking-wider text-white'
// Closing brace — end of block (function, if, object, JSX)
}
// Line 54: >
>
// Line 55: {t(`offers.badge.${offer.badgeKey}`)}
{t(`offers.badge.${offer.badgeKey}`)}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Link href={`/product/${offer.productSlug}`}>
// JSX element — HTML-like tag becomes React component in browser
<h3 className="mb-2 font-display text-lg font-bold text-brand-900 transition-colors hover:text-brand-700">
// Line 61: {t(`${offer.nameKey}.name`)}
{t(`${offer.nameKey}.name`)}
// JSX element — HTML-like tag becomes React component in browser
</h3>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-wrap items-end justify-between gap-3">
// JSX element — HTML-like tag becomes React component in browser
<div>
// Line 67: {offer.originalPrice != null && (
{offer.originalPrice != null && (
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm text-gray-400 line-through">
// Line 69: {t('offers.was')}: {fmt(offer.originalPrice)}/
{t('offers.was')}: {fmt(offer.originalPrice)}/
// Line 70: {t(`priceUnit.${offer.priceUnitKey}`)}
{t(`priceUnit.${offer.priceUnitKey}`)}
// JSX element — HTML-like tag becomes React component in browser
</p>
// Line 72: )}
)}
// JSX element — HTML-like tag becomes React component in browser
<p className="font-display text-xl font-bold text-brand-800">
// Line 74: {offer.originalPrice != null && (
{offer.originalPrice != null && (
// JSX element — HTML-like tag becomes React component in browser
<span className="me-2 text-sm font-semibold uppercase text-gold-600">
// Line 76: {t('offers.now')}
{t('offers.now')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// Line 78: )}
)}
// Line 79: {fmt(offer.price)}
{fmt(offer.price)}
// JSX element — HTML-like tag becomes React component in browser
<span className="text-sm font-normal text-gray-500">
// Line 81: /{t(`priceUnit.${offer.priceUnitKey}`)}
/{t(`priceUnit.${offer.priceUnitKey}`)}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-wrap gap-2">
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href={`/product/${offer.productSlug}`}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="inline-flex items-center gap-1 rounded-lg border border-brand-200 px-3 py-2 text-xs font-semibold text-brand-700 transition-colors hover:bg-brand-50"
// Line 90: >
>
// Line 91: {t('offers.viewProduct')}
{t('offers.viewProduct')}
// JSX element — HTML-like tag becomes React component in browser
<ArrowRight className="h-3 w-3 rtl:rotate-180" />
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
<a
// Link target URL — internal route or external https://
href={whatsappOrderUrl(offer.whatsappProduct)}
// Line 96: target="_blank"
target="_blank"
// Line 97: rel="noopener noreferrer"
rel="noopener noreferrer"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="inline-flex items-center gap-1.5 rounded-lg bg-brand-700 px-4 py-2 text-xs font-semibold text-white transition-colors hover:bg-brand-800"
// Line 99: >
>
// JSX element — HTML-like tag becomes React component in browser
<MessageCircle className="h-3.5 w-3.5" />
// Line 101: {t('offers.order')}
{t('offers.order')}
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 107: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<p className="mt-6 text-xs text-gray-400">{t('offers.disclaimer')}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href="/shop"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="relative hidden aspect-[4/5] overflow-hidden rounded-2xl shadow-premium-lg lg:block"
// Line 116: >
>
// JSX element — HTML-like tag becomes React component in browser
<AppImage
// Line 118: src={IMAGES.hero}
src={IMAGES.hero}
// Line 119: alt=""
alt=""
// Line 120: fill
fill
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="object-cover transition-transform duration-500 hover:scale-105"
// Line 122: sizes="(max-width: 1024px) 0px, 640px"
sizes="(max-width: 1024px) 0px, 640px"
// Line 123: />
/>
// JSX element — HTML-like tag becomes React component in browser
<div className="absolute inset-0 bg-gradient-to-t from-brand-950/50 via-transparent to-transparent" />
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</section>
// Line 129: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,355 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/layout/Footer.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, Phone, Mail, MapPin, Facebook, Instagram, MessageCircle } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// Import project module (@/ alias = src/ folder in tsconfig)
import Logo from '@/components/ui/Logo';
// Import external package or local module
import {
// Property or array item — trailing comma allowed in TypeScript
SITE_PHONE_DISPLAY,
// Property or array item — trailing comma allowed in TypeScript
SITE_ADDRESS,
// Property or array item — trailing comma allowed in TypeScript
SITE_HOURS,
// Property or array item — trailing comma allowed in TypeScript
SITE_EMAIL,
// Property or array item — trailing comma allowed in TypeScript
FACEBOOK_URL,
// Property or array item — trailing comma allowed in TypeScript
INSTAGRAM_URL,
// Property or array item — trailing comma allowed in TypeScript
WHATSAPP_URL,
// 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 Footer() {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (blank line — separates logical blocks for readability)
// Line 20: const footerLinks = {
const footerLinks = {
// Line 21: shop: [
shop: [
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.chicken'), href: '/shop?category=chicken' },
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.beef'), href: '/shop?category=beef' },
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.lamb'), href: '/shop?category=lamb' },
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.fish'), href: '/shop?category=fish' },
// End of array literal
],
// Line 27: company: [
company: [
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.about'), href: '/about' },
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.halalCert'), href: '/about#halal' },
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.delivery'), href: '/about#delivery' },
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.contact'), href: '/about#contact' },
// End of array literal
],
// Line 33: account: [
account: [
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.myAccount'), href: '/account' },
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.orderHistory'), href: '/account#orders' },
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.wishlist'), href: '/wishlist' },
// Property or array item — trailing comma allowed in TypeScript
{ label: t('nav.cart'), href: '/cart' },
// End of array literal
],
// 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
<footer className="border-t border-gray-100 bg-brand-950 text-white">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="grid gap-12 md:grid-cols-2 lg:grid-cols-4">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<Link href="/" className="mb-4 flex items-center gap-3">
// JSX element — HTML-like tag becomes React component in browser
<Logo size="sm" className="ring-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<span className="font-display text-xl font-bold">{t('site.name')}</span>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-6 text-sm leading-relaxed text-brand-200">
// Line 51: {t('footer.tagline')}
{t('footer.tagline')}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-6 flex flex-wrap gap-4">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center gap-1.5 text-xs text-gold-400">
// JSX element — HTML-like tag becomes React component in browser
<ShieldCheck className="h-4 w-4" />
// Line 56: {t('trust.halal')}
{t('trust.halal')}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center gap-1.5 text-xs text-gold-400">
// JSX element — HTML-like tag becomes React component in browser
<Leaf className="h-4 w-4" />
// Line 60: {t('trust.fresh')}
{t('trust.fresh')}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center gap-1.5 text-xs text-gold-400">
// JSX element — HTML-like tag becomes React component in browser
<Award className="h-4 w-4" />
// Line 64: {t('trust.premium')}
{t('trust.premium')}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex gap-3">
// JSX element — HTML-like tag becomes React component in browser
<a
// Link target URL — internal route or external https://
href={FACEBOOK_URL}
// Line 70: target="_blank"
target="_blank"
// Line 71: rel="noopener noreferrer"
rel="noopener noreferrer"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('social.facebook')}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="rounded-lg bg-brand-800 p-2.5 text-brand-200 transition-colors hover:bg-brand-700 hover:text-white"
// Line 74: >
>
// JSX element — HTML-like tag becomes React component in browser
<Facebook className="h-4 w-4" />
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
<a
// Link target URL — internal route or external https://
href={INSTAGRAM_URL}
// Line 79: target="_blank"
target="_blank"
// Line 80: rel="noopener noreferrer"
rel="noopener noreferrer"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('social.instagram')}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="rounded-lg bg-brand-800 p-2.5 text-brand-200 transition-colors hover:bg-brand-700 hover:text-white"
// Line 83: >
>
// JSX element — HTML-like tag becomes React component in browser
<Instagram className="h-4 w-4" />
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
<a
// Link target URL — internal route or external https://
href={WHATSAPP_URL}
// Line 88: target="_blank"
target="_blank"
// Line 89: rel="noopener noreferrer"
rel="noopener noreferrer"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('social.whatsapp')}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="rounded-lg bg-brand-800 p-2.5 text-brand-200 transition-colors hover:bg-brand-700 hover:text-white"
// Line 92: >
>
// JSX element — HTML-like tag becomes React component in browser
<MessageCircle className="h-4 w-4" />
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<h3 className="mb-4 text-sm font-semibold uppercase tracking-wider text-gold-400">
// Line 100: {t('footer.shop')}
{t('footer.shop')}
// JSX element — HTML-like tag becomes React component in browser
</h3>
// JSX element — HTML-like tag becomes React component in browser
<ul className="space-y-2">
// Array.map — transform each item (often render a list of components)
{footerLinks.shop.map((link) => (
// JSX element — HTML-like tag becomes React component in browser
<li key={link.href}>
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href={link.href}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="text-sm text-brand-200 transition-colors hover:text-white"
// Line 108: >
>
// Line 109: {link.label}
{link.label}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</li>
// Line 112: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</ul>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<h3 className="mb-4 text-sm font-semibold uppercase tracking-wider text-gold-400">
// Line 118: {t('footer.company')}
{t('footer.company')}
// JSX element — HTML-like tag becomes React component in browser
</h3>
// JSX element — HTML-like tag becomes React component in browser
<ul className="space-y-2">
// Array.map — transform each item (often render a list of components)
{footerLinks.company.map((link) => (
// JSX element — HTML-like tag becomes React component in browser
<li key={link.href}>
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href={link.href}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="text-sm text-brand-200 transition-colors hover:text-white"
// Line 126: >
>
// Line 127: {link.label}
{link.label}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</li>
// Line 130: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</ul>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<h3 className="mb-4 text-sm font-semibold uppercase tracking-wider text-gold-400">
// Line 136: {t('footer.contact')}
{t('footer.contact')}
// JSX element — HTML-like tag becomes React component in browser
</h3>
// JSX element — HTML-like tag becomes React component in browser
<ul className="space-y-3">
// JSX element — HTML-like tag becomes React component in browser
<li className="flex items-center gap-2 text-sm text-brand-200">
// JSX element — HTML-like tag becomes React component in browser
<Phone className="h-4 w-4 text-gold-400" />
// JSX element — HTML-like tag becomes React component in browser
<a href={`tel:${SITE_PHONE_DISPLAY.replace(/\s/g, '')}`} className="hover:text-white">
// Line 142: {SITE_PHONE_DISPLAY}
{SITE_PHONE_DISPLAY}
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</li>
// JSX element — HTML-like tag becomes React component in browser
<li className="flex items-center gap-2 text-sm text-brand-200">
// JSX element — HTML-like tag becomes React component in browser
<Mail className="h-4 w-4 text-gold-400" />
// JSX element — HTML-like tag becomes React component in browser
<a href={`mailto:${SITE_EMAIL}`} className="hover:text-white">
// Line 148: {SITE_EMAIL}
{SITE_EMAIL}
// JSX element — HTML-like tag becomes React component in browser
</a>
// JSX element — HTML-like tag becomes React component in browser
</li>
// JSX element — HTML-like tag becomes React component in browser
<li className="flex items-start gap-2 text-sm text-brand-200">
// JSX element — HTML-like tag becomes React component in browser
<MapPin className="mt-0.5 h-4 w-4 shrink-0 text-gold-400" />
// JSX element — HTML-like tag becomes React component in browser
<span>
// Line 154: {SITE_ADDRESS}
{SITE_ADDRESS}
// JSX element — HTML-like tag becomes React component in browser
<br />
// Line 156: {t('footer.hours', { hours: SITE_HOURS })}
{t('footer.hours', { hours: SITE_HOURS })}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</li>
// JSX element — HTML-like tag becomes React component in browser
</ul>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="mt-12 flex flex-col items-center justify-between gap-4 border-t border-brand-800 pt-8 sm:flex-row">
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs text-brand-300">
// Line 165: &copy; {new Date().getFullYear()} {t('site.name')}. {t('f...
&copy; {new Date().getFullYear()} {t('site.name')}. {t('footer.rights')}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex gap-6 text-xs text-brand-300">
// JSX element — HTML-like tag becomes React component in browser
<Link href="/about#privacy" className="hover:text-white">
// Line 169: {t('nav.privacy')}
{t('nav.privacy')}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
<Link href="/about#terms" className="hover:text-white">
// Line 172: {t('nav.terms')}
{t('nav.terms')}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</footer>
// Line 178: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,254 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/layout/Header.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';
// Import React — core UI library (components, hooks, JSX)
import { useState } from 'react';
// Lucide icons — lightweight SVG icon components
import { ShoppingCart, Heart, User, Menu, X, Search } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useCartStore } from '@/store/cart';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useWishlistStore } from '@/store/wishlist';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useAuthStore } from '@/store/auth';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// Import project module (@/ alias = src/ folder in tsconfig)
import Logo from '@/components/ui/Logo';
// Import project module (@/ alias = src/ folder in tsconfig)
import { SITE_LOCATION } from '@/lib/constants';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function Header() {
// React useState — local component state that triggers re-render on change
const [mobileOpen, setMobileOpen] = useState(false);
// Zustand selector — subscribe to slice of global store
const cartCount = useCartStore((s) => s.getItemCount());
// Zustand selector — subscribe to slice of global store
const wishlistCount = useWishlistStore((s) => s.items.length);
// Zustand selector — subscribe to slice of global store
const isAuthenticated = useAuthStore((s) => s.isAuthenticated);
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (blank line — separates logical blocks for readability)
// Line 20: const navLinks = [
const navLinks = [
// Property or array item — trailing comma allowed in TypeScript
{ href: '/shop', label: t('nav.shop') },
// Property or array item — trailing comma allowed in TypeScript
{ href: '/shop?category=chicken', label: t('nav.chicken') },
// Property or array item — trailing comma allowed in TypeScript
{ href: '/shop?category=beef', label: t('nav.beef') },
// Property or array item — trailing comma allowed in TypeScript
{ href: '/shop?category=lamb', label: t('nav.lamb') },
// Property or array item — trailing comma allowed in TypeScript
{ href: '/shop?category=fish', label: t('nav.fish') },
// Property or array item — trailing comma allowed in TypeScript
{ href: '/about', label: t('nav.about') },
// Property or array item — trailing comma allowed in TypeScript
{ href: '/about#contact', label: t('nav.contact') },
// End of array literal
];
// (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
<header className="border-b border-cream-300/80 bg-cream-50/95 backdrop-blur-md">
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex h-16 items-center justify-between lg:h-20">
// JSX element — HTML-like tag becomes React component in browser
<Link href="/" className="flex items-center gap-3">
// JSX element — HTML-like tag becomes React component in browser
<Logo size="sm" />
// JSX element — HTML-like tag becomes React component in browser
<div className="hidden sm:block">
// JSX element — HTML-like tag becomes React component in browser
<span className="font-display text-xl font-bold text-brand-800">
// Line 38: {t('site.name')}
{t('site.name')}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-[10px] font-medium uppercase tracking-widest text-brand-600">
// Line 41: {SITE_LOCATION}
{SITE_LOCATION}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<nav className="hidden items-center gap-8 lg:flex">
// Array.map — transform each item (often render a list of components)
{navLinks.map((link) => (
// JSX element — HTML-like tag becomes React component in browser
<Link
// Line 49: key={link.href}
key={link.href}
// Link target URL — internal route or external https://
href={link.href}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="text-sm font-medium text-gray-600 transition-colors hover:text-brand-700"
// Line 52: >
>
// Line 53: {link.label}
{link.label}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// Line 55: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</nav>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center gap-1 sm:gap-2">
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href="/shop?q="
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="hidden rounded-lg p-2 text-gray-500 transition-colors hover:bg-gray-100 hover:text-brand-700 sm:block"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('nav.searchProducts')}
// Line 63: >
>
// JSX element — HTML-like tag becomes React component in browser
<Search className="h-5 w-5" />
// JSX element — HTML-like tag becomes React component in browser
</Link>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href="/wishlist"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="relative rounded-lg p-2 text-gray-500 transition-colors hover:bg-gray-100 hover:text-brand-700"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('nav.wishlist')}
// Line 71: >
>
// JSX element — HTML-like tag becomes React component in browser
<Heart className="h-5 w-5" />
// Line 73: {wishlistCount > 0 && (
{wishlistCount > 0 && (
// JSX element — HTML-like tag becomes React component in browser
<span className="absolute -end-0.5 -top-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-gold-500 text-[10px] font-bold text-white">
// Line 75: {wishlistCount}
{wishlistCount}
// JSX element — HTML-like tag becomes React component in browser
</span>
// Line 77: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href="/cart"
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="relative rounded-lg p-2 text-gray-500 transition-colors hover:bg-gray-100 hover:text-brand-700"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('nav.cart')}
// Line 84: >
>
// JSX element — HTML-like tag becomes React component in browser
<ShoppingCart className="h-5 w-5" />
// Line 86: {cartCount > 0 && (
{cartCount > 0 && (
// JSX element — HTML-like tag becomes React component in browser
<span className="absolute -end-0.5 -top-0.5 flex h-4 w-4 items-center justify-center rounded-full bg-brand-700 text-[10px] font-bold text-white">
// Line 88: {cartCount}
{cartCount}
// JSX element — HTML-like tag becomes React component in browser
</span>
// Line 90: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href={isAuthenticated ? '/account' : '/login'}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="rounded-lg p-2 text-gray-500 transition-colors hover:bg-gray-100 hover:text-brand-700"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('nav.account')}
// Line 97: >
>
// JSX element — HTML-like tag becomes React component in browser
<User className="h-5 w-5" />
// JSX element — HTML-like tag becomes React component in browser
</Link>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<button
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="rounded-lg p-2 text-gray-500 transition-colors hover:bg-gray-100 lg:hidden"
// Click handler — runs when user clicks (must be client component)
onClick={() => setMobileOpen(!mobileOpen)}
// Accessibility attribute — screen readers and assistive tech
aria-label={t('nav.toggleMenu')}
// Line 105: >
>
// Line 106: {mobileOpen ? <X className="h-5 w-5" /> : <Menu className...
{mobileOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
// JSX element — HTML-like tag becomes React component in browser
</button>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// Line 112: {mobileOpen && (
{mobileOpen && (
// JSX element — HTML-like tag becomes React component in browser
<div className="border-t border-gray-100 bg-white lg:hidden">
// JSX element — HTML-like tag becomes React component in browser
<nav className="flex flex-col px-4 py-4">
// Array.map — transform each item (often render a list of components)
{navLinks.map((link) => (
// JSX element — HTML-like tag becomes React component in browser
<Link
// Line 117: key={link.href}
key={link.href}
// Link target URL — internal route or external https://
href={link.href}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="rounded-lg px-4 py-3 text-sm font-medium text-gray-600 transition-colors hover:bg-brand-50 hover:text-brand-700"
// Click handler — runs when user clicks (must be client component)
onClick={() => setMobileOpen(false)}
// Line 121: >
>
// Line 122: {link.label}
{link.label}
// JSX element — HTML-like tag becomes React component in browser
</Link>
// Line 124: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</nav>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 127: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</header>
// Line 129: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,129 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/layout/LanguageBanner.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)
// Lucide icons — lightweight SVG icon components
import { Globe } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { LOCALES, Locale } from '@/i18n/types';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useLocaleStore } from '@/store/locale';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// Import project module (@/ alias = src/ folder in tsconfig)
import { cn } from '@/lib/utils';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function LanguageBanner() {
// Zustand selector — subscribe to slice of global store
const { locale, setLocale } = useLocaleStore();
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (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
<div
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="border-b border-brand-800 bg-brand-950 text-white"
// Line 16: role="navigation"
role="navigation"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('nav.language')}
// Line 18: >
>
// JSX element — HTML-like tag becomes React component in browser
<div className="mx-auto flex max-w-7xl flex-col items-center gap-2 px-4 py-2 sm:flex-row sm:justify-between sm:px-6 lg:px-8">
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center gap-2 text-xs font-medium text-brand-200">
// JSX element — HTML-like tag becomes React component in browser
<Globe className="h-3.5 w-3.5 text-gold-400" aria-hidden />
// JSX element — HTML-like tag becomes React component in browser
<span>{t('languageBanner.choose')}</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="flex w-full max-w-md items-center justify-center gap-1 rounded-full bg-brand-900 p-1 ring-1 ring-brand-800 sm:w-auto sm:max-w-none"
// Line 27: role="group"
role="group"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('nav.language')}
// Line 29: >
>
// Array.map — transform each item (often render a list of components)
{LOCALES.map((lang) => {
// Line 31: const active = locale === lang.code;
const active = locale === lang.code;
// Return JSX — describes UI tree React renders to the DOM
return (
// JSX element — HTML-like tag becomes React component in browser
<button
// Line 34: key={lang.code}
key={lang.code}
// Line 35: type="button"
type="button"
// Click handler — runs when user clicks (must be client component)
onClick={() => setLocale(lang.code as Locale)}
// Accessibility attribute — screen readers and assistive tech
aria-pressed={active}
// Accessibility attribute — screen readers and assistive tech
aria-label={`${lang.label} (${lang.nativeLabel})`}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={cn(
// Property or array item — trailing comma allowed in TypeScript
'flex min-w-0 flex-1 items-center justify-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-semibold transition-all sm:flex-initial sm:px-4 sm:py-2 sm:text-sm',
// Line 41: active
active
// Line 42: ? 'bg-gold-500 text-brand-950 shadow-gold'
? 'bg-gold-500 text-brand-950 shadow-gold'
// Line 43: : 'text-brand-200 hover:bg-brand-800 hover:text-white'
: 'text-brand-200 hover:bg-brand-800 hover:text-white'
// Line 44: )}
)}
// Line 45: >
>
// JSX element — HTML-like tag becomes React component in browser
<span className={lang.code === 'ur' ? 'font-[family-name:var(--font-urdu)]' : ''}>
// Line 47: {lang.nativeLabel}
{lang.nativeLabel}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<span
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={cn(
// Property or array item — trailing comma allowed in TypeScript
'hidden text-[10px] font-normal uppercase tracking-wide sm:inline',
// Line 52: active ? 'text-brand-900/70' : 'text-brand-400'
active ? 'text-brand-900/70' : 'text-brand-400'
// Line 53: )}
)}
// Line 54: >
>
// Line 55: {lang.label}
{lang.label}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
</button>
// Line 58: );
);
// Closing brace — end of block (function, if, object, JSX)
})}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 63: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,135 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/layout/LanguageSwitcher.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, useRef, useEffect } from 'react';
// Lucide icons — lightweight SVG icon components
import { Globe, Check } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { LOCALES, Locale } from '@/i18n/types';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useLocaleStore } from '@/store/locale';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// Import project module (@/ alias = src/ folder in tsconfig)
import { cn } from '@/lib/utils';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function LanguageSwitcher() {
// React useState — local component state that triggers re-render on change
const [open, setOpen] = useState(false);
// Line 12: const ref = useRef<HTMLDivElement>(null);
const ref = useRef<HTMLDivElement>(null);
// Zustand selector — subscribe to slice of global store
const { locale, setLocale } = useLocaleStore();
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (blank line — separates logical blocks for readability)
// Array.find — get first matching item or undefined
const current = LOCALES.find((l) => l.code === locale);
// (blank line — separates logical blocks for readability)
// Side effect hook — runs after paint; deps array controls when it re-runs
useEffect(() => {
// Function declaration — reusable logic in this file
function handleClick(e: MouseEvent) {
// Conditional branch — different behavior based on runtime value
if (ref.current && !ref.current.contains(e.target as Node)) {
// Line 21: setOpen(false);
setOpen(false);
// Closing brace — end of block (function, if, object, JSX)
}
// Closing brace — end of block (function, if, object, JSX)
}
// Line 24: document.addEventListener('mousedown', handleClick);
document.addEventListener('mousedown', handleClick);
// Return JSX — describes UI tree React renders to the DOM
return () => document.removeEventListener('mousedown', handleClick);
// 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
<div ref={ref} className="relative">
// JSX element — HTML-like tag becomes React component in browser
<button
// Click handler — runs when user clicks (must be client component)
onClick={() => setOpen(!open)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="flex items-center gap-1.5 rounded-lg px-2 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 hover:text-brand-700"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('nav.language')}
// Line 34: >
>
// JSX element — HTML-like tag becomes React component in browser
<Globe className="h-4 w-4" />
// JSX element — HTML-like tag becomes React component in browser
<span className="hidden sm:inline">{current?.nativeLabel}</span>
// JSX element — HTML-like tag becomes React component in browser
</button>
// (blank line — separates logical blocks for readability)
// Line 39: {open && (
{open && (
// JSX element — HTML-like tag becomes React component in browser
<div className="absolute end-0 top-full z-50 mt-1 min-w-[10rem] overflow-hidden rounded-xl border border-gray-100 bg-white py-1 shadow-premium-lg">
// Array.map — transform each item (often render a list of components)
{LOCALES.map((lang) => (
// JSX element — HTML-like tag becomes React component in browser
<button
// Line 43: key={lang.code}
key={lang.code}
// Click handler — runs when user clicks (must be client component)
onClick={() => {
// Line 45: setLocale(lang.code as Locale);
setLocale(lang.code as Locale);
// Line 46: setOpen(false);
setOpen(false);
// Closing brace — end of block (function, if, object, JSX)
}}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={cn(
// Property or array item — trailing comma allowed in TypeScript
'flex w-full items-center justify-between gap-3 px-4 py-2.5 text-sm transition-colors hover:bg-brand-50',
// Line 50: locale === lang.code
locale === lang.code
// Line 51: ? 'font-semibold text-brand-700'
? 'font-semibold text-brand-700'
// Line 52: : 'text-gray-600'
: 'text-gray-600'
// Line 53: )}
)}
// Line 54: >
>
// JSX element — HTML-like tag becomes React component in browser
<span>
// Line 56: {lang.nativeLabel}
{lang.nativeLabel}
// JSX element — HTML-like tag becomes React component in browser
<span className="ms-2 text-xs text-gray-400">{lang.label}</span>
// JSX element — HTML-like tag becomes React component in browser
</span>
// Line 59: {locale === lang.code && (
{locale === lang.code && (
// JSX element — HTML-like tag becomes React component in browser
<Check className="h-4 w-4 shrink-0 text-brand-700" />
// Line 61: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</button>
// Line 63: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 65: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 67: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,33 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/layout/LocaleAttributes.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';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useLocaleStore } from '@/store/locale';
// Import project module (@/ alias = src/ folder in tsconfig)
import { getHtmlLang, isRtl } from '@/i18n';
// (blank line — separates logical blocks for readability)
// Default export — main component/page Next.js or other files import
export default function LocaleAttributes() {
// Zustand selector — subscribe to slice of global store
const locale = useLocaleStore((s) => s.locale);
// (blank line — separates logical blocks for readability)
// Side effect hook — runs after paint; deps array controls when it re-runs
useEffect(() => {
// Line 11: document.documentElement.lang = getHtmlLang(locale);
document.documentElement.lang = getHtmlLang(locale);
// Line 12: document.documentElement.dir = isRtl(locale) ? 'rtl' : 'l...
document.documentElement.dir = isRtl(locale) ? 'rtl' : 'ltr';
// Closing brace — end of block (function, if, object, JSX)
}, [locale]);
// (blank line — separates logical blocks for readability)
// Return value from function
return null;
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,202 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/product/CustomizationSelector.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 { Category, ProductCustomization } from '@/types';
// Import project module (@/ alias = src/ folder in tsconfig)
import { CUT_COUNTS, CUTTING_STYLE_KEYS } from '@/lib/constants';
// Import project module (@/ alias = src/ folder in tsconfig)
import { updateMeatCustomization } from '@/lib/customization';
// Import project module (@/ alias = src/ folder in tsconfig)
import { cn } from '@/lib/utils';
// Lucide icons — lightweight SVG icon components
import { Scissors, Hash } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// (blank line — separates logical blocks for readability)
// TypeScript interface — contract for object properties and methods
interface CustomizationSelectorProps {
// Line 11: category: Category;
category: Category;
// Line 12: customization: ProductCustomization;
customization: ProductCustomization;
// Line 13: onChange: (customization: ProductCustomization) => void;
onChange: (customization: ProductCustomization) => void;
// 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 CustomizationSelector({
// Property or array item — trailing comma allowed in TypeScript
category,
// Property or array item — trailing comma allowed in TypeScript
customization,
// Property or array item — trailing comma allowed in TypeScript
onChange,
// Closing brace — end of block (function, if, object, JSX)
}: CustomizationSelectorProps) {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (blank line — separates logical blocks for readability)
// Conditional branch — different behavior based on runtime value
if (category === 'fish') {
// Return JSX — describes UI tree React renders to the DOM
return (
// JSX element — HTML-like tag becomes React component in browser
<div className="rounded-xl border border-brand-100 bg-brand-50/50 p-4">
// JSX element — HTML-like tag becomes React component in browser
<p className="text-sm text-brand-700">{t('product.fishNote')}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 28: );
);
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Line 31: const meat = updateMeatCustomization(customization, categ...
const meat = updateMeatCustomization(customization, category, {});
// Line 32: const selectedCuts = meat.cuts;
const selectedCuts = meat.cuts;
// Line 33: const selectedStyle = meat.cuttingStyle;
const selectedStyle = meat.cuttingStyle;
// (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
<div className="space-y-6">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-3 flex items-center gap-2">
// JSX element — HTML-like tag becomes React component in browser
<Hash className="h-4 w-4 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<h3 className="text-sm font-semibold text-brand-900">
// Line 41: {t('product.howManyCuts')}
{t('product.howManyCuts')}
// JSX element — HTML-like tag becomes React component in browser
</h3>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-wrap gap-2">
// Array.map — transform each item (often render a list of components)
{CUT_COUNTS.map((cuts) => (
// JSX element — HTML-like tag becomes React component in browser
<button
// Line 47: key={cuts}
key={cuts}
// Line 48: type="button"
type="button"
// Click handler — runs when user clicks (must be client component)
onClick={() =>
// Line 50: onChange(updateMeatCustomization(customization, category,...
onChange(updateMeatCustomization(customization, category, { cuts }))
// Closing brace — end of block (function, if, object, JSX)
}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={cn(
// Property or array item — trailing comma allowed in TypeScript
'min-w-[3.5rem] rounded-lg border-2 px-4 py-2.5 text-sm font-semibold transition-all',
// Line 54: selectedCuts === cuts
selectedCuts === cuts
// Line 55: ? 'border-brand-700 bg-brand-700 text-white shadow-premium'
? 'border-brand-700 bg-brand-700 text-white shadow-premium'
// Line 56: : 'border-gray-200 bg-white text-gray-700 hover:border-br...
: 'border-gray-200 bg-white text-gray-700 hover:border-brand-300 hover:bg-brand-50'
// Line 57: )}
)}
// Line 58: >
>
// Line 59: {cuts}
{cuts}
// JSX element — HTML-like tag becomes React component in browser
</button>
// Line 61: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<p className="mt-2 text-xs text-gray-500">{t('product.howManyCutsHint')}</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-3 flex items-center gap-2">
// JSX element — HTML-like tag becomes React component in browser
<Scissors className="h-4 w-4 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<h3 className="text-sm font-semibold text-brand-900">
// Line 70: {t('product.selectCutting')}
{t('product.selectCutting')}
// JSX element — HTML-like tag becomes React component in browser
</h3>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
// Array.map — transform each item (often render a list of components)
{CUTTING_STYLE_KEYS.map((style) => (
// JSX element — HTML-like tag becomes React component in browser
<button
// Line 76: key={style}
key={style}
// Line 77: type="button"
type="button"
// Click handler — runs when user clicks (must be client component)
onClick={() =>
// Line 79: onChange(
onChange(
// Line 80: updateMeatCustomization(customization, category, { cuttin...
updateMeatCustomization(customization, category, { cuttingStyle: style })
// Line 81: )
)
// Closing brace — end of block (function, if, object, JSX)
}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={cn(
// Property or array item — trailing comma allowed in TypeScript
'rounded-lg border-2 px-4 py-3 text-start text-sm font-medium transition-all',
// Line 85: selectedStyle === style
selectedStyle === style
// Line 86: ? 'border-brand-700 bg-brand-700 text-white shadow-premium'
? 'border-brand-700 bg-brand-700 text-white shadow-premium'
// Line 87: : 'border-gray-200 bg-white text-gray-700 hover:border-br...
: 'border-gray-200 bg-white text-gray-700 hover:border-brand-300 hover:bg-brand-50'
// Line 88: )}
)}
// Line 89: >
>
// Line 90: {t(`cutting.${style}`)}
{t(`cutting.${style}`)}
// JSX element — HTML-like tag becomes React component in browser
</button>
// Line 92: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<p className="mt-2 text-xs text-gray-500">
// Line 95: {t('product.selectCuttingHint', {
{t('product.selectCuttingHint', {
// Property or array item — trailing comma allowed in TypeScript
category: t(`categories.${category}.name`),
// Closing brace — end of block (function, if, object, JSX)
})}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 101: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,174 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/product/ProductCard.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 { Heart, ShoppingCart } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { LocalizedProduct } from '@/lib/product-i18n';
// Import project module (@/ alias = src/ folder in tsconfig)
import { formatPrice, getFormatLocale } from '@/lib/utils';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useWishlistStore } from '@/store/wishlist';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// Import project module (@/ alias = src/ folder in tsconfig)
import { cn } from '@/lib/utils';
// Import project module (@/ alias = src/ folder in tsconfig)
import { products } from '@/lib/products';
// (blank line — separates logical blocks for readability)
// TypeScript interface — contract for object properties and methods
interface ProductCardProps {
// Line 14: product: LocalizedProduct;
product: LocalizedProduct;
// 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 ProductCard({ product }: ProductCardProps) {
// Zustand selector — subscribe to slice of global store
const { isInWishlist, toggleItem } = useWishlistStore();
// Custom hook — returns t() translator and current locale
const { t, locale } = useTranslation();
// Array.find — get first matching item or undefined
const rawProduct = products.find((p) => p.id === product.id)!;
// Line 21: const inWishlist = isInWishlist(product.id);
const inWishlist = isInWishlist(product.id);
// (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
<div className="card-premium group overflow-hidden">
// JSX element — HTML-like tag becomes React component in browser
<div className="relative aspect-[4/3] overflow-hidden bg-gray-50">
// JSX element — HTML-like tag becomes React component in browser
<Link href={`/product/${product.slug}`}>
// JSX element — HTML-like tag becomes React component in browser
<AppImage
// Line 28: src={product.image}
src={product.image}
// Line 29: alt={product.name}
alt={product.name}
// Line 30: fill
fill
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="object-cover transition-transform duration-500 group-hover:scale-105"
// Line 32: sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw...
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 320px"
// Line 33: />
/>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// Line 35: {product.badge && (
{product.badge && (
// JSX element — HTML-like tag becomes React component in browser
<span className="absolute start-3 top-3 rounded-full bg-gold-500 px-3 py-1 text-xs font-semibold text-white shadow-gold">
// Line 37: {product.badge}
{product.badge}
// JSX element — HTML-like tag becomes React component in browser
</span>
// Line 39: )}
)}
// JSX element — HTML-like tag becomes React component in browser
<button
// Click handler — runs when user clicks (must be client component)
onClick={() => toggleItem(rawProduct)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={cn(
// Property or array item — trailing comma allowed in TypeScript
'absolute end-3 top-3 flex h-9 w-9 items-center justify-center rounded-full bg-white/90 shadow-sm backdrop-blur transition-all hover:scale-110',
// Line 44: inWishlist && 'text-red-500'
inWishlist && 'text-red-500'
// Line 45: )}
)}
// Accessibility attribute — screen readers and assistive tech
aria-label={inWishlist ? t('product.removeWishlist') : t('product.addWishlist')}
// Line 47: >
>
// JSX element — HTML-like tag becomes React component in browser
<Heart className={cn('h-4 w-4', inWishlist && 'fill-current')} />
// JSX element — HTML-like tag becomes React component in browser
</button>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="p-5">
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-1 flex items-center gap-2">
// JSX element — HTML-like tag becomes React component in browser
<span className="text-xs font-medium uppercase tracking-wider text-brand-600">
// Line 55: {t(`categories.${product.category}.name`)}
{t(`categories.${product.category}.name`)}
// JSX element — HTML-like tag becomes React component in browser
</span>
// Line 57: {product.weight && (
{product.weight && (
// JSX element — HTML-like tag becomes React component in browser
<span className="text-xs text-gray-400">· {product.weight}</span>
// Line 59: )}
)}
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Link href={`/product/${product.slug}`}>
// JSX element — HTML-like tag becomes React component in browser
<h3 className="mb-1 font-display text-lg font-semibold text-brand-900 transition-colors group-hover:text-brand-700">
// Line 64: {product.name}
{product.name}
// JSX element — HTML-like tag becomes React component in browser
</h3>
// JSX element — HTML-like tag becomes React component in browser
</Link>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<p className="mb-4 line-clamp-2 text-sm text-gray-500">{product.description}</p>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div className="flex items-center justify-between">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<span className="text-lg font-bold text-brand-800">
// Line 73: {formatPrice(product.price, getFormatLocale(locale))}
{formatPrice(product.price, getFormatLocale(locale))}
// JSX element — HTML-like tag becomes React component in browser
</span>
// JSX element — HTML-like tag becomes React component in browser
<span className="ms-1 text-xs text-gray-400">{product.priceUnit}</span>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<Link
// Link target URL — internal route or external https://
href={`/product/${product.slug}`}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="flex h-10 w-10 items-center justify-center rounded-full bg-brand-700 text-white transition-all hover:bg-brand-800 hover:shadow-premium"
// Accessibility attribute — screen readers and assistive tech
aria-label={t('product.viewProduct', { name: product.name })}
// Line 82: >
>
// JSX element — HTML-like tag becomes React component in browser
<ShoppingCart className="h-4 w-4" />
// JSX element — HTML-like tag becomes React component in browser
</Link>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 88: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,214 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/shop/ShopFilters.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 { Category, SortOption } from '@/types';
// Import project module (@/ alias = src/ folder in tsconfig)
import { cn } from '@/lib/utils';
// Lucide icons — lightweight SVG icon components
import { SlidersHorizontal } from 'lucide-react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useTranslation } from '@/hooks/useTranslation';
// (blank line — separates logical blocks for readability)
// TypeScript interface — contract for object properties and methods
interface ShopFiltersProps {
// Line 9: selectedCategory: Category | 'all';
selectedCategory: Category | 'all';
// Line 10: onCategoryChange: (category: Category | 'all') => void;
onCategoryChange: (category: Category | 'all') => void;
// Line 11: sortBy: SortOption;
sortBy: SortOption;
// Line 12: onSortChange: (sort: SortOption) => void;
onSortChange: (sort: SortOption) => void;
// Line 13: searchQuery: string;
searchQuery: string;
// Line 14: onSearchChange: (query: string) => void;
onSearchChange: (query: string) => void;
// Line 15: totalResults: number;
totalResults: number;
// 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 ShopFilters({
// Property or array item — trailing comma allowed in TypeScript
selectedCategory,
// Property or array item — trailing comma allowed in TypeScript
onCategoryChange,
// Property or array item — trailing comma allowed in TypeScript
sortBy,
// Property or array item — trailing comma allowed in TypeScript
onSortChange,
// Property or array item — trailing comma allowed in TypeScript
searchQuery,
// Property or array item — trailing comma allowed in TypeScript
onSearchChange,
// Property or array item — trailing comma allowed in TypeScript
totalResults,
// Closing brace — end of block (function, if, object, JSX)
}: ShopFiltersProps) {
// Custom hook — returns t() translator and current locale
const { t } = useTranslation();
// (blank line — separates logical blocks for readability)
// Line 29: const categories: { value: Category | 'all'; label: strin...
const categories: { value: Category | 'all'; label: string }[] = [
// Property or array item — trailing comma allowed in TypeScript
{ value: 'all', label: t('shop.all') },
// Property or array item — trailing comma allowed in TypeScript
{ value: 'chicken', label: t('nav.chicken') },
// Property or array item — trailing comma allowed in TypeScript
{ value: 'beef', label: t('nav.beef') },
// Property or array item — trailing comma allowed in TypeScript
{ value: 'lamb', label: t('nav.lamb') },
// Property or array item — trailing comma allowed in TypeScript
{ value: 'fish', label: t('nav.fish') },
// End of array literal
];
// (blank line — separates logical blocks for readability)
// Line 37: const sortOptions: { value: SortOption; label: string }[]...
const sortOptions: { value: SortOption; label: string }[] = [
// Property or array item — trailing comma allowed in TypeScript
{ value: 'featured', label: t('shop.sortFeatured') },
// Property or array item — trailing comma allowed in TypeScript
{ value: 'price-asc', label: t('shop.sortPriceAsc') },
// Property or array item — trailing comma allowed in TypeScript
{ value: 'price-desc', label: t('shop.sortPriceDesc') },
// Property or array item — trailing comma allowed in TypeScript
{ value: 'name', label: t('shop.sortName') },
// End of array literal
];
// (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
<div className="space-y-6">
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<div className="mb-3 flex items-center gap-2">
// JSX element — HTML-like tag becomes React component in browser
<SlidersHorizontal className="h-4 w-4 text-brand-700" />
// JSX element — HTML-like tag becomes React component in browser
<h2 className="text-sm font-semibold text-brand-900">{t('shop.filters')}</h2>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
<p className="text-xs text-gray-500">
// Line 52: {t('shop.productsFound', { count: totalResults })}
{t('shop.productsFound', { count: totalResults })}
// JSX element — HTML-like tag becomes React component in browser
</p>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label htmlFor="search" className="label-text">
// Line 58: {t('shop.search')}
{t('shop.search')}
// JSX element — HTML-like tag becomes React component in browser
</label>
// JSX element — HTML-like tag becomes React component in browser
<input
// Line 61: id="search"
id="search"
// Line 62: type="text"
type="text"
// Line 63: placeholder={t('shop.searchPlaceholder')}
placeholder={t('shop.searchPlaceholder')}
// Line 64: value={searchQuery}
value={searchQuery}
// Change handler — runs when input/select value changes
onChange={(e) => onSearchChange(e.target.value)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 67: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<p className="label-text">{t('shop.category')}</p>
// JSX element — HTML-like tag becomes React component in browser
<div className="flex flex-wrap gap-2">
// Array.map — transform each item (often render a list of components)
{categories.map((cat) => (
// JSX element — HTML-like tag becomes React component in browser
<button
// Line 75: key={cat.value}
key={cat.value}
// Click handler — runs when user clicks (must be client component)
onClick={() => onCategoryChange(cat.value)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={cn(
// Property or array item — trailing comma allowed in TypeScript
'rounded-full px-4 py-2 text-sm font-medium transition-all',
// Line 79: selectedCategory === cat.value
selectedCategory === cat.value
// Line 80: ? 'bg-brand-700 text-white shadow-premium'
? 'bg-brand-700 text-white shadow-premium'
// Line 81: : 'bg-gray-100 text-gray-600 hover:bg-brand-50 hover:text...
: 'bg-gray-100 text-gray-600 hover:bg-brand-50 hover:text-brand-700'
// Line 82: )}
)}
// Line 83: >
>
// Line 84: {cat.label}
{cat.label}
// JSX element — HTML-like tag becomes React component in browser
</button>
// Line 86: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// (blank line — separates logical blocks for readability)
// JSX element — HTML-like tag becomes React component in browser
<div>
// JSX element — HTML-like tag becomes React component in browser
<label htmlFor="sort" className="label-text">
// Line 92: {t('shop.sortBy')}
{t('shop.sortBy')}
// JSX element — HTML-like tag becomes React component in browser
</label>
// JSX element — HTML-like tag becomes React component in browser
<select
// Line 95: id="sort"
id="sort"
// Line 96: value={sortBy}
value={sortBy}
// Change handler — runs when input/select value changes
onChange={(e) => onSortChange(e.target.value as SortOption)}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="input-field"
// Line 99: >
>
// Array.map — transform each item (often render a list of components)
{sortOptions.map((opt) => (
// JSX element — HTML-like tag becomes React component in browser
<option key={opt.value} value={opt.value}>
// Line 102: {opt.label}
{opt.label}
// JSX element — HTML-like tag becomes React component in browser
</option>
// Line 104: ))}
))}
// JSX element — HTML-like tag becomes React component in browser
</select>
// JSX element — HTML-like tag becomes React component in browser
</div>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 108: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,25 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/ui/AppImage.tsx
* NOT used by the app — read this to learn how the real file works
*/
// Next.js Image — optimized images (lazy load, WebP/AVIF)
import Image, { ImageProps } from 'next/image';
// Import project module (@/ alias = src/ folder in tsconfig)
import { IMAGE_QUALITY } from '@/lib/images';
// (blank line — separates logical blocks for readability)
// TypeScript type alias — union or shorthand for complex types
type AppImageProps = ImageProps & {
// Line 5: quality?: number;
quality?: number;
// Closing brace — end of block (function, if, object, JSX)
};
// (blank line — separates logical blocks for readability)
// Block comment — documents the file or function below
/** Site-wide Image wrapper with consistent high quality defaults */
// Default export — main component/page Next.js or other files import
export default function AppImage({ quality = IMAGE_QUALITY, ...props }: AppImageProps) {
// Return value from function
return <Image quality={quality} {...props} />;
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,76 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/ui/Button.tsx
* NOT used by the app — read this to learn how the real file works
*/
// Import project module (@/ alias = src/ folder in tsconfig)
import { cn } from '@/lib/utils';
// Import React — core UI library (components, hooks, JSX)
import { ButtonHTMLAttributes, forwardRef } from 'react';
// (blank line — separates logical blocks for readability)
// TypeScript interface — contract for object properties and methods
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
// Line 5: variant?: 'primary' | 'secondary' | 'gold' | 'ghost';
variant?: 'primary' | 'secondary' | 'gold' | 'ghost';
// Line 6: size?: 'sm' | 'md' | 'lg';
size?: 'sm' | 'md' | 'lg';
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Line 9: const Button = forwardRef<HTMLButtonElement, ButtonProps>(
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
// Line 10: ({ className, variant = 'primary', size = 'md', children,...
({ className, variant = 'primary', size = 'md', children, ...props }, ref) => {
// Line 11: const variants = {
const variants = {
// Property or array item — trailing comma allowed in TypeScript
primary: 'btn-primary',
// Property or array item — trailing comma allowed in TypeScript
secondary: 'btn-secondary',
// Property or array item — trailing comma allowed in TypeScript
gold: 'btn-gold',
// Line 15: ghost:
ghost:
// Property or array item — trailing comma allowed in TypeScript
'inline-flex items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 hover:text-brand-700',
// Closing brace — end of block (function, if, object, JSX)
};
// (blank line — separates logical blocks for readability)
// Line 19: const sizes = {
const sizes = {
// Property or array item — trailing comma allowed in TypeScript
sm: 'px-4 py-2 text-xs',
// Property or array item — trailing comma allowed in TypeScript
md: '',
// Property or array item — trailing comma allowed in TypeScript
lg: 'px-8 py-4 text-base',
// 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
<button
// Line 27: ref={ref}
ref={ref}
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={cn(variants[variant], sizes[size], className)}
// Line 29: {...props}
{...props}
// Line 30: >
>
// Line 31: {children}
{children}
// JSX element — HTML-like tag becomes React component in browser
</button>
// Line 33: );
);
// Closing brace — end of block (function, if, object, JSX)
}
// Line 35: );
);
// (blank line — separates logical blocks for readability)
// Line 37: Button.displayName = 'Button';
Button.displayName = 'Button';
// Line 38: export default Button;
export default Button;
@@ -0,0 +1,74 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/components/ui/Logo.tsx
* NOT used by the app — read this to learn how the real file works
*/
// Import project module (@/ alias = src/ folder in tsconfig)
import AppImage from '@/components/ui/AppImage';
// Import project module (@/ alias = src/ folder in tsconfig)
import { IMAGES } from '@/lib/images';
// Import project module (@/ alias = src/ folder in tsconfig)
import { cn } from '@/lib/utils';
// (blank line — separates logical blocks for readability)
// TypeScript interface — contract for object properties and methods
interface LogoProps {
// Line 6: size?: 'sm' | 'md' | 'lg';
size?: 'sm' | 'md' | 'lg';
// Line 7: className?: string;
className?: string;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Line 10: const sizes = {
const sizes = {
// Property or array item — trailing comma allowed in TypeScript
sm: 'h-10 w-10',
// Property or array item — trailing comma allowed in TypeScript
md: 'h-12 w-12',
// Property or array item — trailing comma allowed in TypeScript
lg: 'h-20 w-20',
// 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 Logo({ size = 'md', className }: LogoProps) {
// Return JSX — describes UI tree React renders to the DOM
return (
// JSX element — HTML-like tag becomes React component in browser
<div
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className={cn(
// Property or array item — trailing comma allowed in TypeScript
'relative shrink-0 overflow-hidden rounded-full ring-2 ring-cream-200',
// Property or array item — trailing comma allowed in TypeScript
sizes[size],
// Line 22: className
className
// Line 23: )}
)}
// Line 24: >
>
// JSX element — HTML-like tag becomes React component in browser
<AppImage
// Line 26: src={IMAGES.logo}
src={IMAGES.logo}
// Line 27: alt="Kött Gård"
alt="Kött Gård"
// Line 28: fill
fill
// Tailwind CSS utility classes — styling (colors, spacing, layout)
className="object-cover"
// Line 30: sizes="(max-width: 640px) 40px, 80px"
sizes="(max-width: 640px) 40px, 80px"
// Line 31: priority
priority
// Line 32: quality={95}
quality={95}
// Line 33: />
/>
// JSX element — HTML-like tag becomes React component in browser
</div>
// Line 35: );
);
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,27 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/hooks/useTranslation.ts
* 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 { useMemo } from 'react';
// Import project module (@/ alias = src/ folder in tsconfig)
import { createTranslator } from '@/i18n';
// Import project module (@/ alias = src/ folder in tsconfig)
import { useLocaleStore } from '@/store/locale';
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function useTranslation() {
// Zustand selector — subscribe to slice of global store
const locale = useLocaleStore((s) => s.locale);
// (blank line — separates logical blocks for readability)
// React useMemo — cache expensive computed value until dependencies change
const t = useMemo(() => createTranslator(locale), [locale]);
// (blank line — separates logical blocks for readability)
// Return value from function
return { t, locale };
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,85 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/i18n/index.ts
* NOT used by the app — read this to learn how the real file works
*/
// Import from a relative file in the same project
import { Locale, TranslationDict } from './types';
// Import from a relative file in the same project
import { en } from './locales/en';
// Import from a relative file in the same project
import { sv } from './locales/sv';
// Import from a relative file in the same project
import { ur } from './locales/ur';
// (blank line — separates logical blocks for readability)
// Line 6: const dictionaries: Record<Locale, TranslationDict> = { e...
const dictionaries: Record<Locale, TranslationDict> = { en, sv, ur };
// (blank line — separates logical blocks for readability)
// Function declaration — reusable logic in this file
function resolve(obj: TranslationDict, path: string): string {
// Line 9: const keys = path.split('.');
const keys = path.split('.');
// Line 10: let current: string | TranslationDict = obj;
let current: string | TranslationDict = obj;
// (blank line — separates logical blocks for readability)
// Line 12: for (const key of keys) {
for (const key of keys) {
// Conditional branch — different behavior based on runtime value
if (typeof current !== 'object' || current === null || !(key in current)) {
// Return value from function
return path;
// Closing brace — end of block (function, if, object, JSX)
}
// Line 16: current = current[key];
current = current[key];
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Return value from function
return typeof current === 'string' ? current : path;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function createTranslator(locale: Locale) {
// Line 23: const dict = dictionaries[locale] ?? dictionaries.en;
const dict = dictionaries[locale] ?? dictionaries.en;
// (blank line — separates logical blocks for readability)
// Return value from function
return function t(path: string, params?: Record<string, string | number>): string {
// Line 26: let text = resolve(dict, path);
let text = resolve(dict, path);
// (blank line — separates logical blocks for readability)
// Conditional branch — different behavior based on runtime value
if (params) {
// Line 29: Object.entries(params).forEach(([key, value]) => {
Object.entries(params).forEach(([key, value]) => {
// Line 30: text = text.replace(new RegExp(`\\{${key}\\}`, 'g'), Stri...
text = text.replace(new RegExp(`\\{${key}\\}`, 'g'), String(value));
// 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)
// Return value from function
return text;
// 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)
// Named export — utility function other files can import
export function isRtl(locale: Locale): boolean {
// Return value from function
return locale === 'ur';
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function getHtmlLang(locale: Locale): string {
// Line 43: const map: Record<Locale, string> = { en: 'en', sv: 'sv',...
const map: Record<Locale, string> = { en: 'en', sv: 'sv', ur: 'ur' };
// Return value from function
return map[locale];
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,934 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/i18n/locales/en.ts
* NOT used by the app — read this to learn how the real file works
*/
// Import external package or local module
import { TranslationDict } from '../types';
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const en: TranslationDict = {
// Line 4: site: {
site: {
// Property or array item — trailing comma allowed in TypeScript
name: 'Kött Gård',
// Property or array item — trailing comma allowed in TypeScript
tagline: 'Premium Halal',
// Line 7: description:
description:
// Property or array item — trailing comma allowed in TypeScript
'Premium 100% Halal meat delivery. Fresh and frozen chicken, beef, lamb, and fish — customized to your preference and delivered to your door.',
// Property or array item — trailing comma allowed in TypeScript
metaTitle: 'Premium Halal Meat Delivery',
// Property or array item — trailing comma allowed in TypeScript
initials: 'KG',
// Property or array item — trailing comma allowed in TypeScript
email: 'hello@kottgard.se',
// Property or array item — trailing comma allowed in TypeScript
demoEmail: 'demo@kottgard.se',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 14: nav: {
nav: {
// Property or array item — trailing comma allowed in TypeScript
shop: 'Shop',
// Property or array item — trailing comma allowed in TypeScript
chicken: 'Chicken',
// Property or array item — trailing comma allowed in TypeScript
beef: 'Beef',
// Property or array item — trailing comma allowed in TypeScript
lamb: 'Lamb',
// Property or array item — trailing comma allowed in TypeScript
fish: 'Fish',
// Property or array item — trailing comma allowed in TypeScript
about: 'About Us',
// Property or array item — trailing comma allowed in TypeScript
halalCert: 'Halal Certification',
// Property or array item — trailing comma allowed in TypeScript
delivery: 'Delivery Info',
// Property or array item — trailing comma allowed in TypeScript
contact: 'Contact',
// Property or array item — trailing comma allowed in TypeScript
myAccount: 'My Account',
// Property or array item — trailing comma allowed in TypeScript
orderHistory: 'Order History',
// Property or array item — trailing comma allowed in TypeScript
wishlist: 'Wishlist',
// Property or array item — trailing comma allowed in TypeScript
cart: 'Cart',
// Property or array item — trailing comma allowed in TypeScript
privacy: 'Privacy Policy',
// Property or array item — trailing comma allowed in TypeScript
terms: 'Terms of Service',
// Property or array item — trailing comma allowed in TypeScript
searchProducts: 'Search products',
// Property or array item — trailing comma allowed in TypeScript
toggleMenu: 'Toggle menu',
// Property or array item — trailing comma allowed in TypeScript
account: 'Account',
// Property or array item — trailing comma allowed in TypeScript
language: 'Language',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 35: languageBanner: {
languageBanner: {
// Property or array item — trailing comma allowed in TypeScript
choose: 'Choose your language',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 38: hero: {
hero: {
// Property or array item — trailing comma allowed in TypeScript
badge: '100% Halal Certified',
// Property or array item — trailing comma allowed in TypeScript
taglineShort: 'Naturally Pure',
// Property or array item — trailing comma allowed in TypeScript
title: 'Premium Halal Meat',
// Property or array item — trailing comma allowed in TypeScript
titleHighlight: '',
// Property or array item — trailing comma allowed in TypeScript
titleEnd: '',
// Property or array item — trailing comma allowed in TypeScript
subtitleShort: 'Fresh. Quality. Reliable.',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'Halal certified · Fresh daily · Home delivery · Open every day',
// Property or array item — trailing comma allowed in TypeScript
hours: 'Open every day {hours}',
// Property or array item — trailing comma allowed in TypeScript
location: 'Tingvallavägen 11, Märsta',
// Property or array item — trailing comma allowed in TypeScript
shopNow: 'Browse Our Selection',
// Property or array item — trailing comma allowed in TypeScript
browseChicken: 'Browse Chicken',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'Order via WhatsApp',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 53: aboutPreview: {
aboutPreview: {
// Property or array item — trailing comma allowed in TypeScript
label: 'About Us',
// Property or array item — trailing comma allowed in TypeScript
title: "Märsta's Finest Butcher Shop",
// Property or array item — trailing comma allowed in TypeScript
p1: 'Kött Gård is more than a butcher shop — we are a promise of quality. All our meat is 100% Halal certified and delivered fresh every day.',
// Property or array item — trailing comma allowed in TypeScript
p2: 'We source lamb from Ireland and New Zealand, chicken and beef from reputable producers, and help you find the right cut for dinner, celebrations, or Sunday roast.',
// Property or array item — trailing comma allowed in TypeScript
p3: 'Visit us at Tingvallavägen, tell us what you are looking for — we cut and pack to your specifications.',
// Property or array item — trailing comma allowed in TypeScript
statHalal: '100%',
// Property or array item — trailing comma allowed in TypeScript
statHalalLabel: 'Halal certified',
// Property or array item — trailing comma allowed in TypeScript
statDays: '7 days',
// Property or array item — trailing comma allowed in TypeScript
statDaysLabel: 'Open weekly',
// Property or array item — trailing comma allowed in TypeScript
statDelivery: 'Daily',
// Property or array item — trailing comma allowed in TypeScript
statDeliveryLabel: 'Delivery',
// Property or array item — trailing comma allowed in TypeScript
statFresh: 'Fresh',
// Property or array item — trailing comma allowed in TypeScript
statFreshLabel: 'Every day',
// Property or array item — trailing comma allowed in TypeScript
readMore: 'Read more about us',
// Property or array item — trailing comma allowed in TypeScript
imageAlt: 'Fresh meat cuts on a cutting board from Kött Gård',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 70: trust: {
trust: {
// Property or array item — trailing comma allowed in TypeScript
halal: '100% Halal',
// Property or array item — trailing comma allowed in TypeScript
halalDesc: 'Certified halal sourcing with full traceability and compliance.',
// Property or array item — trailing comma allowed in TypeScript
fresh: 'Fresh Daily',
// Property or array item — trailing comma allowed in TypeScript
freshDesc: 'Sourced fresh every morning and delivered at peak quality.',
// Property or array item — trailing comma allowed in TypeScript
premium: 'Premium Quality',
// Property or array item — trailing comma allowed in TypeScript
premiumDesc: 'Hand-selected cuts from trusted farms, prepared by expert butchers.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 78: categories: {
categories: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Our Selection',
// Property or array item — trailing comma allowed in TypeScript
subtitle: 'Hand-picked meat — every day. Fresh delivery. Halal. Cut to order.',
// Property or array item — trailing comma allowed in TypeScript
shop: 'Shop {name}',
// Line 82: chicken: {
chicken: {
// Property or array item — trailing comma allowed in TypeScript
name: 'Chicken',
// Property or array item — trailing comma allowed in TypeScript
description: 'Breast fillet, wings, drumsticks and whole chicken. Fresh every morning.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 86: beef: {
beef: {
// Property or array item — trailing comma allowed in TypeScript
name: 'Beef & Veal',
// Property or array item — trailing comma allowed in TypeScript
description: 'Mince, bone marrow and premium cuts. High marbling, consistent quality.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 90: lamb: {
lamb: {
// Property or array item — trailing comma allowed in TypeScript
name: 'Lamb',
// Property or array item — trailing comma allowed in TypeScript
description: 'Shoulder, neck, roast and rack. From Ireland and New Zealand.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 94: fish: {
fish: {
// Property or array item — trailing comma allowed in TypeScript
name: 'Fish',
// Property or array item — trailing comma allowed in TypeScript
description: 'Fresh catch, cleaned and ready to cook.',
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
},
// Line 99: featured: {
featured: {
// Property or array item — trailing comma allowed in TypeScript
label: 'Curated Selection',
// Property or array item — trailing comma allowed in TypeScript
title: 'Featured Products',
// Property or array item — trailing comma allowed in TypeScript
subtitle: 'Our most popular cuts, loved by families across the city.',
// Property or array item — trailing comma allowed in TypeScript
viewAll: 'View All',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 105: howItWorks: {
howItWorks: {
// Property or array item — trailing comma allowed in TypeScript
label: 'Order',
// Property or array item — trailing comma allowed in TypeScript
title: 'How easy it is to order',
// Property or array item — trailing comma allowed in TypeScript
step1Title: 'Contact us',
// Line 109: step1Desc:
step1Desc:
// Property or array item — trailing comma allowed in TypeScript
'Send us a WhatsApp message with what you want — we reply quickly.',
// Property or array item — trailing comma allowed in TypeScript
step2Title: 'We confirm',
// Line 112: step2Desc:
step2Desc:
// Property or array item — trailing comma allowed in TypeScript
'We confirm your order, give you the price, and tell you when it is ready.',
// Property or array item — trailing comma allowed in TypeScript
step3Title: 'Pick up or delivery',
// Line 115: step3Desc:
step3Desc:
// Property or array item — trailing comma allowed in TypeScript
'Pick up in store at Tingvallavägen 11 or choose home delivery.',
// Property or array item — trailing comma allowed in TypeScript
step: 'Step {n}',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'Order on WhatsApp',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 120: offers: {
offers: {
// Property or array item — trailing comma allowed in TypeScript
label: 'Order',
// Property or array item — trailing comma allowed in TypeScript
title: 'Weekly offers',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'We update regularly with fresh deals. Follow us on social media for the latest prices.',
// Property or array item — trailing comma allowed in TypeScript
disclaimer: 'Price valid while supplies last',
// Property or array item — trailing comma allowed in TypeScript
was: 'Was',
// Property or array item — trailing comma allowed in TypeScript
now: 'NOW',
// Property or array item — trailing comma allowed in TypeScript
order: 'Order',
// Property or array item — trailing comma allowed in TypeScript
viewProduct: 'View product',
// Line 130: badge: {
badge: {
// Property or array item — trailing comma allowed in TypeScript
fresh: 'FRESH',
// Property or array item — trailing comma allowed in TypeScript
halal: 'HALAL',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 134: items: {
items: {
// Property or array item — trailing comma allowed in TypeScript
chickenWings: { name: 'Fresh chicken wings PL' },
// Property or array item — trailing comma allowed in TypeScript
lambSteak: { name: 'Fresh lamb roast Ireland' },
// Property or array item — trailing comma allowed in TypeScript
beefMince: { name: 'Beef mince 5% fat IRL' },
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
},
// Line 140: social: {
social: {
// Property or array item — trailing comma allowed in TypeScript
label: 'Follow us',
// Property or array item — trailing comma allowed in TypeScript
title: 'Follow us',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'1,100+ followers on Facebook · 249 posts · Daily updates',
// Property or array item — trailing comma allowed in TypeScript
facebook: 'Facebook',
// Property or array item — trailing comma allowed in TypeScript
instagram: 'Instagram',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'WhatsApp',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 149: contact: {
contact: {
// Property or array item — trailing comma allowed in TypeScript
label: 'Contact',
// Property or array item — trailing comma allowed in TypeScript
title: 'Visit us',
// Property or array item — trailing comma allowed in TypeScript
addressLabel: 'Address',
// Property or array item — trailing comma allowed in TypeScript
phoneLabel: 'Phone',
// Property or array item — trailing comma allowed in TypeScript
hoursLabel: 'Opening hours',
// Property or array item — trailing comma allowed in TypeScript
hoursValue: 'Every day: {hours}',
// Property or array item — trailing comma allowed in TypeScript
writeUs: 'Message us',
// Property or array item — trailing comma allowed in TypeScript
callUs: 'Call us',
// Property or array item — trailing comma allowed in TypeScript
openMaps: 'Open in Google Maps',
// Property or array item — trailing comma allowed in TypeScript
learnMore: 'Contact details',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 161: cta: {
cta: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Ready for Premium Halal Meat?',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'Order today and experience the difference of truly fresh, customized halal meat delivered to your doorstep.',
// Property or array item — trailing comma allowed in TypeScript
button: 'Browse Our Selection',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'Order on WhatsApp',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 168: shop: {
shop: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Shop All Products',
// Property or array item — trailing comma allowed in TypeScript
subtitle: 'Premium halal meat, customized to your preference',
// Property or array item — trailing comma allowed in TypeScript
noProducts: 'No products found',
// Property or array item — trailing comma allowed in TypeScript
noProductsHint: 'Try adjusting your filters or search query',
// Property or array item — trailing comma allowed in TypeScript
filters: 'Filters',
// Property or array item — trailing comma allowed in TypeScript
productsFound: '{count} products found',
// Property or array item — trailing comma allowed in TypeScript
search: 'Search',
// Property or array item — trailing comma allowed in TypeScript
searchPlaceholder: 'Search products...',
// Property or array item — trailing comma allowed in TypeScript
category: 'Category',
// Property or array item — trailing comma allowed in TypeScript
sortBy: 'Sort By',
// Property or array item — trailing comma allowed in TypeScript
all: 'All',
// Property or array item — trailing comma allowed in TypeScript
sortFeatured: 'Featured',
// Property or array item — trailing comma allowed in TypeScript
sortPriceAsc: 'Price: Low to High',
// Property or array item — trailing comma allowed in TypeScript
sortPriceDesc: 'Price: High to Low',
// Property or array item — trailing comma allowed in TypeScript
sortName: 'Name AZ',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 185: product: {
product: {
// Property or array item — trailing comma allowed in TypeScript
backToShop: 'Back to Shop',
// Property or array item — trailing comma allowed in TypeScript
inStock: 'In Stock',
// Property or array item — trailing comma allowed in TypeScript
outOfStock: 'Out of Stock',
// Property or array item — trailing comma allowed in TypeScript
halalTrust: '100% Halal certified · Fresh daily · Premium quality',
// Property or array item — trailing comma allowed in TypeScript
yourSelection: 'Your selection',
// Property or array item — trailing comma allowed in TypeScript
aboutProduct: 'About This Product',
// Property or array item — trailing comma allowed in TypeScript
addToCart: 'Add to Cart',
// Property or array item — trailing comma allowed in TypeScript
addedToCart: 'Added to Cart',
// Property or array item — trailing comma allowed in TypeScript
decreaseQty: 'Decrease quantity',
// Property or array item — trailing comma allowed in TypeScript
increaseQty: 'Increase quantity',
// Property or array item — trailing comma allowed in TypeScript
removeWishlist: 'Remove from wishlist',
// Property or array item — trailing comma allowed in TypeScript
addWishlist: 'Add to wishlist',
// Property or array item — trailing comma allowed in TypeScript
viewProduct: 'View {name}',
// Property or array item — trailing comma allowed in TypeScript
pieces: '{count} pieces',
// Property or array item — trailing comma allowed in TypeScript
standardCut: 'Standard cut',
// Property or array item — trailing comma allowed in TypeScript
howManyCuts: 'How Many Cuts Do You Want?',
// Property or array item — trailing comma allowed in TypeScript
howManyCutsHint: 'Select the number of cuts for your order',
// Property or array item — trailing comma allowed in TypeScript
cutsAndStyle: '{cuts} cuts · {style}',
// Property or array item — trailing comma allowed in TypeScript
selectCutting: 'Select Cutting Style',
// Property or array item — trailing comma allowed in TypeScript
selectCuttingHint: 'Our butchers will prepare your {category} exactly to your preferred cut',
// Line 206: fishNote:
fishNote:
// Property or array item — trailing comma allowed in TypeScript
'Fish products are prepared with our standard professional cut — cleaned, scaled, and ready to cook.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 209: cutting: {
cutting: {
// Property or array item — trailing comma allowed in TypeScript
nihari: 'Nihari cut',
// Property or array item — trailing comma allowed in TypeScript
karahi: 'Karahi cut',
// Property or array item — trailing comma allowed in TypeScript
qeema: 'Qeema (minced)',
// Property or array item — trailing comma allowed in TypeScript
boneless: 'Boneless',
// Property or array item — trailing comma allowed in TypeScript
steak: 'Steak cut',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 216: priceUnit: {
priceUnit: {
// Property or array item — trailing comma allowed in TypeScript
perBird: 'per bird',
// Property or array item — trailing comma allowed in TypeScript
perPack: 'per pack',
// Property or array item — trailing comma allowed in TypeScript
perKg: 'per kg',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 221: badges: {
badges: {
// Property or array item — trailing comma allowed in TypeScript
bestseller: 'Bestseller',
// Property or array item — trailing comma allowed in TypeScript
chefsPick: "Chef's Pick",
// Property or array item — trailing comma allowed in TypeScript
premium: 'Premium',
// Property or array item — trailing comma allowed in TypeScript
popular: 'Popular',
// Property or array item — trailing comma allowed in TypeScript
freshCatch: 'Fresh Catch',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 228: products: {
products: {
// Line 229: 'chicken-whole': {
'chicken-whole': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Whole Chicken',
// Property or array item — trailing comma allowed in TypeScript
description: 'Farm-fresh whole halal chicken, perfect for roasting or curry.',
// Line 232: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Our whole chickens are sourced from certified halal farms and delivered at peak freshness. Each bird is hand-selected for quality, with tender meat and clean processing. Choose your preferred piece count and we will prepare it exactly how you need.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 235: 'chicken-breast': {
'chicken-breast': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Chicken Breast',
// Property or array item — trailing comma allowed in TypeScript
description: 'Lean, boneless chicken breast — ideal for grilling and healthy meals.',
// Line 238: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Premium boneless chicken breast, trimmed and ready to cook. Perfect for kebabs, stir-fries, and healthy weeknight dinners. Select your piece count and enjoy consistent quality every time.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 241: 'chicken-thighs': {
'chicken-thighs': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Chicken Thighs',
// Property or array item — trailing comma allowed in TypeScript
description: 'Juicy halal chicken thighs with rich flavor for curries and grills.',
// Line 244: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Our chicken thighs are known for their succulence and depth of flavor. Whether you are making a traditional karahi or a weekend BBQ, these thighs deliver every time.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 247: 'chicken-wings': {
'chicken-wings': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Chicken Wings',
// Property or array item — trailing comma allowed in TypeScript
description: 'Party-ready halal chicken wings for frying, baking, or grilling.',
// Line 250: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Crispy, flavorful chicken wings prepared halal and delivered fresh. A crowd favorite for game nights and family gatherings.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 253: 'beef-nihari': {
'beef-nihari': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Beef for Nihari',
// Property or array item — trailing comma allowed in TypeScript
description: 'Slow-cook ready beef cuts, perfect for traditional nihari.',
// Line 256: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Specially selected beef cuts ideal for slow-cooked nihari. Rich in collagen and flavor, these cuts break down beautifully over hours of simmering for an authentic, melt-in-your-mouth experience.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 259: 'beef-steak': {
'beef-steak': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Premium Beef Steak',
// Property or array item — trailing comma allowed in TypeScript
description: 'Restaurant-quality halal steak cuts for the perfect sear.',
// Line 262: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Hand-cut premium beef steaks from the finest halal sources. Marbled, tender, and ready for your grill or cast-iron pan. Choose your preferred cutting style.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 265: 'beef-mince': {
'beef-mince': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Beef Mince',
// Property or array item — trailing comma allowed in TypeScript
description: 'Fresh halal beef mince for kebabs, burgers, and qeema.',
// Line 268: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Finely ground halal beef mince with the perfect fat ratio for juicy kebabs, flavorful qeema, and homemade burgers. Ground fresh daily.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 271: 'beef-boneless': {
'beef-boneless': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Boneless Beef Cubes',
// Property or array item — trailing comma allowed in TypeScript
description: 'Versatile boneless beef cubes for karahi, biryani, and stews.',
// Line 274: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Uniform boneless beef cubes cut to perfection for quick-cooking dishes. Ideal for karahi, pulao, and stir-fries where consistent sizing matters.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 277: 'lamb-shoulder': {
'lamb-shoulder': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Lamb Shoulder',
// Property or array item — trailing comma allowed in TypeScript
description: 'Rich, flavorful lamb shoulder for slow roasts and curries.',
// Line 280: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Premium halal lamb shoulder with beautiful marbling. Perfect for slow-roasted feasts, hearty curries, and traditional family meals. Customized to your preferred cut.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 283: 'lamb-leg': {
'lamb-leg': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Lamb Leg',
// Property or array item — trailing comma allowed in TypeScript
description: 'Tender lamb leg for roasts, grills, and special occasions.',
// Line 286: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Whole or portioned lamb leg from certified halal sources. A centerpiece cut for Eid celebrations, dinner parties, and Sunday roasts.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 289: 'lamb-chops': {
'lamb-chops': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Lamb Chops',
// Property or array item — trailing comma allowed in TypeScript
description: 'Premium lamb chops for grilling and fine dining at home.',
// Line 292: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Thick-cut halal lamb chops with perfect fat caps for grilling. Restaurant quality, delivered to your kitchen.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 295: 'lamb-mince': {
'lamb-mince': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Lamb Mince',
// Property or array item — trailing comma allowed in TypeScript
description: 'Fresh halal lamb mince for kebabs, samosas, and qeema.',
// Line 298: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Finely ground lamb mince with rich flavor. Essential for seekh kebabs, lamb qeema, and stuffed parathas.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 301: 'fish-salmon': {
'fish-salmon': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Atlantic Salmon Fillet',
// Property or array item — trailing comma allowed in TypeScript
description: 'Buttery salmon fillets, skin-on and ready to pan-sear.',
// Line 304: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Premium Atlantic salmon fillets with rich omega-3 content. Cleaned, portioned, and vacuum-sealed for maximum freshness.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 307: 'fish-rohu': {
'fish-rohu': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Rohu Fish',
// Property or array item — trailing comma allowed in TypeScript
description: 'Whole rohu fish, cleaned and scaled — a South Asian favorite.',
// Line 310: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Fresh rohu fish, a staple in South Asian cuisine. Cleaned, scaled, and gutted. Perfect for fish curry, fried fish, and traditional recipes.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 313: 'fish-prawns': {
'fish-prawns': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Jumbo Prawns',
// Property or array item — trailing comma allowed in TypeScript
description: 'Large shell-on prawns for grilling, curries, and biryanis.',
// Line 316: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Premium jumbo prawns, deveined and ready to cook. Sweet, firm flesh that holds up beautifully in curries and tandoori preparations.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 319: 'fish-basa': {
'fish-basa': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Basa Fillet',
// Property or array item — trailing comma allowed in TypeScript
description: 'Mild, flaky basa fillets — perfect for beginners and kids.',
// Line 322: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Boneless basa fillets with a mild, delicate flavor. Easy to cook and versatile — great for fish tacos, baking, and light curries.',
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
},
// Line 326: cart: {
cart: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Your Cart',
// Property or array item — trailing comma allowed in TypeScript
itemsCount: '{count} item(s) in your cart',
// Property or array item — trailing comma allowed in TypeScript
empty: 'Your cart is empty',
// Property or array item — trailing comma allowed in TypeScript
emptyHint: 'Browse our premium halal selection and add items to your cart.',
// Property or array item — trailing comma allowed in TypeScript
startShopping: 'Start Shopping',
// Property or array item — trailing comma allowed in TypeScript
customization: 'Customization:',
// Property or array item — trailing comma allowed in TypeScript
orderSummary: 'Order Summary',
// Property or array item — trailing comma allowed in TypeScript
subtotal: 'Subtotal',
// Property or array item — trailing comma allowed in TypeScript
delivery: 'Delivery',
// Property or array item — trailing comma allowed in TypeScript
free: 'Free',
// Property or array item — trailing comma allowed in TypeScript
freeDeliveryHint: 'Free delivery on orders over 500 kr',
// Property or array item — trailing comma allowed in TypeScript
total: 'Total',
// Property or array item — trailing comma allowed in TypeScript
proceedCheckout: 'Proceed to Checkout',
// Property or array item — trailing comma allowed in TypeScript
continueShopping: 'Continue Shopping',
// Property or array item — trailing comma allowed in TypeScript
removeItem: 'Remove item',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 343: checkout: {
checkout: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Secure Checkout',
// Property or array item — trailing comma allowed in TypeScript
backToCart: 'Back to Cart',
// Property or array item — trailing comma allowed in TypeScript
noItems: 'No items to checkout',
// Property or array item — trailing comma allowed in TypeScript
goToShop: 'Go to Shop',
// Property or array item — trailing comma allowed in TypeScript
orderConfirmed: 'Order Confirmed!',
// Property or array item — trailing comma allowed in TypeScript
thankYou: 'Thank you for your order. Your premium halal meat is being prepared.',
// Property or array item — trailing comma allowed in TypeScript
orderId: 'Order ID: {id}',
// Property or array item — trailing comma allowed in TypeScript
viewOrders: 'View Orders',
// Property or array item — trailing comma allowed in TypeScript
haveAccount: 'Have an account?',
// Property or array item — trailing comma allowed in TypeScript
signIn: 'Sign in',
// Property or array item — trailing comma allowed in TypeScript
fasterCheckout: 'for faster checkout.',
// Property or array item — trailing comma allowed in TypeScript
deliveryDetails: 'Delivery Details',
// Property or array item — trailing comma allowed in TypeScript
fullName: 'Full Name',
// Property or array item — trailing comma allowed in TypeScript
email: 'Email',
// Property or array item — trailing comma allowed in TypeScript
phone: 'Phone',
// Property or array item — trailing comma allowed in TypeScript
street: 'Street Address',
// Property or array item — trailing comma allowed in TypeScript
city: 'City',
// Property or array item — trailing comma allowed in TypeScript
state: 'State',
// Property or array item — trailing comma allowed in TypeScript
zip: 'ZIP Code',
// Property or array item — trailing comma allowed in TypeScript
payment: 'Payment',
// Property or array item — trailing comma allowed in TypeScript
creditCard: 'Credit Card',
// Property or array item — trailing comma allowed in TypeScript
cashOnDelivery: 'Cash on Delivery',
// Property or array item — trailing comma allowed in TypeScript
cardNumber: 'Card Number',
// Property or array item — trailing comma allowed in TypeScript
expiry: 'Expiry',
// Property or array item — trailing comma allowed in TypeScript
cvv: 'CVV',
// Property or array item — trailing comma allowed in TypeScript
qty: 'Qty: {count}',
// Property or array item — trailing comma allowed in TypeScript
processing: 'Processing...',
// Property or array item — trailing comma allowed in TypeScript
pay: 'Pay {amount}',
// Property or array item — trailing comma allowed in TypeScript
secure: 'Secure 256-bit SSL encryption',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 374: auth: {
auth: {
// Property or array item — trailing comma allowed in TypeScript
welcomeBack: 'Welcome Back',
// Property or array item — trailing comma allowed in TypeScript
createAccount: 'Create Account',
// Property or array item — trailing comma allowed in TypeScript
joinTagline: 'Join {name} for a premium shopping experience',
// Property or array item — trailing comma allowed in TypeScript
signInTagline: 'Sign in to your {name} account',
// Property or array item — trailing comma allowed in TypeScript
fullName: 'Full Name',
// Property or array item — trailing comma allowed in TypeScript
email: 'Email',
// Property or array item — trailing comma allowed in TypeScript
password: 'Password',
// Property or array item — trailing comma allowed in TypeScript
phone: 'Phone',
// Property or array item — trailing comma allowed in TypeScript
street: 'Street Address',
// Property or array item — trailing comma allowed in TypeScript
city: 'City',
// Property or array item — trailing comma allowed in TypeScript
state: 'State',
// Property or array item — trailing comma allowed in TypeScript
zip: 'ZIP Code',
// Property or array item — trailing comma allowed in TypeScript
signIn: 'Sign In',
// Property or array item — trailing comma allowed in TypeScript
register: 'Create Account',
// Property or array item — trailing comma allowed in TypeScript
hasAccount: 'Already have an account? Sign in',
// Property or array item — trailing comma allowed in TypeScript
noAccount: "Don't have an account? Register",
// Property or array item — trailing comma allowed in TypeScript
invalidCredentials: 'Invalid email or password. Try {email} / demo123',
// Property or array item — trailing comma allowed in TypeScript
demo: 'Demo: {email} / demo123',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 394: account: {
account: {
// Property or array item — trailing comma allowed in TypeScript
title: 'My Account',
// Property or array item — trailing comma allowed in TypeScript
welcome: 'Welcome back, {name}',
// Property or array item — trailing comma allowed in TypeScript
memberSince: 'Member since {date}',
// Property or array item — trailing comma allowed in TypeScript
myWishlist: 'My Wishlist',
// Property or array item — trailing comma allowed in TypeScript
signOut: 'Sign Out',
// Property or array item — trailing comma allowed in TypeScript
orderHistory: 'Order History',
// Property or array item — trailing comma allowed in TypeScript
noOrders: 'No orders yet',
// Property or array item — trailing comma allowed in TypeScript
startShopping: 'Start Shopping',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 404: wishlist: {
wishlist: {
// Property or array item — trailing comma allowed in TypeScript
title: 'My Wishlist',
// Property or array item — trailing comma allowed in TypeScript
saved: '{count} saved item(s)',
// Property or array item — trailing comma allowed in TypeScript
empty: 'Your wishlist is empty',
// Property or array item — trailing comma allowed in TypeScript
emptyHint: 'Save your favorite products to buy them later.',
// Property or array item — trailing comma allowed in TypeScript
browse: 'Browse Products',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 411: about: {
about: {
// Property or array item — trailing comma allowed in TypeScript
title: 'About {name}',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'Bringing premium, 100% Halal meat to your table — fresh, customized, and delivered with care.',
// Property or array item — trailing comma allowed in TypeScript
ourStory: 'Our Story',
// Line 416: storyP1:
storyP1:
// Property or array item — trailing comma allowed in TypeScript
'{name} was founded with a simple mission: make premium halal meat accessible to every family, without compromising on quality, freshness, or religious compliance. We understand that for many households, the right cut prepared the right way is not a luxury — it is essential.',
// Line 418: storyP2:
storyP2:
// Property or array item — trailing comma allowed in TypeScript
'From selecting piece counts for chicken to choosing Nihari or Karahi cuts for beef and lamb, we put customization at the heart of every order. Our expert butchers prepare each order by hand, and our temperature-controlled delivery ensures your meat arrives as fresh as the day it was cut.',
// Property or array item — trailing comma allowed in TypeScript
halalTitle: 'Halal Certification',
// Line 421: halalDesc:
halalDesc:
// Property or array item — trailing comma allowed in TypeScript
'Every product at {name} is sourced from certified halal suppliers. Our supply chain is fully traceable, and we maintain strict compliance with halal slaughter and processing standards. We work exclusively with farms and processors that share our commitment to ethical, religiously compliant meat production.',
// Property or array item — trailing comma allowed in TypeScript
freshDaily: 'Fresh Daily',
// Property or array item — trailing comma allowed in TypeScript
freshDailyDesc: 'Sourced every morning from trusted farms',
// Property or array item — trailing comma allowed in TypeScript
premiumQuality: 'Premium Quality',
// Property or array item — trailing comma allowed in TypeScript
premiumQualityDesc: 'Hand-selected cuts by expert butchers',
// Property or array item — trailing comma allowed in TypeScript
fastDelivery: 'Fast Delivery',
// Property or array item — trailing comma allowed in TypeScript
fastDeliveryDesc: 'Temperature-controlled same-day delivery',
// Property or array item — trailing comma allowed in TypeScript
deliveryTitle: 'Delivery Information',
// Property or array item — trailing comma allowed in TypeScript
delivery1: 'We deliver within a 25-mile radius of our processing facility.',
// Property or array item — trailing comma allowed in TypeScript
delivery2: 'Orders placed before 2 PM are eligible for same-day delivery.',
// Property or array item — trailing comma allowed in TypeScript
delivery3: 'Free delivery on orders over 500 kr. Standard delivery fee: 49 kr.',
// Property or array item — trailing comma allowed in TypeScript
delivery4: 'All products are vacuum-sealed and transported in insulated packaging.',
// Property or array item — trailing comma allowed in TypeScript
contactTitle: 'Contact Us',
// Property or array item — trailing comma allowed in TypeScript
address: 'Tingvallavägen 11, 195 31 Märsta',
// Property or array item — trailing comma allowed in TypeScript
privacyTitle: 'Privacy Policy',
// Line 437: privacyText:
privacyText:
// Property or array item — trailing comma allowed in TypeScript
'We collect only the information needed to process your orders and improve our service — name, contact details, and delivery address. We do not sell your data to third parties. Payment details are handled securely by our payment partners.',
// Property or array item — trailing comma allowed in TypeScript
termsTitle: 'Terms of Service',
// Line 440: termsText:
termsText:
// Property or array item — trailing comma allowed in TypeScript
'All prices are shown in SEK and may change without notice. Orders are subject to availability. Halal certification applies to all meat products listed. Delivery times are estimates and may vary during peak periods.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 443: footer: {
footer: {
// Line 444: tagline:
tagline:
// Property or array item — trailing comma allowed in TypeScript
'Premium 100% Halal meat delivery. Fresh, customized cuts delivered to your door with uncompromising quality.',
// Property or array item — trailing comma allowed in TypeScript
shop: 'Shop',
// Property or array item — trailing comma allowed in TypeScript
company: 'Company',
// Property or array item — trailing comma allowed in TypeScript
contact: 'Contact',
// Property or array item — trailing comma allowed in TypeScript
rights: 'All rights reserved.',
// Property or array item — trailing comma allowed in TypeScript
phone: '072-585 50 50',
// Property or array item — trailing comma allowed in TypeScript
hours: 'Open every day {hours}',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 453: notFound: {
notFound: {
// Property or array item — trailing comma allowed in TypeScript
title: '404',
// Property or array item — trailing comma allowed in TypeScript
message: 'Page not found',
// Property or array item — trailing comma allowed in TypeScript
goHome: 'Go Home',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 458: orderStatus: {
orderStatus: {
// Property or array item — trailing comma allowed in TypeScript
pending: 'pending',
// Property or array item — trailing comma allowed in TypeScript
confirmed: 'confirmed',
// Property or array item — trailing comma allowed in TypeScript
preparing: 'preparing',
// Property or array item — trailing comma allowed in TypeScript
'out-for-delivery': 'out for delivery',
// Property or array item — trailing comma allowed in TypeScript
delivered: 'delivered',
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
};
@@ -0,0 +1,934 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/i18n/locales/sv.ts
* NOT used by the app — read this to learn how the real file works
*/
// Import external package or local module
import { TranslationDict } from '../types';
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const sv: TranslationDict = {
// Line 4: site: {
site: {
// Property or array item — trailing comma allowed in TypeScript
name: 'Kött Gård',
// Property or array item — trailing comma allowed in TypeScript
tagline: 'Premium Halal',
// Line 7: description:
description:
// Property or array item — trailing comma allowed in TypeScript
'Premium 100 % halal köttleverans. Färskt och fryst kyckling, nötkött, lamm och fisk — anpassat efter dina önskemål och levererat till din dörr.',
// Property or array item — trailing comma allowed in TypeScript
metaTitle: 'Premium Halal Köttleverans',
// Property or array item — trailing comma allowed in TypeScript
initials: 'KG',
// Property or array item — trailing comma allowed in TypeScript
email: 'hello@kottgard.se',
// Property or array item — trailing comma allowed in TypeScript
demoEmail: 'demo@kottgard.se',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 14: nav: {
nav: {
// Property or array item — trailing comma allowed in TypeScript
shop: 'Butik',
// Property or array item — trailing comma allowed in TypeScript
chicken: 'Kyckling',
// Property or array item — trailing comma allowed in TypeScript
beef: 'Nötkött',
// Property or array item — trailing comma allowed in TypeScript
lamb: 'Lamm',
// Property or array item — trailing comma allowed in TypeScript
fish: 'Fisk',
// Property or array item — trailing comma allowed in TypeScript
about: 'Om oss',
// Property or array item — trailing comma allowed in TypeScript
halalCert: 'Halalcertifiering',
// Property or array item — trailing comma allowed in TypeScript
delivery: 'Leveransinfo',
// Property or array item — trailing comma allowed in TypeScript
contact: 'Kontakt',
// Property or array item — trailing comma allowed in TypeScript
myAccount: 'Mitt konto',
// Property or array item — trailing comma allowed in TypeScript
orderHistory: 'Orderhistorik',
// Property or array item — trailing comma allowed in TypeScript
wishlist: 'Önskelista',
// Property or array item — trailing comma allowed in TypeScript
cart: 'Varukorg',
// Property or array item — trailing comma allowed in TypeScript
privacy: 'Integritetspolicy',
// Property or array item — trailing comma allowed in TypeScript
terms: 'Användarvillkor',
// Property or array item — trailing comma allowed in TypeScript
searchProducts: 'Sök produkter',
// Property or array item — trailing comma allowed in TypeScript
toggleMenu: 'Växla meny',
// Property or array item — trailing comma allowed in TypeScript
account: 'Konto',
// Property or array item — trailing comma allowed in TypeScript
language: 'Språk',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 35: languageBanner: {
languageBanner: {
// Property or array item — trailing comma allowed in TypeScript
choose: 'Välj språk',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 38: hero: {
hero: {
// Property or array item — trailing comma allowed in TypeScript
badge: '100 % Halal-certifierat',
// Property or array item — trailing comma allowed in TypeScript
taglineShort: 'Naturligt Rent',
// Property or array item — trailing comma allowed in TypeScript
title: 'Premium Halal Kött',
// Property or array item — trailing comma allowed in TypeScript
titleHighlight: '',
// Property or array item — trailing comma allowed in TypeScript
titleEnd: '',
// Property or array item — trailing comma allowed in TypeScript
subtitleShort: 'Färskt. Kvalitet. Tillförlitligt.',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'Halal-certifierat · Färskt dagligen · Hemleverans · Öppet alla dagar',
// Property or array item — trailing comma allowed in TypeScript
hours: 'Öppet alla dagar {hours}',
// Property or array item — trailing comma allowed in TypeScript
location: 'Tingvallavägen 11, Märsta',
// Property or array item — trailing comma allowed in TypeScript
shopNow: 'Se vårt sortiment',
// Property or array item — trailing comma allowed in TypeScript
browseChicken: 'Bläddra kyckling',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'Beställ via WhatsApp',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 53: aboutPreview: {
aboutPreview: {
// Property or array item — trailing comma allowed in TypeScript
label: 'Om oss',
// Property or array item — trailing comma allowed in TypeScript
title: 'Märstas finaste köttbutik',
// Property or array item — trailing comma allowed in TypeScript
p1: 'Kött Gård är mer än en köttbutik — vi är ett löfte om kvalitet. Allt vårt kött är 100 % Halal-certifierat och färskt levererat varje dag.',
// Property or array item — trailing comma allowed in TypeScript
p2: 'Vi handlar lamm från Irland och Nya Zeeland, kyckling och nötkött från välrenommerade producenter, och hjälper dig hitta rätt detalj för middagen, festen eller söndagssteken.',
// Property or array item — trailing comma allowed in TypeScript
p3: 'Kom in i butiken på Tingvallavägen, prata med oss om vad du letar efter — vi styckar och packar efter dina önskemål.',
// Property or array item — trailing comma allowed in TypeScript
statHalal: '100 %',
// Property or array item — trailing comma allowed in TypeScript
statHalalLabel: 'Halal-certifierat',
// Property or array item — trailing comma allowed in TypeScript
statDays: '7 dagar',
// Property or array item — trailing comma allowed in TypeScript
statDaysLabel: 'Öppet i veckan',
// Property or array item — trailing comma allowed in TypeScript
statDelivery: 'Daglig',
// Property or array item — trailing comma allowed in TypeScript
statDeliveryLabel: 'Leverans',
// Property or array item — trailing comma allowed in TypeScript
statFresh: 'Färskt',
// Property or array item — trailing comma allowed in TypeScript
statFreshLabel: 'Varje dag',
// Property or array item — trailing comma allowed in TypeScript
readMore: 'Läs mer om oss',
// Property or array item — trailing comma allowed in TypeScript
imageAlt: 'Färska köttdetaljer på skärbräda från Kött Gård',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 70: trust: {
trust: {
// Property or array item — trailing comma allowed in TypeScript
halal: '100 % Halal',
// Property or array item — trailing comma allowed in TypeScript
halalDesc: 'Certifierad halal-källa med full spårbarhet och efterlevnad.',
// Property or array item — trailing comma allowed in TypeScript
fresh: 'Färskt Dagligen',
// Property or array item — trailing comma allowed in TypeScript
freshDesc: 'Hämtas färskt varje morgon och levereras i toppskick.',
// Property or array item — trailing comma allowed in TypeScript
premium: 'Premiumkvalitet',
// Property or array item — trailing comma allowed in TypeScript
premiumDesc: 'Handplockade styckningar från betrodda gårdar, förberedda av expertslaktare.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 78: categories: {
categories: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Vårt sortiment',
// Property or array item — trailing comma allowed in TypeScript
subtitle: 'Handplockat kött — varje dag. Färskt levererat. Halal. Styckat efter önskemål.',
// Property or array item — trailing comma allowed in TypeScript
shop: 'Handla {name}',
// Line 82: chicken: {
chicken: {
// Property or array item — trailing comma allowed in TypeScript
name: 'Kyckling',
// Property or array item — trailing comma allowed in TypeScript
description: 'Kycklingbröstfilé, vingar, klubba och hel kyckling. Färsk varje morgon.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 86: beef: {
beef: {
// Property or array item — trailing comma allowed in TypeScript
name: 'Nötkött & Kalv',
// Property or array item — trailing comma allowed in TypeScript
description: 'Nötfärs, kalv bone marrow och premiumskär. Hög marmorering, jämn kvalitet.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 90: lamb: {
lamb: {
// Property or array item — trailing comma allowed in TypeScript
name: 'Lamm',
// Property or array item — trailing comma allowed in TypeScript
description: 'Lammbringa, lammhals, lammstek och lammrygg. Från Irland och Nya Zeeland.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 94: fish: {
fish: {
// Property or array item — trailing comma allowed in TypeScript
name: 'Fisk',
// Property or array item — trailing comma allowed in TypeScript
description: 'Färsk fångst, rengjord och redo att tillagas.',
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
},
// Line 99: featured: {
featured: {
// Property or array item — trailing comma allowed in TypeScript
label: 'Utvalt Sortiment',
// Property or array item — trailing comma allowed in TypeScript
title: 'Utvalda Produkter',
// Property or array item — trailing comma allowed in TypeScript
subtitle: 'Våra mest populära styckningar, älskade av familjer i hela staden.',
// Property or array item — trailing comma allowed in TypeScript
viewAll: 'Visa alla',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 105: howItWorks: {
howItWorks: {
// Property or array item — trailing comma allowed in TypeScript
label: 'Beställ',
// Property or array item — trailing comma allowed in TypeScript
title: 'Så enkelt beställer du',
// Property or array item — trailing comma allowed in TypeScript
step1Title: 'Kontakta oss',
// Line 109: step1Desc:
step1Desc:
// Property or array item — trailing comma allowed in TypeScript
'Skicka ett meddelande på WhatsApp med vad du vill ha — vi svarar snabbt.',
// Property or array item — trailing comma allowed in TypeScript
step2Title: 'Vi bekräftar',
// Line 112: step2Desc:
step2Desc:
// Property or array item — trailing comma allowed in TypeScript
'Vi bekräftar din beställning, ger pris och säger när den är klar.',
// Property or array item — trailing comma allowed in TypeScript
step3Title: 'Hämta eller leverans',
// Line 115: step3Desc:
step3Desc:
// Property or array item — trailing comma allowed in TypeScript
'Hämta i butik på Tingvallavägen 11 eller välj hemleverans.',
// Property or array item — trailing comma allowed in TypeScript
step: 'Steg {n}',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'Beställ på WhatsApp',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 120: offers: {
offers: {
// Property or array item — trailing comma allowed in TypeScript
label: 'Beställ',
// Property or array item — trailing comma allowed in TypeScript
title: 'Veckans erbjudanden',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'Vi uppdaterar löpande med färska erbjudanden. Följ oss på sociala medier för senaste priserna.',
// Property or array item — trailing comma allowed in TypeScript
disclaimer: 'Pris gäller så långt lagret räcker',
// Property or array item — trailing comma allowed in TypeScript
was: 'Före',
// Property or array item — trailing comma allowed in TypeScript
now: 'NU',
// Property or array item — trailing comma allowed in TypeScript
order: 'Beställ',
// Property or array item — trailing comma allowed in TypeScript
viewProduct: 'Visa produkt',
// Line 130: badge: {
badge: {
// Property or array item — trailing comma allowed in TypeScript
fresh: 'FÄRSK',
// Property or array item — trailing comma allowed in TypeScript
halal: 'HALAL',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 134: items: {
items: {
// Property or array item — trailing comma allowed in TypeScript
chickenWings: { name: 'Kycklingvingar färsk PL' },
// Property or array item — trailing comma allowed in TypeScript
lambSteak: { name: 'Lammstek färsk Ireland' },
// Property or array item — trailing comma allowed in TypeScript
beefMince: { name: 'Nötfärs 5% fett IRL' },
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
},
// Line 140: social: {
social: {
// Property or array item — trailing comma allowed in TypeScript
label: 'Följ oss',
// Property or array item — trailing comma allowed in TypeScript
title: 'Följ oss',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'1 100+ följare på Facebook · 249 inlägg · Dagliga uppdateringar',
// Property or array item — trailing comma allowed in TypeScript
facebook: 'Facebook',
// Property or array item — trailing comma allowed in TypeScript
instagram: 'Instagram',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'WhatsApp',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 149: contact: {
contact: {
// Property or array item — trailing comma allowed in TypeScript
label: 'Kontakt',
// Property or array item — trailing comma allowed in TypeScript
title: 'Besök oss',
// Property or array item — trailing comma allowed in TypeScript
addressLabel: 'Adress',
// Property or array item — trailing comma allowed in TypeScript
phoneLabel: 'Telefon',
// Property or array item — trailing comma allowed in TypeScript
hoursLabel: 'Öppettider',
// Property or array item — trailing comma allowed in TypeScript
hoursValue: 'Alla dagar: {hours}',
// Property or array item — trailing comma allowed in TypeScript
writeUs: 'Skriv till oss',
// Property or array item — trailing comma allowed in TypeScript
callUs: 'Ring oss',
// Property or array item — trailing comma allowed in TypeScript
openMaps: 'Öppna i Google Maps',
// Property or array item — trailing comma allowed in TypeScript
learnMore: 'Kontaktuppgifter',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 161: cta: {
cta: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Redo för Premium Halal Kött?',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'Beställ idag och upplev skillnaden med verkligt färskt, anpassat halal kött levererat till din dörr.',
// Property or array item — trailing comma allowed in TypeScript
button: 'Se vårt sortiment',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'Beställ på WhatsApp',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 168: shop: {
shop: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Alla Produkter',
// Property or array item — trailing comma allowed in TypeScript
subtitle: 'Premium halal kött, anpassat efter dina önskemål',
// Property or array item — trailing comma allowed in TypeScript
noProducts: 'Inga produkter hittades',
// Property or array item — trailing comma allowed in TypeScript
noProductsHint: 'Prova att justera dina filter eller sökfråga',
// Property or array item — trailing comma allowed in TypeScript
filters: 'Filter',
// Property or array item — trailing comma allowed in TypeScript
productsFound: '{count} produkter hittades',
// Property or array item — trailing comma allowed in TypeScript
search: 'Sök',
// Property or array item — trailing comma allowed in TypeScript
searchPlaceholder: 'Sök produkter...',
// Property or array item — trailing comma allowed in TypeScript
category: 'Kategori',
// Property or array item — trailing comma allowed in TypeScript
sortBy: 'Sortera efter',
// Property or array item — trailing comma allowed in TypeScript
all: 'Alla',
// Property or array item — trailing comma allowed in TypeScript
sortFeatured: 'Utvalda',
// Property or array item — trailing comma allowed in TypeScript
sortPriceAsc: 'Pris: Lägst till högst',
// Property or array item — trailing comma allowed in TypeScript
sortPriceDesc: 'Pris: Högst till lägst',
// Property or array item — trailing comma allowed in TypeScript
sortName: 'Namn A–Ö',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 185: product: {
product: {
// Property or array item — trailing comma allowed in TypeScript
backToShop: 'Tillbaka till butiken',
// Property or array item — trailing comma allowed in TypeScript
inStock: 'I lager',
// Property or array item — trailing comma allowed in TypeScript
outOfStock: 'Slut i lager',
// Property or array item — trailing comma allowed in TypeScript
halalTrust: '100 % Halal-certifierad · Färskt dagligen · Premiumkvalitet',
// Property or array item — trailing comma allowed in TypeScript
yourSelection: 'Ditt val',
// Property or array item — trailing comma allowed in TypeScript
aboutProduct: 'Om denna produkt',
// Property or array item — trailing comma allowed in TypeScript
addToCart: 'Lägg i varukorg',
// Property or array item — trailing comma allowed in TypeScript
addedToCart: 'Tillagd i varukorg',
// Property or array item — trailing comma allowed in TypeScript
decreaseQty: 'Minska antal',
// Property or array item — trailing comma allowed in TypeScript
increaseQty: 'Öka antal',
// Property or array item — trailing comma allowed in TypeScript
removeWishlist: 'Ta bort från önskelista',
// Property or array item — trailing comma allowed in TypeScript
addWishlist: 'Lägg till i önskelista',
// Property or array item — trailing comma allowed in TypeScript
viewProduct: 'Visa {name}',
// Property or array item — trailing comma allowed in TypeScript
pieces: '{count} bitar',
// Property or array item — trailing comma allowed in TypeScript
standardCut: 'Standardstyckning',
// Property or array item — trailing comma allowed in TypeScript
howManyCuts: 'Hur Många Styckningar Vill Du Ha?',
// Property or array item — trailing comma allowed in TypeScript
howManyCutsHint: 'Välj antal styckningar för din beställning',
// Property or array item — trailing comma allowed in TypeScript
cutsAndStyle: '{cuts} styckningar · {style}',
// Property or array item — trailing comma allowed in TypeScript
selectCutting: 'Välj skärstil',
// Property or array item — trailing comma allowed in TypeScript
selectCuttingHint: 'Våra slaktare förbereder ditt {category} exakt enligt din önskade styckning',
// Line 206: fishNote:
fishNote:
// Property or array item — trailing comma allowed in TypeScript
'Fiskprodukter förbereds med vår standard professionella styckning — rengjord, fjällad och redo att tillagas.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 209: cutting: {
cutting: {
// Property or array item — trailing comma allowed in TypeScript
nihari: 'Nihari-styckning',
// Property or array item — trailing comma allowed in TypeScript
karahi: 'Karahi-styckning',
// Property or array item — trailing comma allowed in TypeScript
qeema: 'Qeema (färs)',
// Property or array item — trailing comma allowed in TypeScript
boneless: 'Benfri',
// Property or array item — trailing comma allowed in TypeScript
steak: 'Biffstyckning',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 216: priceUnit: {
priceUnit: {
// Property or array item — trailing comma allowed in TypeScript
perBird: 'per kyckling',
// Property or array item — trailing comma allowed in TypeScript
perPack: 'per förpackning',
// Property or array item — trailing comma allowed in TypeScript
perKg: 'per kg',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 221: badges: {
badges: {
// Property or array item — trailing comma allowed in TypeScript
bestseller: 'Bästsäljare',
// Property or array item — trailing comma allowed in TypeScript
chefsPick: 'Kockens val',
// Property or array item — trailing comma allowed in TypeScript
premium: 'Premium',
// Property or array item — trailing comma allowed in TypeScript
popular: 'Populär',
// Property or array item — trailing comma allowed in TypeScript
freshCatch: 'Färsk fångst',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 228: products: {
products: {
// Line 229: 'chicken-whole': {
'chicken-whole': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Hel Kyckling',
// Property or array item — trailing comma allowed in TypeScript
description: 'Gårdsfärsk hel halal kyckling, perfekt för stekning eller curry.',
// Line 232: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Våra hela kycklingar kommer från certifierade halalgårdar och levereras i toppskick. Varje fågel är handplockad för kvalitet, med mört kött och ren bearbetning. Välj önskat antal bitar så förbereder vi den precis som du behöver.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 235: 'chicken-breast': {
'chicken-breast': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Kycklingbröst',
// Property or array item — trailing comma allowed in TypeScript
description: 'Magert, benfritt kycklingbröst — idealiskt för grillning och hälsosamma måltider.',
// Line 238: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Premium benfritt kycklingbröst, trimmat och redo att tillagas. Perfekt för kebab, wok och hälsosamma vardagsmiddagar. Välj antal bitar och njut av konsekvent kvalitet varje gång.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 241: 'chicken-thighs': {
'chicken-thighs': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Kycklinglår',
// Property or array item — trailing comma allowed in TypeScript
description: 'Saftiga halal kycklinglår med rik smak för curry och grill.',
// Line 244: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Våra kycklinglår är kända för sin saftighet och djupa smak. Oavsett om du lagar traditionell karahi eller helg-BBQ levererar dessa lår varje gång.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 247: 'chicken-wings': {
'chicken-wings': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Kycklingvingar',
// Property or array item — trailing comma allowed in TypeScript
description: 'Festfärdiga halal kycklingvingar för stekning, bakning eller grillning.',
// Line 250: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Krispiga, smakrika kycklingvingar tillagade enligt halal och levererade färskt. En favorit för matchkvällar och familjesammankomster.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 253: 'beef-nihari': {
'beef-nihari': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Nötkött för Nihari',
// Property or array item — trailing comma allowed in TypeScript
description: 'Långkoksklara nötköttsstyckningar, perfekta för traditionell nihari.',
// Line 256: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Särskilt utvalda nötköttsstyckningar idealiska för långkokt nihari. Rika på kollagen och smak, dessa styckningar bryts ner vackert under timmar av sjudning för en autentisk, smältande upplevelse.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 259: 'beef-steak': {
'beef-steak': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Premium Nötbiff',
// Property or array item — trailing comma allowed in TypeScript
description: 'Restaurangkvalitet halal biffstyckningar för perfekt stekyta.',
// Line 262: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Handskurna premium biffar från de finaste halal-källorna. Marmorering, mörhet och redo för din grill eller gjutjärnspanna. Välj önskad skärstil.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 265: 'beef-mince': {
'beef-mince': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Nötfärs',
// Property or array item — trailing comma allowed in TypeScript
description: 'Färsk halal nötfärs för kebab, burgare och qeema.',
// Line 268: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Finmalen halal nötfärs med perfekt fettförhållande för saftiga kebab, smakrik qeema och hemlagade burgare. Malas färskt dagligen.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 271: 'beef-boneless': {
'beef-boneless': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Benfria Nötköttskuber',
// Property or array item — trailing comma allowed in TypeScript
description: 'Mångsidiga benfria nötköttskuber för karahi, biryani och grytor.',
// Line 274: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Enhetliga benfria nötköttskuber skurna till perfektion för snabbkokta rätter. Idealiska för karahi, pulao och wok där konsekvent storlek är viktigt.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 277: 'lamb-shoulder': {
'lamb-shoulder': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Lammbog',
// Property or array item — trailing comma allowed in TypeScript
description: 'Rik, smakrik lammbog för långstekning och curry.',
// Line 280: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Premium halal lammbog med vacker marmorering. Perfekt för långstekta festmåltider, rejäla curryrätter och traditionella familjemiddagar. Anpassad efter din önskade styckning.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 283: 'lamb-leg': {
'lamb-leg': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Lammlägg',
// Property or array item — trailing comma allowed in TypeScript
description: 'Mört lammlägg för stekning, grillning och speciella tillfällen.',
// Line 286: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Hela eller portionerade lammlägg från certifierade halal-källor. En centerstyckning för Eid-firanden, middagsbjudningar och söndagsstek.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 289: 'lamb-chops': {
'lamb-chops': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Lammkotletter',
// Property or array item — trailing comma allowed in TypeScript
description: 'Premium lammkotletter för grillning och finmiddag hemma.',
// Line 292: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Tjockskurna halal lammkotletter med perfekt fettlock för grillning. Restaurangkvalitet, levererad till ditt kök.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 295: 'lamb-mince': {
'lamb-mince': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Lammfärs',
// Property or array item — trailing comma allowed in TypeScript
description: 'Färsk halal lammfärs för kebab, samosas och qeema.',
// Line 298: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Finmalen lammfärs med rik smak. Nödvändig för seekh kebab, lamm qeema och fyllda parathas.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 301: 'fish-salmon': {
'fish-salmon': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Atlantisk Laxfilé',
// Property or array item — trailing comma allowed in TypeScript
description: 'Smörig laxfilé, med skinn och redo att steka i panna.',
// Line 304: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Premium atlantisk laxfilé med rikt omega-3-innehåll. Rengjord, portionerad och vakuumförpackad för maximal färskhet.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 307: 'fish-rohu': {
'fish-rohu': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Rohu-fisk',
// Property or array item — trailing comma allowed in TypeScript
description: 'Hel rohu-fisk, rengjord och fjällad — en sydasiatisk favorit.',
// Line 310: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Färsk rohu-fisk, en stapel i sydasiatisk matlagning. Rengjord, fjällad och urtagen. Perfekt för fiskcurry, stekt fisk och traditionella recept.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 313: 'fish-prawns': {
'fish-prawns': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Jätteräkor',
// Property or array item — trailing comma allowed in TypeScript
description: 'Stora räkor med skal för grillning, curry och biryani.',
// Line 316: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Premium jätteräkor, urtagna och redo att tillagas. Söt, fast kött som håller sig vackert i curry och tandoori-rätter.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 319: 'fish-basa': {
'fish-basa': {
// Property or array item — trailing comma allowed in TypeScript
name: 'Basa-filé',
// Property or array item — trailing comma allowed in TypeScript
description: 'Mild, flagnig basa-filé — perfekt för nybörjare och barn.',
// Line 322: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'Benfria basa-filéer med mild, delikat smak. Lätt att tillaga och mångsidig — utmärkt för fisk-tacos, bakning och lätta curryrätter.',
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
},
// Line 326: cart: {
cart: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Din Varukorg',
// Property or array item — trailing comma allowed in TypeScript
itemsCount: '{count} artikel/artiklar i din varukorg',
// Property or array item — trailing comma allowed in TypeScript
empty: 'Din varukorg är tom',
// Property or array item — trailing comma allowed in TypeScript
emptyHint: 'Bläddra i vårt premium halal-sortiment och lägg till artiklar.',
// Property or array item — trailing comma allowed in TypeScript
startShopping: 'Börja handla',
// Property or array item — trailing comma allowed in TypeScript
customization: 'Anpassning:',
// Property or array item — trailing comma allowed in TypeScript
orderSummary: 'Ordersammanfattning',
// Property or array item — trailing comma allowed in TypeScript
subtotal: 'Delsumma',
// Property or array item — trailing comma allowed in TypeScript
delivery: 'Leverans',
// Property or array item — trailing comma allowed in TypeScript
free: 'Gratis',
// Property or array item — trailing comma allowed in TypeScript
freeDeliveryHint: 'Fri leverans på beställningar över 500 kr',
// Property or array item — trailing comma allowed in TypeScript
total: 'Totalt',
// Property or array item — trailing comma allowed in TypeScript
proceedCheckout: 'Gå till kassan',
// Property or array item — trailing comma allowed in TypeScript
continueShopping: 'Fortsätt handla',
// Property or array item — trailing comma allowed in TypeScript
removeItem: 'Ta bort artikel',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 343: checkout: {
checkout: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Säker Kassa',
// Property or array item — trailing comma allowed in TypeScript
backToCart: 'Tillbaka till varukorg',
// Property or array item — trailing comma allowed in TypeScript
noItems: 'Inga artiklar att betala',
// Property or array item — trailing comma allowed in TypeScript
goToShop: 'Gå till butiken',
// Property or array item — trailing comma allowed in TypeScript
orderConfirmed: 'Beställning bekräftad!',
// Property or array item — trailing comma allowed in TypeScript
thankYou: 'Tack för din beställning. Ditt premium halal kött förbereds.',
// Property or array item — trailing comma allowed in TypeScript
orderId: 'Order-ID: {id}',
// Property or array item — trailing comma allowed in TypeScript
viewOrders: 'Visa beställningar',
// Property or array item — trailing comma allowed in TypeScript
haveAccount: 'Har du ett konto?',
// Property or array item — trailing comma allowed in TypeScript
signIn: 'Logga in',
// Property or array item — trailing comma allowed in TypeScript
fasterCheckout: 'för snabbare utcheckning.',
// Property or array item — trailing comma allowed in TypeScript
deliveryDetails: 'Leveransuppgifter',
// Property or array item — trailing comma allowed in TypeScript
fullName: 'Fullständigt namn',
// Property or array item — trailing comma allowed in TypeScript
email: 'E-post',
// Property or array item — trailing comma allowed in TypeScript
phone: 'Telefon',
// Property or array item — trailing comma allowed in TypeScript
street: 'Gatuadress',
// Property or array item — trailing comma allowed in TypeScript
city: 'Stad',
// Property or array item — trailing comma allowed in TypeScript
state: 'Län',
// Property or array item — trailing comma allowed in TypeScript
zip: 'Postnummer',
// Property or array item — trailing comma allowed in TypeScript
payment: 'Betalning',
// Property or array item — trailing comma allowed in TypeScript
creditCard: 'Kreditkort',
// Property or array item — trailing comma allowed in TypeScript
cashOnDelivery: 'Kontant vid leverans',
// Property or array item — trailing comma allowed in TypeScript
cardNumber: 'Kortnummer',
// Property or array item — trailing comma allowed in TypeScript
expiry: 'Giltig till',
// Property or array item — trailing comma allowed in TypeScript
cvv: 'CVV',
// Property or array item — trailing comma allowed in TypeScript
qty: 'Antal: {count}',
// Property or array item — trailing comma allowed in TypeScript
processing: 'Bearbetar...',
// Property or array item — trailing comma allowed in TypeScript
pay: 'Betala {amount}',
// Property or array item — trailing comma allowed in TypeScript
secure: 'Säker 256-bitars SSL-kryptering',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 374: auth: {
auth: {
// Property or array item — trailing comma allowed in TypeScript
welcomeBack: 'Välkommen tillbaka',
// Property or array item — trailing comma allowed in TypeScript
createAccount: 'Skapa konto',
// Property or array item — trailing comma allowed in TypeScript
joinTagline: 'Gå med i {name} för en premium shoppingupplevelse',
// Property or array item — trailing comma allowed in TypeScript
signInTagline: 'Logga in på ditt {name}-konto',
// Property or array item — trailing comma allowed in TypeScript
fullName: 'Fullständigt namn',
// Property or array item — trailing comma allowed in TypeScript
email: 'E-post',
// Property or array item — trailing comma allowed in TypeScript
password: 'Lösenord',
// Property or array item — trailing comma allowed in TypeScript
phone: 'Telefon',
// Property or array item — trailing comma allowed in TypeScript
street: 'Gatuadress',
// Property or array item — trailing comma allowed in TypeScript
city: 'Stad',
// Property or array item — trailing comma allowed in TypeScript
state: 'Län',
// Property or array item — trailing comma allowed in TypeScript
zip: 'Postnummer',
// Property or array item — trailing comma allowed in TypeScript
signIn: 'Logga in',
// Property or array item — trailing comma allowed in TypeScript
register: 'Skapa konto',
// Property or array item — trailing comma allowed in TypeScript
hasAccount: 'Har du redan ett konto? Logga in',
// Property or array item — trailing comma allowed in TypeScript
noAccount: 'Har du inget konto? Registrera dig',
// Property or array item — trailing comma allowed in TypeScript
invalidCredentials: 'Ogiltig e-post eller lösenord. Prova {email} / demo123',
// Property or array item — trailing comma allowed in TypeScript
demo: 'Demo: {email} / demo123',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 394: account: {
account: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Mitt Konto',
// Property or array item — trailing comma allowed in TypeScript
welcome: 'Välkommen tillbaka, {name}',
// Property or array item — trailing comma allowed in TypeScript
memberSince: 'Medlem sedan {date}',
// Property or array item — trailing comma allowed in TypeScript
myWishlist: 'Min önskelista',
// Property or array item — trailing comma allowed in TypeScript
signOut: 'Logga ut',
// Property or array item — trailing comma allowed in TypeScript
orderHistory: 'Orderhistorik',
// Property or array item — trailing comma allowed in TypeScript
noOrders: 'Inga beställningar ännu',
// Property or array item — trailing comma allowed in TypeScript
startShopping: 'Börja handla',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 404: wishlist: {
wishlist: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Min Önskelista',
// Property or array item — trailing comma allowed in TypeScript
saved: '{count} sparad(e) artikel/artiklar',
// Property or array item — trailing comma allowed in TypeScript
empty: 'Din önskelista är tom',
// Property or array item — trailing comma allowed in TypeScript
emptyHint: 'Spara dina favoritprodukter för att köpa dem senare.',
// Property or array item — trailing comma allowed in TypeScript
browse: 'Bläddra produkter',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 411: about: {
about: {
// Property or array item — trailing comma allowed in TypeScript
title: 'Om {name}',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'Vi levererar premium 100 % halal kött till ditt bord — färskt, anpassat och med omsorg.',
// Property or array item — trailing comma allowed in TypeScript
ourStory: 'Vår Historia',
// Line 416: storyP1:
storyP1:
// Property or array item — trailing comma allowed in TypeScript
'{name} grundades med ett enkelt uppdrag: göra premium halal kött tillgängligt för varje familj, utan att kompromissa med kvalitet, färskhet eller religiös efterlevnad. Vi förstår att för många hushåll är rätt styckning tillagad på rätt sätt inte en lyx — det är nödvändigt.',
// Line 418: storyP2:
storyP2:
// Property or array item — trailing comma allowed in TypeScript
'Från att välja antal bitar för kyckling till att välja Nihari- eller Karahi-styckning för nötkött och lamm, sätter vi anpassning i centrum för varje beställning. Våra expertslaktare förbereder varje order för hand, och vår temperaturkontrollerade leverans säkerställer att ditt kött anländer lika färskt som dagen det styckades.',
// Property or array item — trailing comma allowed in TypeScript
halalTitle: 'Halalcertifiering',
// Line 421: halalDesc:
halalDesc:
// Property or array item — trailing comma allowed in TypeScript
'Varje produkt hos {name} kommer från certifierade halal-leverantörer. Vår leveranskedja är fullt spårbar och vi upprätthåller strikt efterlevnad av halal-slakt och -bearbetningsstandarder. Vi arbetar uteslutande med gårdar och bearbetare som delar vårt engagemang för etiskt, religiöst korrekt köttproduktion.',
// Property or array item — trailing comma allowed in TypeScript
freshDaily: 'Färskt Dagligen',
// Property or array item — trailing comma allowed in TypeScript
freshDailyDesc: 'Hämtas varje morgon från betrodda gårdar',
// Property or array item — trailing comma allowed in TypeScript
premiumQuality: 'Premiumkvalitet',
// Property or array item — trailing comma allowed in TypeScript
premiumQualityDesc: 'Handplockade styckningar av expertslaktare',
// Property or array item — trailing comma allowed in TypeScript
fastDelivery: 'Snabb Leverans',
// Property or array item — trailing comma allowed in TypeScript
fastDeliveryDesc: 'Temperaturkontrollerad leverans samma dag',
// Property or array item — trailing comma allowed in TypeScript
deliveryTitle: 'Leveransinformation',
// Property or array item — trailing comma allowed in TypeScript
delivery1: 'Vi levererar inom en radie på 40 km från vår anläggning.',
// Property or array item — trailing comma allowed in TypeScript
delivery2: 'Beställningar före kl. 14 är berättigade till leverans samma dag.',
// Property or array item — trailing comma allowed in TypeScript
delivery3: 'Fri leverans på beställningar över 500 kr. Standardleverans: 49 kr.',
// Property or array item — trailing comma allowed in TypeScript
delivery4: 'Alla produkter vakuumförpackas och transporteras i isolerad förpackning.',
// Property or array item — trailing comma allowed in TypeScript
contactTitle: 'Kontakta Oss',
// Property or array item — trailing comma allowed in TypeScript
address: 'Tingvallavägen 11, 195 31 Märsta',
// Property or array item — trailing comma allowed in TypeScript
privacyTitle: 'Integritetspolicy',
// Line 437: privacyText:
privacyText:
// Property or array item — trailing comma allowed in TypeScript
'Vi samlar endast den information som behövs för att behandla dina beställningar — namn, kontaktuppgifter och leveransadress. Vi säljer inte dina uppgifter till tredje part. Betalningsuppgifter hanteras säkert av våra betalpartners.',
// Property or array item — trailing comma allowed in TypeScript
termsTitle: 'Användarvillkor',
// Line 440: termsText:
termsText:
// Property or array item — trailing comma allowed in TypeScript
'Alla priser visas i SEK och kan ändras utan föregående meddelande. Beställningar är beroende av tillgång. Halalcertifiering gäller för alla köttprodukter. Leveranstider är uppskattningar och kan variera under högtrafik.',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 443: footer: {
footer: {
// Line 444: tagline:
tagline:
// Property or array item — trailing comma allowed in TypeScript
'Premium 100 % halal köttleverans. Färskt, anpassat kött levererat till din dörr med kompromisslös kvalitet.',
// Property or array item — trailing comma allowed in TypeScript
shop: 'Butik',
// Property or array item — trailing comma allowed in TypeScript
company: 'Företag',
// Property or array item — trailing comma allowed in TypeScript
contact: 'Kontakt',
// Property or array item — trailing comma allowed in TypeScript
rights: 'Alla rättigheter förbehållna.',
// Property or array item — trailing comma allowed in TypeScript
phone: '072-585 50 50',
// Property or array item — trailing comma allowed in TypeScript
hours: 'Öppet alla dagar {hours}',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 453: notFound: {
notFound: {
// Property or array item — trailing comma allowed in TypeScript
title: '404',
// Property or array item — trailing comma allowed in TypeScript
message: 'Sidan hittades inte',
// Property or array item — trailing comma allowed in TypeScript
goHome: 'Gå till startsidan',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 458: orderStatus: {
orderStatus: {
// Property or array item — trailing comma allowed in TypeScript
pending: 'väntande',
// Property or array item — trailing comma allowed in TypeScript
confirmed: 'bekräftad',
// Property or array item — trailing comma allowed in TypeScript
preparing: 'förbereds',
// Property or array item — trailing comma allowed in TypeScript
'out-for-delivery': 'ute för leverans',
// Property or array item — trailing comma allowed in TypeScript
delivered: 'levererad',
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
};
@@ -0,0 +1,936 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/i18n/locales/ur.ts
* NOT used by the app — read this to learn how the real file works
*/
// Import external package or local module
import { TranslationDict } from '../types';
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const ur: TranslationDict = {
// Line 4: site: {
site: {
// Property or array item — trailing comma allowed in TypeScript
name: 'کوٹ گارڈ',
// Property or array item — trailing comma allowed in TypeScript
tagline: 'پریمیم حلال',
// Line 7: description:
description:
// Property or array item — trailing comma allowed in TypeScript
'پریمیم 100% حلال گوشت کی ڈیلیوری۔ تازہ اور منجمد مرغی، گائے کا گوشت، بکرے کا گوشت اور مچھلی — آپ کی پسند کے مطابق تیار کر کے آپ کے دروازے تک پہنچائی جاتی ہے۔',
// Property or array item — trailing comma allowed in TypeScript
metaTitle: 'پریمیم حلال گوشت ڈیلیوری',
// Property or array item — trailing comma allowed in TypeScript
initials: 'KG',
// Property or array item — trailing comma allowed in TypeScript
email: 'hello@kottgard.se',
// Property or array item — trailing comma allowed in TypeScript
demoEmail: 'demo@kottgard.se',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 14: nav: {
nav: {
// Property or array item — trailing comma allowed in TypeScript
shop: 'خریداری',
// Property or array item — trailing comma allowed in TypeScript
chicken: 'مرغی',
// Property or array item — trailing comma allowed in TypeScript
beef: 'گائے کا گوشت',
// Property or array item — trailing comma allowed in TypeScript
lamb: 'بکرے کا گوشت',
// Property or array item — trailing comma allowed in TypeScript
fish: 'مچھلی',
// Property or array item — trailing comma allowed in TypeScript
about: 'ہمارے بارے میں',
// Property or array item — trailing comma allowed in TypeScript
halalCert: 'حلال سرٹیفیکیشن',
// Property or array item — trailing comma allowed in TypeScript
delivery: 'ڈیلیوری کی معلومات',
// Property or array item — trailing comma allowed in TypeScript
contact: 'رابطہ',
// Property or array item — trailing comma allowed in TypeScript
myAccount: 'میرا اکاؤنٹ',
// Property or array item — trailing comma allowed in TypeScript
orderHistory: 'آرڈر کی تاریخ',
// Property or array item — trailing comma allowed in TypeScript
wishlist: 'پسندیدہ',
// Property or array item — trailing comma allowed in TypeScript
cart: 'ٹوکری',
// Property or array item — trailing comma allowed in TypeScript
privacy: 'رازداری کی پالیسی',
// Property or array item — trailing comma allowed in TypeScript
terms: 'شرائط و ضوابط',
// Property or array item — trailing comma allowed in TypeScript
searchProducts: 'مصنوعات تلاش کریں',
// Property or array item — trailing comma allowed in TypeScript
toggleMenu: 'مینو کھولیں',
// Property or array item — trailing comma allowed in TypeScript
account: 'اکاؤنٹ',
// Property or array item — trailing comma allowed in TypeScript
language: 'زبان',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 35: languageBanner: {
languageBanner: {
// Property or array item — trailing comma allowed in TypeScript
choose: 'اپنی زبان منتخب کریں',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 38: hero: {
hero: {
// Property or array item — trailing comma allowed in TypeScript
badge: '100% حلال تصدیق شدہ',
// Property or array item — trailing comma allowed in TypeScript
taglineShort: 'قدرتی طور پر خالص',
// Property or array item — trailing comma allowed in TypeScript
title: 'پریمیم حلال گوشت',
// Property or array item — trailing comma allowed in TypeScript
titleHighlight: '',
// Property or array item — trailing comma allowed in TypeScript
titleEnd: '',
// Property or array item — trailing comma allowed in TypeScript
subtitleShort: 'تازہ۔ معیار۔ قابل اعتماد۔',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'حلال تصدیق شدہ · روزانہ تازہ · گھر کی ڈیلیوری · ہر دن کھلا',
// Property or array item — trailing comma allowed in TypeScript
hours: 'ہر دن کھلا {hours}',
// Property or array item — trailing comma allowed in TypeScript
location: 'Tingvallavägen 11, Märsta',
// Property or array item — trailing comma allowed in TypeScript
shopNow: 'ہمارا مجموعہ دیکھیں',
// Property or array item — trailing comma allowed in TypeScript
browseChicken: 'مرغی دیکھیں',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'واٹس ایپ سے آرڈر کریں',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 53: aboutPreview: {
aboutPreview: {
// Property or array item — trailing comma allowed in TypeScript
label: 'ہمارے بارے میں',
// Property or array item — trailing comma allowed in TypeScript
title: 'مائرسٹا کی بہترین گوشت کی دکان',
// Property or array item — trailing comma allowed in TypeScript
p1: 'کوٹ گارڈ صرف ایک گوشت کی دکان نہیں — ہم معیار کا وعدہ ہیں۔ ہمارا تمام گوشت 100% حلال تصدیق شدہ ہے اور روزانہ تازہ پہنچایا جاتا ہے۔',
// Property or array item — trailing comma allowed in TypeScript
p2: 'ہم آئرلینڈ اور نیوزی لینڈ سے بکرے کا گوشت، معتبر پیدا کنندگان سے مرغی اور گائے کا گوشت حاصل کرتے ہیں، اور آپ کو رات کے کھانے، تقریب یا اتوار کی روست کے لیے صحیح کٹ تلاش کرنے میں مدد کرتے ہیں۔',
// Property or array item — trailing comma allowed in TypeScript
p3: 'Tingvallavägen پر ہماری دکان میں آئیں، بتائیں آپ کیا تلاش کر رہے ہیں — ہم آپ کی خواہش کے مطابق کاٹتے اور پیک کرتے ہیں۔',
// Property or array item — trailing comma allowed in TypeScript
statHalal: '100%',
// Property or array item — trailing comma allowed in TypeScript
statHalalLabel: 'حلال تصدیق شدہ',
// Property or array item — trailing comma allowed in TypeScript
statDays: '7 دن',
// Property or array item — trailing comma allowed in TypeScript
statDaysLabel: 'ہفتے میں کھلا',
// Property or array item — trailing comma allowed in TypeScript
statDelivery: 'روزانہ',
// Property or array item — trailing comma allowed in TypeScript
statDeliveryLabel: 'ڈیلیوری',
// Property or array item — trailing comma allowed in TypeScript
statFresh: 'تازہ',
// Property or array item — trailing comma allowed in TypeScript
statFreshLabel: 'ہر دن',
// Property or array item — trailing comma allowed in TypeScript
readMore: 'مزید پڑھیں',
// Property or array item — trailing comma allowed in TypeScript
imageAlt: 'کوٹ گارڈ سے تازہ گوشت کی کٹس',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 70: trust: {
trust: {
// Property or array item — trailing comma allowed in TypeScript
halal: '100% حلال',
// Property or array item — trailing comma allowed in TypeScript
halalDesc: 'مکمل سراغ رسانی اور تعمیل کے ساتھ تصدیق شدہ حلال ذرائع۔',
// Property or array item — trailing comma allowed in TypeScript
fresh: 'روزانہ تازہ',
// Property or array item — trailing comma allowed in TypeScript
freshDesc: 'ہر صبح تازہ حاصل کیا جاتا ہے اور بہترین حالت میں پہنچایا جاتا ہے۔',
// Property or array item — trailing comma allowed in TypeScript
premium: 'پریمیم معیار',
// Property or array item — trailing comma allowed in TypeScript
premiumDesc: 'قابل اعتماد فارموں سے منتخب کردہ کٹس، ماہر قصابوں کے ذریعے تیار۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 78: categories: {
categories: {
// Property or array item — trailing comma allowed in TypeScript
title: 'زمرے کے لحاظ سے خریداری',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'روزمرہ کی مرغی سے لے کر پریمیم بکرے کے کٹlets تک — ہر کٹ آپ کی پسند کے مطابق۔',
// Property or array item — trailing comma allowed in TypeScript
shop: '{name} خریدیں',
// Line 83: chicken: {
chicken: {
// Property or array item — trailing comma allowed in TypeScript
name: 'مرغی',
// Property or array item — trailing comma allowed in TypeScript
description: 'فارم سے تازہ حلال مرغی، آپ کی پسند کے مطابق کاٹی گئی',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 87: beef: {
beef: {
// Property or array item — trailing comma allowed in TypeScript
name: 'گائے کا گوشت',
// Property or array item — trailing comma allowed in TypeScript
description: 'روایتی اور جدید کٹس میں پریمیم حلال گائے کا گوشت',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 91: lamb: {
lamb: {
// Property or array item — trailing comma allowed in TypeScript
name: 'بکرے کا گوشت',
// Property or array item — trailing comma allowed in TypeScript
description: 'ہر موقع کے لیے نرم حلال بکرے کا گوشت',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 95: fish: {
fish: {
// Property or array item — trailing comma allowed in TypeScript
name: 'مچھلی',
// Property or array item — trailing comma allowed in TypeScript
description: 'تازہ پکڑ، صاف کی گئی اور پکانے کے لیے تیار',
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
},
// Line 100: featured: {
featured: {
// Property or array item — trailing comma allowed in TypeScript
label: 'منتخب مجموعہ',
// Property or array item — trailing comma allowed in TypeScript
title: 'نمایاں مصنوعات',
// Property or array item — trailing comma allowed in TypeScript
subtitle: 'ہماری سب سے مقبول کٹس، شہر بھر کے خاندانوں کی پسندیدہ۔',
// Property or array item — trailing comma allowed in TypeScript
viewAll: 'سب دیکھیں',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 106: howItWorks: {
howItWorks: {
// Property or array item — trailing comma allowed in TypeScript
label: 'آرڈر',
// Property or array item — trailing comma allowed in TypeScript
title: 'آرڈر کرنا کتنا آسان ہے',
// Property or array item — trailing comma allowed in TypeScript
step1Title: 'ہم سے رابطہ کریں',
// Line 110: step1Desc:
step1Desc:
// Property or array item — trailing comma allowed in TypeScript
'واٹس ایپ پر پیغام بھیجیں کہ آپ کیا چاہتے ہیں — ہم جلدی جواب دیتے ہیں۔',
// Property or array item — trailing comma allowed in TypeScript
step2Title: 'ہم تصدیق کرتے ہیں',
// Line 113: step2Desc:
step2Desc:
// Property or array item — trailing comma allowed in TypeScript
'ہم آپ کے آرڈر کی تصدیق کرتے ہیں، قیمت بتاتے ہیں اور بتاتے ہیں کہ کب تیار ہوگا۔',
// Property or array item — trailing comma allowed in TypeScript
step3Title: 'وصول یا ڈیلیوری',
// Line 116: step3Desc:
step3Desc:
// Property or array item — trailing comma allowed in TypeScript
'Tingvallavägen 11 پر دکان سے وصول کریں یا گھر کی ڈیلیوری منتخب کریں۔',
// Property or array item — trailing comma allowed in TypeScript
step: 'مرحلہ {n}',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'واٹس ایپ پر آرڈر کریں',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 121: offers: {
offers: {
// Property or array item — trailing comma allowed in TypeScript
label: 'آرڈر',
// Property or array item — trailing comma allowed in TypeScript
title: 'ہفتہ وار پیشکشیں',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'ہم مسلسل تازہ پیشکشوں کے ساتھ اپ ڈیٹ کرتے ہیں۔ تازہ ترین قیمتوں کے لیے سوشل میڈیا پر فالو کریں۔',
// Property or array item — trailing comma allowed in TypeScript
disclaimer: 'قیمت اس وقت تک جب تک اسٹاک موجود ہے',
// Property or array item — trailing comma allowed in TypeScript
was: 'پہلے',
// Property or array item — trailing comma allowed in TypeScript
now: 'اب',
// Property or array item — trailing comma allowed in TypeScript
order: 'آرڈر',
// Property or array item — trailing comma allowed in TypeScript
viewProduct: 'مصنوعات دیکھیں',
// Line 131: badge: {
badge: {
// Property or array item — trailing comma allowed in TypeScript
fresh: 'تازہ',
// Property or array item — trailing comma allowed in TypeScript
halal: 'حلال',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 135: items: {
items: {
// Property or array item — trailing comma allowed in TypeScript
chickenWings: { name: 'تازہ چکن ونگز PL' },
// Property or array item — trailing comma allowed in TypeScript
lambSteak: { name: 'تازہ لیمب روست آئرلینڈ' },
// Property or array item — trailing comma allowed in TypeScript
beefMince: { name: 'بیف منس 5% چکنائی IRL' },
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
},
// Line 141: social: {
social: {
// Property or array item — trailing comma allowed in TypeScript
label: 'ہمیں فالو کریں',
// Property or array item — trailing comma allowed in TypeScript
title: 'ہمیں فالو کریں',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'فیس بک پر 1,100+ فالوورز · 249 پوسٹس · روزانہ اپ ڈیٹس',
// Property or array item — trailing comma allowed in TypeScript
facebook: 'فیس بک',
// Property or array item — trailing comma allowed in TypeScript
instagram: 'انسٹاگرام',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'واٹس ایپ',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 150: contact: {
contact: {
// Property or array item — trailing comma allowed in TypeScript
label: 'رابطہ',
// Property or array item — trailing comma allowed in TypeScript
title: 'ہم سے ملیں',
// Property or array item — trailing comma allowed in TypeScript
addressLabel: 'پتہ',
// Property or array item — trailing comma allowed in TypeScript
phoneLabel: 'فون',
// Property or array item — trailing comma allowed in TypeScript
hoursLabel: 'اوقات',
// Property or array item — trailing comma allowed in TypeScript
hoursValue: 'ہر دن: {hours}',
// Property or array item — trailing comma allowed in TypeScript
writeUs: 'ہمیں لکھیں',
// Property or array item — trailing comma allowed in TypeScript
callUs: 'ہمیں کال کریں',
// Property or array item — trailing comma allowed in TypeScript
openMaps: 'گوگل میپس میں کھولیں',
// Property or array item — trailing comma allowed in TypeScript
learnMore: 'رابطے کی تفصیلات',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 162: cta: {
cta: {
// Property or array item — trailing comma allowed in TypeScript
title: 'پریمیم حلال گوشت کے لیے تیار ہیں؟',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'آج ہی آرڈر کریں اور حقیقی تازہ، حسب ضرورت حلال گوشت کا فرق محسوس کریں جو آپ کے دروازے تک پہنچایا جائے۔',
// Property or array item — trailing comma allowed in TypeScript
button: 'ہمارا مجموعہ دیکھیں',
// Property or array item — trailing comma allowed in TypeScript
whatsapp: 'واٹس ایپ پر آرڈر کریں',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 169: shop: {
shop: {
// Property or array item — trailing comma allowed in TypeScript
title: 'تمام مصنوعات',
// Property or array item — trailing comma allowed in TypeScript
subtitle: 'پریمیم حلال گوشت، آپ کی پسند کے مطابق',
// Property or array item — trailing comma allowed in TypeScript
noProducts: 'کوئی مصنوعات نہیں ملیں',
// Property or array item — trailing comma allowed in TypeScript
noProductsHint: 'اپنے فلٹرز یا تلاش کی کوشش کو ایڈجسٹ کریں',
// Property or array item — trailing comma allowed in TypeScript
filters: 'فلٹرز',
// Property or array item — trailing comma allowed in TypeScript
productsFound: '{count} مصنوعات ملیں',
// Property or array item — trailing comma allowed in TypeScript
search: 'تلاش',
// Property or array item — trailing comma allowed in TypeScript
searchPlaceholder: 'مصنوعات تلاش کریں...',
// Property or array item — trailing comma allowed in TypeScript
category: 'زمرہ',
// Property or array item — trailing comma allowed in TypeScript
sortBy: 'ترتیب دیں',
// Property or array item — trailing comma allowed in TypeScript
all: 'سب',
// Property or array item — trailing comma allowed in TypeScript
sortFeatured: 'نمایاں',
// Property or array item — trailing comma allowed in TypeScript
sortPriceAsc: 'قیمت: کم سے زیادہ',
// Property or array item — trailing comma allowed in TypeScript
sortPriceDesc: 'قیمت: زیادہ سے کم',
// Property or array item — trailing comma allowed in TypeScript
sortName: 'نام الف سے ی',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 186: product: {
product: {
// Property or array item — trailing comma allowed in TypeScript
backToShop: 'خریداری پر واپس',
// Property or array item — trailing comma allowed in TypeScript
inStock: 'دستیاب',
// Property or array item — trailing comma allowed in TypeScript
outOfStock: 'اسٹاک ختم',
// Property or array item — trailing comma allowed in TypeScript
halalTrust: '100% حلال تصدیق شدہ · روزانہ تازہ · پریمیم معیار',
// Property or array item — trailing comma allowed in TypeScript
yourSelection: 'آپ کا انتخاب',
// Property or array item — trailing comma allowed in TypeScript
aboutProduct: 'اس مصنوع کے بارے میں',
// Property or array item — trailing comma allowed in TypeScript
addToCart: 'ٹوکری میں شامل کریں',
// Property or array item — trailing comma allowed in TypeScript
addedToCart: 'ٹوکری میں شامل ہو گیا',
// Property or array item — trailing comma allowed in TypeScript
decreaseQty: 'مقدار کم کریں',
// Property or array item — trailing comma allowed in TypeScript
increaseQty: 'مقدار بڑھائیں',
// Property or array item — trailing comma allowed in TypeScript
removeWishlist: 'پسندیدہ سے ہٹائیں',
// Property or array item — trailing comma allowed in TypeScript
addWishlist: 'پسندیدہ میں شامل کریں',
// Property or array item — trailing comma allowed in TypeScript
viewProduct: '{name} دیکھیں',
// Property or array item — trailing comma allowed in TypeScript
pieces: '{count} ٹکڑے',
// Property or array item — trailing comma allowed in TypeScript
standardCut: 'معیاری کٹ',
// Property or array item — trailing comma allowed in TypeScript
howManyCuts: 'آپ کتنے کٹ چاہتے ہیں؟',
// Property or array item — trailing comma allowed in TypeScript
howManyCutsHint: 'اپنے آرڈر کے لیے کٹوں کی تعداد منتخب کریں',
// Property or array item — trailing comma allowed in TypeScript
cutsAndStyle: '{cuts} کٹ · {style}',
// Property or array item — trailing comma allowed in TypeScript
selectCutting: 'کاٹنے کا انداز منتخب کریں',
// Property or array item — trailing comma allowed in TypeScript
selectCuttingHint: 'ہمارے قصاب آپ کے {category} کو بالکل آپ کی پسند کے مطابق تیار کریں گے',
// Line 207: fishNote:
fishNote:
// Property or array item — trailing comma allowed in TypeScript
'مچھلی کی مصنوعات ہمارے معیاری پیشہ ورانہ کٹ سے تیار کی جاتی ہیں — صاف، چھلکے اتارے اور پکانے کے لیے تیار۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 210: cutting: {
cutting: {
// Property or array item — trailing comma allowed in TypeScript
nihari: 'نہاری کٹ',
// Property or array item — trailing comma allowed in TypeScript
karahi: 'کڑاہی کٹ',
// Property or array item — trailing comma allowed in TypeScript
qeema: 'قیمہ (کима)',
// Property or array item — trailing comma allowed in TypeScript
boneless: 'بغیر ہڈی',
// Property or array item — trailing comma allowed in TypeScript
steak: 'اسٹیک کٹ',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 217: priceUnit: {
priceUnit: {
// Property or array item — trailing comma allowed in TypeScript
perBird: 'فی مرغی',
// Property or array item — trailing comma allowed in TypeScript
perPack: 'فی پیک',
// Property or array item — trailing comma allowed in TypeScript
perKg: 'فی کلو',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 222: badges: {
badges: {
// Property or array item — trailing comma allowed in TypeScript
bestseller: 'سب سے زیادہ فروخت',
// Property or array item — trailing comma allowed in TypeScript
chefsPick: 'شیف کی پسند',
// Property or array item — trailing comma allowed in TypeScript
premium: 'پریمیم',
// Property or array item — trailing comma allowed in TypeScript
popular: 'مقبول',
// Property or array item — trailing comma allowed in TypeScript
freshCatch: 'تازہ پکڑ',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 229: products: {
products: {
// Line 230: 'chicken-whole': {
'chicken-whole': {
// Property or array item — trailing comma allowed in TypeScript
name: 'پوری مرغی',
// Property or array item — trailing comma allowed in TypeScript
description: 'فارم سے تازہ پوری حلال مرغی، بھوننے یا کڑی کے لیے بہترین۔',
// Line 233: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'ہماری پوری مرغیاں تصدیق شدہ حلال فارموں سے حاصل کی جاتی ہیں اور بہترین تازگی میں پہنچائی جاتی ہیں۔ ہر پرندہ معیار کے لیے ہاتھ سے منتخب کیا جاتا ہے، نرم گوشت اور صاف پروسیسنگ کے ساتھ۔ اپنی پسندیدہ ٹکڑوں کی تعداد منتخب کریں اور ہم اسے بالکل ویسے تیار کریں گے جیسا آپ چاہیں۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 236: 'chicken-breast': {
'chicken-breast': {
// Property or array item — trailing comma allowed in TypeScript
name: 'مرغی کا سینہ',
// Property or array item — trailing comma allowed in TypeScript
description: 'دبلا، بغیر ہڈی کا مرغی کا سینہ — گرل اور صحت مند کھانوں کے لیے بہترین۔',
// Line 239: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'پریمیم بغیر ہڈی کا مرغی کا سینہ، تراشا ہوا اور پکانے کے لیے تیار۔ کباب، سٹیر فرائی اور صحت مند ہفتے کی رات کے کھانوں کے لیے بہترین۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 242: 'chicken-thighs': {
'chicken-thighs': {
// Property or array item — trailing comma allowed in TypeScript
name: 'مرغی کی ران',
// Property or array item — trailing comma allowed in TypeScript
description: 'کڑی اور گرل کے لیے بھرپور ذائقے والی رس بھرے حلال مرغی کی ران۔',
// Line 245: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'ہماری مرغی کی ران اپنی رس اور گہرے ذائقے کے لیے مشہور ہے۔ چاہے روایتی کڑاہی بنائیں یا ہفتے کے آخر میں BBQ، یہ ران ہر بار بہترین نتائج دیتی ہے۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 248: 'chicken-wings': {
'chicken-wings': {
// Property or array item — trailing comma allowed in TypeScript
name: 'مرغی کے بازو',
// Property or array item — trailing comma allowed in TypeScript
description: 'تلی، بیکنگ یا گرل کے لیے پارٹی کے لیے تیار حلال مرغی کے بازو۔',
// Line 251: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'کرکرے، ذائقے دار مرغی کے بازو حلال طریقے سے تیار اور تازہ پہنچائے جاتے ہیں۔ میچ کی راتوں اور خاندانی محفلوں کی پسندیدہ۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 254: 'beef-nihari': {
'beef-nihari': {
// Property or array item — trailing comma allowed in TypeScript
name: 'نہاری کے لیے گائے کا گوشت',
// Property or array item — trailing comma allowed in TypeScript
description: 'آہستہ پکانے کے لیے تیار گائے کے گوشت کے کٹس، روایتی نہاری کے لیے بہترین۔',
// Line 257: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'خاص طور پر منتخب گائے کے گوشت کے کٹس جو آہستہ پکی ہوئی نہاری کے لیے بہترین ہیں۔ کولیجن اور ذائقے سے بھرپور، یہ کٹس گھنٹوں کی دھیمی آنچ پر خوبصورتی سے گل جاتے ہیں۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 260: 'beef-steak': {
'beef-steak': {
// Property or array item — trailing comma allowed in TypeScript
name: 'پریمیم گائے کا اسٹیک',
// Property or array item — trailing comma allowed in TypeScript
description: 'ریستوران معیار کے حلال اسٹیک کٹس، بہترین سیک کے لیے۔',
// Line 263: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'بہترین حلال ذرائع سے ہاتھ سے کاٹے گئے پریمیم اسٹیک۔ چربیلے، نرم اور آپ کی گرل یا کاسٹ آئرن پین کے لیے تیار۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 266: 'beef-mince': {
'beef-mince': {
// Property or array item — trailing comma allowed in TypeScript
name: 'گائے کا قیمہ',
// Property or array item — trailing comma allowed in TypeScript
description: 'کباب، برگر اور قیمہ کے لیے تازہ حلال گائے کا قیمہ۔',
// Line 269: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'باریک پیسا ہوا حلال گائے کا قیمہ بہترین چربی کے تناسب کے ساتھ رسیلے کباب، ذائقے دار قیمہ اور گھریلو برگرز کے لیے۔ روزانہ تازہ پیسا جاتا ہے۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 272: 'beef-boneless': {
'beef-boneless': {
// Property or array item — trailing comma allowed in TypeScript
name: 'بغیر ہڈی کے گائے کے مکعب',
// Property or array item — trailing comma allowed in TypeScript
description: 'کڑاہی، بریانی اور اسٹیو کے لیے کثیر الاستعمال بغیر ہڈی کے مکعب۔',
// Line 275: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'یکساں بغیر ہڈی کے گائے کے مکعب تیز پکوان کے لیے کامل سائز میں کاٹے گئے۔ کڑاہی، پلاؤ اور سٹیر فرائی کے لیے بہترین۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 278: 'lamb-shoulder': {
'lamb-shoulder': {
// Property or array item — trailing comma allowed in TypeScript
name: 'بکرے کا کندھا',
// Property or array item — trailing comma allowed in TypeScript
description: 'آہستہ بھوننے اور کڑی کے لیے بھرپور ذائقے والا بکرے کا کندھا۔',
// Line 281: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'خوبصورت چربی کے نمونے والے پریمیم حلال بکرے کا کندھا۔ آہستہ بھونی ہوئی دعوتوں، بھرپور کڑیوں اور روایتی خاندانی کھانوں کے لیے بہترین۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 284: 'lamb-leg': {
'lamb-leg': {
// Property or array item — trailing comma allowed in TypeScript
name: 'بکرے کی ٹانگ',
// Property or array item — trailing comma allowed in TypeScript
description: 'بھوننے، گرل اور خاص مواقع کے لیے نرم بکرے کی ٹانگ۔',
// Line 287: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'تصدیق شدہ حلال ذرائع سے پوری یا حصوں میں بکرے کی ٹانگ۔ عید کی تقریبات، ڈنر پارٹیوں اور اتوار کی بھوننے کے لیے مرکزی کٹ۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 290: 'lamb-chops': {
'lamb-chops': {
// Property or array item — trailing comma allowed in TypeScript
name: 'بکرے کے کٹlets',
// Property or array item — trailing comma allowed in TypeScript
description: 'گرل اور گھر میں فائن ڈائننگ کے لیے پریمیم بکرے کے کٹlets۔',
// Line 293: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'موٹے کاٹے گئے حلال بکرے کے کٹlets گرل کے لیے بہترین چربی کی تہہ کے ساتھ۔ ریستوران معیار، آپ کے باورچی خانے تک پہنچایا گیا۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 296: 'lamb-mince': {
'lamb-mince': {
// Property or array item — trailing comma allowed in TypeScript
name: 'بکرے کا قیمہ',
// Property or array item — trailing comma allowed in TypeScript
description: 'کباب، سموسے اور قیمہ کے لیے تازہ حلال بکرے کا قیمہ۔',
// Line 299: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'باریک پیسا ہوا بکرے کا قیمہ بھرپور ذائقے کے ساتھ۔ سیخ کباب، بکرے کا قیمہ اور بھرے ہوئے پراٹھوں کے لیے ضروری۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 302: 'fish-salmon': {
'fish-salmon': {
// Property or array item — trailing comma allowed in TypeScript
name: 'اٹلانٹک سامن فلیٹ',
// Property or array item — trailing comma allowed in TypeScript
description: 'مکھن جیسے نرم سامن فلیٹس، جلد سمیت اور پین میں سیکنے کے لیے تیار۔',
// Line 305: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'پریمیم اٹلانٹک سامن فلیٹس بھرپور اومیگا 3 کے ساتھ۔ صاف، حصوں میں تقسیم اور زیادہ سے زیادہ تازگی کے لیے ویکیوم سیلڈ۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 308: 'fish-rohu': {
'fish-rohu': {
// Property or array item — trailing comma allowed in TypeScript
name: 'روہو مچھلی',
// Property or array item — trailing comma allowed in TypeScript
description: 'پوری روہو مچھلی، صاف اور چھلکے اتارے — جنوبی ایشیائی پسندیدہ۔',
// Line 311: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'تازہ روہو مچھلی، جنوبی ایشیائی کھانوں کا ایک بنیادی جزو۔ صاف، چھلکے اتارے اور اندر صاف کی گئی۔ مچھلی کی کڑی، تلی ہوئی مچھلی اور روایتی ترکیبوں کے لیے بہترین۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 314: 'fish-prawns': {
'fish-prawns': {
// Property or array item — trailing comma allowed in TypeScript
name: 'بڑی جھینگے',
// Property or array item — trailing comma allowed in TypeScript
description: 'گرل، کڑی اور بریانی کے لیے بڑے چھلکے سمیت جھینگے۔',
// Line 317: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'پریمیم بڑی جھینگے، صاف کی گئی اور پکانے کے لیے تیار۔ میٹھا، مضبوط گوشت جو کڑی اور تندوری تیاریوں میں خوبصورتی سے کھڑا رہتا ہے۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 320: 'fish-basa': {
'fish-basa': {
// Property or array item — trailing comma allowed in TypeScript
name: 'باسا فلیٹ',
// Property or array item — trailing comma allowed in TypeScript
description: 'ہلکا، پرت دار باسا فلیٹس — ابتدائیوں اور بچوں کے لیے بہترین۔',
// Line 323: longDescription:
longDescription:
// Property or array item — trailing comma allowed in TypeScript
'بغیر ہڈی کے باسا فلیٹس ہلکے، نفیس ذائقے کے ساتھ۔ پکانا آسان اور کثیر الاستعمال — مچھلی کے ٹیکو، بیکنگ اور ہلکی کڑیوں کے لیے بہترین۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
},
// Line 327: cart: {
cart: {
// Property or array item — trailing comma allowed in TypeScript
title: 'آپ کی ٹوکری',
// Property or array item — trailing comma allowed in TypeScript
itemsCount: 'آپ کی ٹوکری میں {count} آئٹم',
// Property or array item — trailing comma allowed in TypeScript
empty: 'آپ کی ٹوکری خالی ہے',
// Property or array item — trailing comma allowed in TypeScript
emptyHint: 'ہمارے پریمیم حلال مجموعے میں سے دیکھیں اور آئٹمز شامل کریں۔',
// Property or array item — trailing comma allowed in TypeScript
startShopping: 'خریداری شروع کریں',
// Property or array item — trailing comma allowed in TypeScript
customization: 'حسب ضرورت:',
// Property or array item — trailing comma allowed in TypeScript
orderSummary: 'آرڈر کا خلاصہ',
// Property or array item — trailing comma allowed in TypeScript
subtotal: 'ذیلی کل',
// Property or array item — trailing comma allowed in TypeScript
delivery: 'ڈیلیوری',
// Property or array item — trailing comma allowed in TypeScript
free: 'مفت',
// Property or array item — trailing comma allowed in TypeScript
freeDeliveryHint: '500 کرون سے زیادہ کے آرڈرز پر مفت ڈیلیوری',
// Property or array item — trailing comma allowed in TypeScript
total: 'کل',
// Property or array item — trailing comma allowed in TypeScript
proceedCheckout: 'چیک آؤٹ پر جائیں',
// Property or array item — trailing comma allowed in TypeScript
continueShopping: 'خریداری جاری رکھیں',
// Property or array item — trailing comma allowed in TypeScript
removeItem: 'آئٹم ہٹائیں',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 344: checkout: {
checkout: {
// Property or array item — trailing comma allowed in TypeScript
title: 'محفوظ چیک آؤٹ',
// Property or array item — trailing comma allowed in TypeScript
backToCart: 'ٹوکری پر واپس',
// Property or array item — trailing comma allowed in TypeScript
noItems: 'چیک آؤٹ کے لیے کوئی آئٹم نہیں',
// Property or array item — trailing comma allowed in TypeScript
goToShop: 'خریداری پر جائیں',
// Property or array item — trailing comma allowed in TypeScript
orderConfirmed: 'آرڈر کی تصدیق ہو گئی!',
// Property or array item — trailing comma allowed in TypeScript
thankYou: 'آپ کے آرڈر کا شکریہ۔ آپ کا پریمیم حلال گوشت تیار کیا جا رہا ہے۔',
// Property or array item — trailing comma allowed in TypeScript
orderId: 'آرڈر آئی ڈی: {id}',
// Property or array item — trailing comma allowed in TypeScript
viewOrders: 'آرڈرز دیکھیں',
// Property or array item — trailing comma allowed in TypeScript
haveAccount: 'اکاؤنٹ ہے؟',
// Property or array item — trailing comma allowed in TypeScript
signIn: 'سائن ان کریں',
// Property or array item — trailing comma allowed in TypeScript
fasterCheckout: 'تیز چیک آؤٹ کے لیے۔',
// Property or array item — trailing comma allowed in TypeScript
deliveryDetails: 'ڈیلیوری کی تفصیلات',
// Property or array item — trailing comma allowed in TypeScript
fullName: 'پورا نام',
// Property or array item — trailing comma allowed in TypeScript
email: 'ای میل',
// Property or array item — trailing comma allowed in TypeScript
phone: 'فون',
// Property or array item — trailing comma allowed in TypeScript
street: 'گلی کا پتہ',
// Property or array item — trailing comma allowed in TypeScript
city: 'شہر',
// Property or array item — trailing comma allowed in TypeScript
state: 'صوبہ',
// Property or array item — trailing comma allowed in TypeScript
zip: 'پوسٹل کوڈ',
// Property or array item — trailing comma allowed in TypeScript
payment: 'ادائیگی',
// Property or array item — trailing comma allowed in TypeScript
creditCard: 'کریڈٹ کارڈ',
// Property or array item — trailing comma allowed in TypeScript
cashOnDelivery: 'ڈیلیوری پر نقد',
// 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: 'CVV',
// Property or array item — trailing comma allowed in TypeScript
qty: 'مقدار: {count}',
// Property or array item — trailing comma allowed in TypeScript
processing: 'پروسیسنگ...',
// Property or array item — trailing comma allowed in TypeScript
pay: '{amount} ادا کریں',
// Property or array item — trailing comma allowed in TypeScript
secure: 'محفوظ 256 بٹ SSL انکرپشن',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 375: auth: {
auth: {
// Property or array item — trailing comma allowed in TypeScript
welcomeBack: 'خوش آمدید',
// Property or array item — trailing comma allowed in TypeScript
createAccount: 'اکاؤنٹ بنائیں',
// Property or array item — trailing comma allowed in TypeScript
joinTagline: 'پریمیم شاپنگ کے تجربے کے لیے {name} میں شامل ہوں',
// Property or array item — trailing comma allowed in TypeScript
signInTagline: 'اپنے {name} اکاؤنٹ میں سائن ان کریں',
// Property or array item — trailing comma allowed in TypeScript
fullName: 'پورا نام',
// Property or array item — trailing comma allowed in TypeScript
email: 'ای میل',
// Property or array item — trailing comma allowed in TypeScript
password: 'پاس ورڈ',
// Property or array item — trailing comma allowed in TypeScript
phone: 'فون',
// Property or array item — trailing comma allowed in TypeScript
street: 'گلی کا پتہ',
// Property or array item — trailing comma allowed in TypeScript
city: 'شہر',
// Property or array item — trailing comma allowed in TypeScript
state: 'صوبہ',
// Property or array item — trailing comma allowed in TypeScript
zip: 'پوسٹل کوڈ',
// Property or array item — trailing comma allowed in TypeScript
signIn: 'سائن ان',
// Property or array item — trailing comma allowed in TypeScript
register: 'اکاؤنٹ بنائیں',
// Property or array item — trailing comma allowed in TypeScript
hasAccount: 'پہلے سے اکاؤنٹ ہے؟ سائن ان کریں',
// Property or array item — trailing comma allowed in TypeScript
noAccount: 'اکاؤنٹ نہیں ہے؟ رجسٹر کریں',
// Property or array item — trailing comma allowed in TypeScript
invalidCredentials: 'غلط ای میل یا پاس ورڈ۔ {email} / demo123 آزمائیں',
// Property or array item — trailing comma allowed in TypeScript
demo: 'ڈیمو: {email} / demo123',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 395: account: {
account: {
// Property or array item — trailing comma allowed in TypeScript
title: 'میرا اکاؤنٹ',
// Property or array item — trailing comma allowed in TypeScript
welcome: 'خوش آمدید، {name}',
// Property or array item — trailing comma allowed in TypeScript
memberSince: '{date} سے رکن',
// Property or array item — trailing comma allowed in TypeScript
myWishlist: 'میری پسندیدہ فہرست',
// Property or array item — trailing comma allowed in TypeScript
signOut: 'سائن آؤٹ',
// Property or array item — trailing comma allowed in TypeScript
orderHistory: 'آرڈر کی تاریخ',
// Property or array item — trailing comma allowed in TypeScript
noOrders: 'ابھی تک کوئی آرڈر نہیں',
// Property or array item — trailing comma allowed in TypeScript
startShopping: 'خریداری شروع کریں',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 405: wishlist: {
wishlist: {
// Property or array item — trailing comma allowed in TypeScript
title: 'میری پسندیدہ فہرست',
// Property or array item — trailing comma allowed in TypeScript
saved: '{count} محفوظ آئٹم',
// Property or array item — trailing comma allowed in TypeScript
empty: 'آپ کی پسندیدہ فہرست خالی ہے',
// Property or array item — trailing comma allowed in TypeScript
emptyHint: 'اپنی پسندیدہ مصنوعات محفوظ کریں تاکہ بعد میں خرید سکیں۔',
// Property or array item — trailing comma allowed in TypeScript
browse: 'مصنوعات دیکھیں',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 412: about: {
about: {
// Property or array item — trailing comma allowed in TypeScript
title: '{name} کے بارے میں',
// Translated UI string for current language (sv / en / ur)
subtitle:
// Property or array item — trailing comma allowed in TypeScript
'پریمیم 100% حلال گوشت آپ کی میز تک — تازہ، حسب ضرورت اور احتیاط سے پہنچایا گیا۔',
// Property or array item — trailing comma allowed in TypeScript
ourStory: 'ہماری کہانی',
// Line 417: storyP1:
storyP1:
// Property or array item — trailing comma allowed in TypeScript
'{name} ایک سادہ مشن کے ساتھ قائم ہوا: ہر خاندان کے لیے پریمیم حلال گوشت قابل رسائی بنانا، معیار، تازگی یا مذہبی تعمیل سے سمجھوتہ کیے بغیر۔ ہم سمجھتے ہیں کہ بہت سے گھروں کے لیے صحیح کٹ صحیح طریقے سے تیار کرنا عیش نہیں — یہ ضروری ہے۔',
// Line 419: storyP2:
storyP2:
// Property or array item — trailing comma allowed in TypeScript
'مرغی کے لیے ٹکڑوں کی تعداد منتخب کرنے سے لے کر گائے اور بکرے کے گوشت کے لیے نہاری یا کڑاہی کٹس کا انتخاب کرنے تک، ہم ہر آرڈر کے مرکز میں حسب ضرورت بناتے ہیں۔ ہمارے ماہر قصاب ہر آرڈر ہاتھ سے تیار کرتے ہیں، اور ہماری درجہ حرارت کنٹرولڈ ڈیلیوری یقینی بناتی ہے کہ آپ کا گوشت اسی دن کی طرح تازہ پہنچے جیسے کاٹا گیا تھا۔',
// Property or array item — trailing comma allowed in TypeScript
halalTitle: 'حلال سرٹیفیکیشن',
// Line 422: halalDesc:
halalDesc:
// Property or array item — trailing comma allowed in TypeScript
'{name} کی ہر مصنوع تصدیق شدہ حلال سپلائرز سے حاصل کی جاتی ہے۔ ہمارا سپلائی چین مکمل طور پر قابل سراغ ہے، اور ہم حلال ذبح اور پروسیسنگ کے معیارات کی سخت تعمیل برقرار رکھتے ہیں۔',
// Property or array item — trailing comma allowed in TypeScript
freshDaily: 'روزانہ تازہ',
// Property or array item — trailing comma allowed in TypeScript
freshDailyDesc: 'ہر صبح قابل اعتماد فارموں سے حاصل',
// Property or array item — trailing comma allowed in TypeScript
premiumQuality: 'پریمیم معیار',
// Property or array item — trailing comma allowed in TypeScript
premiumQualityDesc: 'ماہر قصابوں کے ذریعے منتخب کردہ کٹس',
// Property or array item — trailing comma allowed in TypeScript
fastDelivery: 'تیز ڈیلیوری',
// Property or array item — trailing comma allowed in TypeScript
fastDeliveryDesc: 'درجہ حرارت کنٹرولڈ اسی دن کی ڈیلیوری',
// Property or array item — trailing comma allowed in TypeScript
deliveryTitle: 'ڈیلیوری کی معلومات',
// Property or array item — trailing comma allowed in TypeScript
delivery1: 'ہم اپنی پروسیسنگ سہولت سے 40 کلومیٹر کے دائرے میں ڈیلیوری کرتے ہیں۔',
// Property or array item — trailing comma allowed in TypeScript
delivery2: 'دوپہر 2 بجے سے پہلے کے آرڈرز اسی دن کی ڈیلیوری کے اہل ہیں۔',
// Property or array item — trailing comma allowed in TypeScript
delivery3: '500 کرون سے زیادہ کے آرڈرز پر مفت ڈیلیوری۔ معیاری ڈیلیوری فیس: 49 کرون۔',
// Property or array item — trailing comma allowed in TypeScript
delivery4: 'تمام مصنوعات ویکیوم سیلڈ اور انسولیٹڈ پیکجنگ میں منتقل کی جاتی ہیں۔',
// Property or array item — trailing comma allowed in TypeScript
contactTitle: 'ہم سے رابطہ کریں',
// Property or array item — trailing comma allowed in TypeScript
address: 'Tingvallavägen 11, 195 31 Märsta',
// Property or array item — trailing comma allowed in TypeScript
privacyTitle: 'رازداری کی پالیسی',
// Line 438: privacyText:
privacyText:
// Property or array item — trailing comma allowed in TypeScript
'ہم صرف وہ معلومات جمع کرتے ہیں جو آپ کے آرڈرز پر کارروائی کے لیے ضروری ہیں — نام، رابطے کی تفصیلات اور ڈیلیوری کا پتہ۔ ہم آپ کا ڈیٹا تیسرے فریق کو نہیں بیچتے۔ ادائیگی کی تفصیلات محفوظ طریقے سے ہمارے پارٹنرز کے ذریعے سنبھالی جاتی ہیں۔',
// Property or array item — trailing comma allowed in TypeScript
termsTitle: 'سروس کی شرائط',
// Line 441: termsText:
termsText:
// Property or array item — trailing comma allowed in TypeScript
'تمام قیمتیں SEK میں دکھائی جاتی ہیں اور بغیر اطلاع کے تبدیل ہو سکتی ہیں۔ آرڈرز دستیابی پر منحصر ہیں۔ تمام گوشت کی مصنوعات پر حلال سرٹیفیکیشن لاگو ہوتا ہے۔ ڈیلیوری کے اوقات تخمینے ہیں اور مصروف اوقات میں مختلف ہو سکتے ہیں۔',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 444: footer: {
footer: {
// Line 445: tagline:
tagline:
// Property or array item — trailing comma allowed in TypeScript
'پریمیم 100% حلال گوشت ڈیلیوری۔ تازہ، حسب ضرورت کٹس آپ کے دروازے تک بغیر کسی سمجھوتے کے معیار کے ساتھ پہنچائی جاتی ہیں۔',
// Property or array item — trailing comma allowed in TypeScript
shop: 'خریداری',
// Property or array item — trailing comma allowed in TypeScript
company: 'کمپنی',
// Property or array item — trailing comma allowed in TypeScript
contact: 'رابطہ',
// Property or array item — trailing comma allowed in TypeScript
rights: 'جملہ حقوق محفوظ ہیں۔',
// Property or array item — trailing comma allowed in TypeScript
phone: '072-585 50 50',
// Property or array item — trailing comma allowed in TypeScript
hours: 'ہر دن کھلا {hours}',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 454: notFound: {
notFound: {
// Property or array item — trailing comma allowed in TypeScript
title: '404',
// Property or array item — trailing comma allowed in TypeScript
message: 'صفحہ نہیں ملا',
// Property or array item — trailing comma allowed in TypeScript
goHome: 'ہوم پر جائیں',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 459: orderStatus: {
orderStatus: {
// Property or array item — trailing comma allowed in TypeScript
pending: 'زیر التوا',
// Property or array item — trailing comma allowed in TypeScript
confirmed: 'تصدیق شدہ',
// Property or array item — trailing comma allowed in TypeScript
preparing: 'تیاری میں',
// Property or array item — trailing comma allowed in TypeScript
'out-for-delivery': 'ڈیلیوری کے لیے روانہ',
// Property or array item — trailing comma allowed in TypeScript
delivered: 'پہنچا دیا گیا',
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
};
@@ -0,0 +1,28 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/i18n/types.ts
* NOT used by the app — read this to learn how the real file works
*/
// Export TypeScript type — defines data shape used across the app
export type Locale = 'en' | 'sv' | 'ur';
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const LOCALES: { code: Locale; label: string; nativeLabel: string }[] = [
// Property or array item — trailing comma allowed in TypeScript
{ code: 'sv', label: 'Swedish', nativeLabel: 'Svenska' },
// Property or array item — trailing comma allowed in TypeScript
{ code: 'en', label: 'English', nativeLabel: 'English' },
// Property or array item — trailing comma allowed in TypeScript
{ code: 'ur', label: 'Urdu', nativeLabel: 'اردو' },
// End of array literal
];
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const DEFAULT_LOCALE: Locale = 'sv';
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export interface TranslationDict {
// Line 12: [key: string]: string | TranslationDict;
[key: string]: string | TranslationDict;
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,74 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/lib/constants.ts
* NOT used by the app — read this to learn how the real file works
*/
// Import project module (@/ alias = src/ folder in tsconfig)
import { CutCount, CuttingStyleKey } from '@/types';
// Import from a relative file in the same project
import { IMAGES } from './images';
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const CUT_COUNTS: CutCount[] = [4, 8, 10, 12];
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const CUTTING_STYLE_KEYS: CuttingStyleKey[] = [
// Property or array item — trailing comma allowed in TypeScript
'nihari',
// Property or array item — trailing comma allowed in TypeScript
'karahi',
// Property or array item — trailing comma allowed in TypeScript
'qeema',
// Property or array item — trailing comma allowed in TypeScript
'boneless',
// Property or array item — trailing comma allowed in TypeScript
'steak',
// End of array literal
];
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const MEAT_CATEGORIES = ['chicken', 'beef', 'lamb'] as const;
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const CATEGORY_IDS = ['chicken', 'beef', 'lamb', 'fish'] as const;
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const CATEGORY_IMAGES = IMAGES.categories;
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const SITE_NAME = 'Kött Gård';
// Named export constant — shared config/data imported elsewhere
export const SITE_EMAIL = 'hello@kottgard.se';
// Named export constant — shared config/data imported elsewhere
export const DEMO_EMAIL = 'demo@kottgard.se';
// Named export constant — shared config/data imported elsewhere
export const SITE_PHONE = '+46 72 585 50 50';
// Named export constant — shared config/data imported elsewhere
export const SITE_PHONE_DISPLAY = '072-585 50 50';
// Named export constant — shared config/data imported elsewhere
export const SITE_ADDRESS = 'Tingvallavägen 11, 195 31 Märsta';
// Named export constant — shared config/data imported elsewhere
export const SITE_LOCATION = 'Märsta · Sverige';
// Named export constant — shared config/data imported elsewhere
export const SITE_HOURS = '10:0019:00';
// Named export constant — shared config/data imported elsewhere
export const FACEBOOK_URL = 'https://www.facebook.com/kottgard/';
// Named export constant — shared config/data imported elsewhere
export const INSTAGRAM_URL = 'https://www.instagram.com/kottgard';
// Named export constant — shared config/data imported elsewhere
export const MAPS_URL = 'https://share.google/fUkkhDNlhDTcImKo5';
// (blank line — separates logical blocks for readability)
// Line 32: const WHATSAPP_BASE = 'Hej Kött Gård! Jag vill beställa';
const WHATSAPP_BASE = 'Hej Kött Gård! Jag vill beställa';
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function whatsappOrderUrl(product?: string): string {
// Line 35: const text = product ? `${WHATSAPP_BASE} ${product}.` : `...
const text = product ? `${WHATSAPP_BASE} ${product}.` : `${WHATSAPP_BASE}.`;
// Return value from function
return `https://wa.me/46725855050?text=${encodeURIComponent(text)}`;
// 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 WHATSAPP_URL = whatsappOrderUrl();
@@ -0,0 +1,114 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/lib/customization.ts
* NOT used by the app — read this to learn how the real file works
*/
// Import project module (@/ alias = src/ folder in tsconfig)
import { Category, MeatCustomization, ProductCustomization } from '@/types';
// (blank line — separates logical blocks for readability)
// TypeScript type alias — union or shorthand for complex types
type Translator = (path: string, params?: Record<string, string | number>) => string;
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function getDefaultCustomization(category: Category): ProductCustomization {
// Conditional branch — different behavior based on runtime value
if (category === 'fish') {
// Return value from function
return { type: 'fish' };
// Closing brace — end of block (function, if, object, JSX)
}
// Return value from function
return {
// Property or array item — trailing comma allowed in TypeScript
type: category,
// Property or array item — trailing comma allowed in TypeScript
cuts: 8,
// Property or array item — trailing comma allowed in TypeScript
cuttingStyle: 'karahi',
// 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)
// Named export — utility function other files can import
export function getCustomizationKey(customization: ProductCustomization): string {
// Conditional branch — different behavior based on runtime value
if (customization.type === 'fish') {
// Return value from function
return 'standard';
// Closing brace — end of block (function, if, object, JSX)
}
// Return value from function
return `cuts-${customization.cuts}::${customization.cuttingStyle}`;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function getCustomizationLabel(
// Property or array item — trailing comma allowed in TypeScript
customization: ProductCustomization,
// Line 25: t: Translator
t: Translator
// Line 26: ): string {
): string {
// Conditional branch — different behavior based on runtime value
if (customization.type === 'fish') {
// Return value from function
return t('product.standardCut');
// Closing brace — end of block (function, if, object, JSX)
}
// Return value from function
return t('product.cutsAndStyle', {
// Property or array item — trailing comma allowed in TypeScript
cuts: customization.cuts,
// Property or array item — trailing comma allowed in TypeScript
style: t(`cutting.${customization.cuttingStyle}`),
// 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)
// Named export — utility function other files can import
export function getCartItemKey(
// Property or array item — trailing comma allowed in TypeScript
productId: string,
// Line 38: customization: ProductCustomization
customization: ProductCustomization
// Line 39: ): string {
): string {
// Return value from function
return `${productId}::${getCustomizationKey(customization)}`;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function updateMeatCustomization(
// Property or array item — trailing comma allowed in TypeScript
current: ProductCustomization,
// Property or array item — trailing comma allowed in TypeScript
category: Category,
// Line 46: update: Partial<Pick<MeatCustomization, 'cuts' | 'cutting...
update: Partial<Pick<MeatCustomization, 'cuts' | 'cuttingStyle'>>
// Line 47: ): MeatCustomization {
): MeatCustomization {
// Line 48: const base =
const base =
// Line 49: current.type !== 'fish' && current.type === category
current.type !== 'fish' && current.type === category
// Line 50: ? current
? current
// Line 51: : (getDefaultCustomization(category) as MeatCustomization);
: (getDefaultCustomization(category) as MeatCustomization);
// (blank line — separates logical blocks for readability)
// Return value from function
return {
// Property or array item — trailing comma allowed in TypeScript
type: category as MeatCustomization['type'],
// Property or array item — trailing comma allowed in TypeScript
cuts: update.cuts ?? base.cuts,
// Property or array item — trailing comma allowed in TypeScript
cuttingStyle: update.cuttingStyle ?? base.cuttingStyle,
// Closing brace — end of block (function, if, object, JSX)
};
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,128 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/lib/demo-orders.ts
* NOT used by the app — read this to learn how the real file works
*/
// Import project module (@/ alias = src/ folder in tsconfig)
import { Order } from '@/types';
// Import from a relative file in the same project
import { products } from './products';
// Import from a relative file in the same project
import { getDefaultCustomization, getCartItemKey } from './customization';
// (blank line — separates logical blocks for readability)
// Function declaration — reusable logic in this file
function orderItem(
// Property or array item — trailing comma allowed in TypeScript
productId: string,
// Property or array item — trailing comma allowed in TypeScript
quantity: number,
// Line 8: customizationLabel: string
customizationLabel: string
// Line 9: ) {
) {
// Array.find — get first matching item or undefined
const product = products.find((p) => p.id === productId)!;
// Line 11: const customization = getDefaultCustomization(product.cat...
const customization = getDefaultCustomization(product.category);
// Return value from function
return {
// Property or array item — trailing comma allowed in TypeScript
id: getCartItemKey(productId, customization),
// Property or array item — trailing comma allowed in TypeScript
product,
// Property or array item — trailing comma allowed in TypeScript
quantity,
// Property or array item — trailing comma allowed in TypeScript
customization,
// Property or array item — trailing comma allowed in TypeScript
customizationLabel,
// 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)
// Line 21: const demoAddress = {
const demoAddress = {
// Property or array item — trailing comma allowed in TypeScript
street: 'Tingvallavägen 11',
// Property or array item — trailing comma allowed in TypeScript
city: 'Märsta',
// Property or array item — trailing comma allowed in TypeScript
state: 'Stockholm',
// Property or array item — trailing comma allowed in TypeScript
zip: '195 31',
// 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 DEMO_ORDERS: Order[] = [
// Line 29: {
{
// Property or array item — trailing comma allowed in TypeScript
id: 'KG-2026-0042',
// Line 31: items: [
items: [
// Property or array item — trailing comma allowed in TypeScript
orderItem('beef-steak', 2, '8 cuts · Karahi'),
// Property or array item — trailing comma allowed in TypeScript
orderItem('lamb-shoulder', 1, '8 cuts · Nihari'),
// End of array literal
],
// Property or array item — trailing comma allowed in TypeScript
total: 647,
// Property or array item — trailing comma allowed in TypeScript
status: 'delivered',
// Property or array item — trailing comma allowed in TypeScript
createdAt: '2026-06-10T14:30:00.000Z',
// Property or array item — trailing comma allowed in TypeScript
deliveryAddress: demoAddress,
// Property or array item — trailing comma allowed in TypeScript
paymentMethod: 'card',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 41: {
{
// Property or array item — trailing comma allowed in TypeScript
id: 'KG-2026-0051',
// Line 43: items: [
items: [
// Property or array item — trailing comma allowed in TypeScript
orderItem('chicken-whole', 2, '8 cuts · Karahi'),
// Property or array item — trailing comma allowed in TypeScript
orderItem('beef-mince', 1, '8 cuts · Qeema'),
// Property or array item — trailing comma allowed in TypeScript
orderItem('fish-prawns', 1, 'Standard cut'),
// End of array literal
],
// Property or array item — trailing comma allowed in TypeScript
total: 556,
// Property or array item — trailing comma allowed in TypeScript
status: 'out-for-delivery',
// Property or array item — trailing comma allowed in TypeScript
createdAt: '2026-06-16T09:15:00.000Z',
// Property or array item — trailing comma allowed in TypeScript
deliveryAddress: demoAddress,
// Property or array item — trailing comma allowed in TypeScript
paymentMethod: 'swish',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 54: {
{
// Property or array item — trailing comma allowed in TypeScript
id: 'KG-2026-0058',
// Property or array item — trailing comma allowed in TypeScript
items: [orderItem('lamb-leg', 1, '8 cuts · Steak'), orderItem('fish-salmon', 1, 'Standard cut')],
// Property or array item — trailing comma allowed in TypeScript
total: 418,
// Property or array item — trailing comma allowed in TypeScript
status: 'preparing',
// Property or array item — trailing comma allowed in TypeScript
createdAt: '2026-06-17T08:00:00.000Z',
// Property or array item — trailing comma allowed in TypeScript
deliveryAddress: demoAddress,
// Property or array item — trailing comma allowed in TypeScript
paymentMethod: 'card',
// Closing brace — end of block (function, if, object, JSX)
},
// End of array literal
];
+170
View File
@@ -0,0 +1,170 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/lib/images.ts
* NOT used by the app — read this to learn how the real file works
*/
// Block comment — documents the file or function below
/**
// Block comment — documents the file or function below
* All imagery: raw, fresh, uncooked meat & seafood only.
// Block comment — documents the file or function below
* High-quality Unsplash photos (verified URLs).
// Block comment — documents the file or function below
*/
// (blank line — separates logical blocks for readability)
// TypeScript type alias — union or shorthand for complex types
type UnsplashOptions = {
// Line 7: height?: number;
height?: number;
// Line 8: crop?: 'center' | 'top' | 'bottom' | 'entropy';
crop?: 'center' | 'top' | 'bottom' | 'entropy';
// Closing brace — end of block (function, if, object, JSX)
};
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function unsplashUrl(
// Property or array item — trailing comma allowed in TypeScript
path: string,
// Property or array item — trailing comma allowed in TypeScript
width: number,
// Line 14: options?: UnsplashOptions
options?: UnsplashOptions
// Line 15: ): string {
): string {
// Line 16: const id = path.startsWith('photo-') ? path : `photo-${pa...
const id = path.startsWith('photo-') ? path : `photo-${path}`;
// Line 17: const params = new URLSearchParams({
const params = new URLSearchParams({
// Property or array item — trailing comma allowed in TypeScript
auto: 'format',
// Property or array item — trailing comma allowed in TypeScript
fit: 'crop',
// Property or array item — trailing comma allowed in TypeScript
w: String(width),
// Property or array item — trailing comma allowed in TypeScript
q: '90',
// Closing brace — end of block (function, if, object, JSX)
});
// Conditional branch — different behavior based on runtime value
if (options?.height) params.set('h', String(options.height));
// Conditional branch — different behavior based on runtime value
if (options?.crop) params.set('crop', options.crop);
// Return value from function
return `https://images.unsplash.com/${id}?${params.toString()}`;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Block comment — documents the file or function below
/** Verified raw-meat & seafood Unsplash IDs */
// Line 29: const RAW = {
const RAW = {
// Property or array item — trailing comma allowed in TypeScript
butcherCounter: 'photo-1607623814075-e51df1bdc82f',
// Property or array item — trailing comma allowed in TypeScript
meatPrep: 'photo-1529692236671-f1f6cf9683ba',
// Property or array item — trailing comma allowed in TypeScript
chickenPieces: 'photo-1587593810167-a84920ea0781',
// Property or array item — trailing comma allowed in TypeScript
rawChicken: 'photo-1621996346565-e3dbc646d9a9',
// Property or array item — trailing comma allowed in TypeScript
beefSteaks: 'photo-1558030006-450675393462',
// Property or array item — trailing comma allowed in TypeScript
beefRibeye: 'photo-1559847844-5315695dadae',
// Property or array item — trailing comma allowed in TypeScript
lambRack: 'photo-1615937657715-bc7b4b7962c1',
// Property or array item — trailing comma allowed in TypeScript
beefCubes: 'photo-1546833999-b9f581a1996d',
// Property or array item — trailing comma allowed in TypeScript
beefMince: 'photo-1603048297172-c92544798d5a',
// Property or array item — trailing comma allowed in TypeScript
lambLeg: 'photo-1544025162-d76694265947',
// Property or array item — trailing comma allowed in TypeScript
lambChops: 'photo-1574672280600-4accfa5b6f98',
// Property or array item — trailing comma allowed in TypeScript
salmonFillet: 'photo-1544551763-46a013bb70d5',
// Property or array item — trailing comma allowed in TypeScript
rawPrawns: 'photo-1565680018434-b513d5e5fd47',
// Property or array item — trailing comma allowed in TypeScript
seafoodDisplay: 'photo-1504674900247-0877df9cc836',
// Closing brace — end of block (function, if, object, JSX)
} as const;
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const IMAGES = {
// Property or array item — trailing comma allowed in TypeScript
logo: '/images/kottgard-logo.jpeg',
// Property or array item — trailing comma allowed in TypeScript
hero: unsplashUrl(RAW.butcherCounter, 2560),
// Property or array item — trailing comma allowed in TypeScript
aboutMeat: unsplashUrl(RAW.meatPrep, 1920),
// Line 50: categories: {
categories: {
// Property or array item — trailing comma allowed in TypeScript
chicken: unsplashUrl(RAW.chickenPieces, 1400),
// Property or array item — trailing comma allowed in TypeScript
beef: unsplashUrl(RAW.beefSteaks, 1400),
// Property or array item — trailing comma allowed in TypeScript
lamb: unsplashUrl(RAW.lambRack, 1400),
// Property or array item — trailing comma allowed in TypeScript
fish: unsplashUrl(RAW.salmonFillet, 1400),
// Closing brace — end of block (function, if, object, JSX)
},
// Line 56: products: {
products: {
// Property or array item — trailing comma allowed in TypeScript
'chicken-whole': unsplashUrl(RAW.rawChicken, 1400),
// Line 58: 'chicken-breast': unsplashUrl(RAW.chickenPieces, 1400, {
'chicken-breast': unsplashUrl(RAW.chickenPieces, 1400, {
// Property or array item — trailing comma allowed in TypeScript
height: 1000,
// Property or array item — trailing comma allowed in TypeScript
crop: 'center',
// Closing brace — end of block (function, if, object, JSX)
}),
// Line 62: 'chicken-thighs': unsplashUrl(RAW.rawChicken, 1400, {
'chicken-thighs': unsplashUrl(RAW.rawChicken, 1400, {
// Property or array item — trailing comma allowed in TypeScript
height: 1100,
// Property or array item — trailing comma allowed in TypeScript
crop: 'entropy',
// Closing brace — end of block (function, if, object, JSX)
}),
// Line 66: 'chicken-wings': unsplashUrl(RAW.chickenPieces, 1400, {
'chicken-wings': unsplashUrl(RAW.chickenPieces, 1400, {
// Property or array item — trailing comma allowed in TypeScript
height: 900,
// Property or array item — trailing comma allowed in TypeScript
crop: 'top',
// Closing brace — end of block (function, if, object, JSX)
}),
// Property or array item — trailing comma allowed in TypeScript
'beef-nihari': unsplashUrl(RAW.beefCubes, 1400),
// Property or array item — trailing comma allowed in TypeScript
'beef-steak': unsplashUrl(RAW.beefRibeye, 1400),
// Property or array item — trailing comma allowed in TypeScript
'beef-mince': unsplashUrl(RAW.beefMince, 1400),
// Property or array item — trailing comma allowed in TypeScript
'beef-boneless': unsplashUrl(RAW.beefSteaks, 1400, { height: 1000, crop: 'center' }),
// Property or array item — trailing comma allowed in TypeScript
'lamb-shoulder': unsplashUrl(RAW.lambRack, 1400),
// Property or array item — trailing comma allowed in TypeScript
'lamb-leg': unsplashUrl(RAW.lambLeg, 1400),
// Property or array item — trailing comma allowed in TypeScript
'lamb-chops': unsplashUrl(RAW.lambChops, 1400),
// Property or array item — trailing comma allowed in TypeScript
'lamb-mince': unsplashUrl(RAW.lambChops, 1400, { height: 1000, crop: 'center' }),
// Property or array item — trailing comma allowed in TypeScript
'fish-salmon': unsplashUrl(RAW.salmonFillet, 1400),
// Property or array item — trailing comma allowed in TypeScript
'fish-rohu': unsplashUrl(RAW.seafoodDisplay, 1400, { height: 1100, crop: 'entropy' }),
// Property or array item — trailing comma allowed in TypeScript
'fish-prawns': unsplashUrl(RAW.rawPrawns, 1400),
// Property or array item — trailing comma allowed in TypeScript
'fish-basa': unsplashUrl(RAW.salmonFillet, 1400, { height: 900, crop: 'center' }),
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
} as const;
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const IMAGE_QUALITY = 90;
@@ -0,0 +1,78 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/lib/offers.ts
* NOT used by the app — read this to learn how the real file works
*/
// Import project module (@/ alias = src/ folder in tsconfig)
import { WeeklyOffer } from '@/types';
// Import from a relative file in the same project
import { IMAGES } from './images';
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const weeklyOffers: WeeklyOffer[] = [
// Line 5: {
{
// Property or array item — trailing comma allowed in TypeScript
id: 'chicken-wings-pl',
// Property or array item — trailing comma allowed in TypeScript
nameKey: 'offers.items.chickenWings',
// Property or array item — trailing comma allowed in TypeScript
badgeKey: 'fresh',
// Property or array item — trailing comma allowed in TypeScript
price: 49.99,
// Property or array item — trailing comma allowed in TypeScript
originalPrice: 59.99,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: IMAGES.products['chicken-wings'],
// Property or array item — trailing comma allowed in TypeScript
whatsappProduct: 'Kycklingvingar färsk PL',
// Property or array item — trailing comma allowed in TypeScript
productSlug: 'chicken-wings',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 16: {
{
// Property or array item — trailing comma allowed in TypeScript
id: 'lamb-steak-ireland',
// Property or array item — trailing comma allowed in TypeScript
nameKey: 'offers.items.lambSteak',
// Property or array item — trailing comma allowed in TypeScript
badgeKey: 'halal',
// Property or array item — trailing comma allowed in TypeScript
price: 204.99,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: IMAGES.categories.lamb,
// Property or array item — trailing comma allowed in TypeScript
whatsappProduct: 'Lammstek färsk Ireland',
// Property or array item — trailing comma allowed in TypeScript
productSlug: 'lamb-leg',
// Closing brace — end of block (function, if, object, JSX)
},
// Line 26: {
{
// Property or array item — trailing comma allowed in TypeScript
id: 'beef-mince-irl',
// Property or array item — trailing comma allowed in TypeScript
nameKey: 'offers.items.beefMince',
// Property or array item — trailing comma allowed in TypeScript
badgeKey: 'fresh',
// Property or array item — trailing comma allowed in TypeScript
price: 139.99,
// Property or array item — trailing comma allowed in TypeScript
originalPrice: 159.99,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: IMAGES.products['beef-mince'],
// Property or array item — trailing comma allowed in TypeScript
whatsappProduct: 'Nötfärs 5% fett IRL',
// Property or array item — trailing comma allowed in TypeScript
productSlug: 'beef-mince',
// Closing brace — end of block (function, if, object, JSX)
},
// End of array literal
];
@@ -0,0 +1,88 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/lib/product-i18n.ts
* NOT used by the app — read this to learn how the real file works
*/
// Import project module (@/ alias = src/ folder in tsconfig)
import { Product } from '@/types';
// (blank line — separates logical blocks for readability)
// TypeScript type alias — union or shorthand for complex types
type Translator = (path: string, params?: Record<string, string | number>) => string;
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export interface LocalizedProduct {
// Line 6: id: string;
id: string;
// Line 7: slug: string;
slug: string;
// Line 8: category: Product['category'];
category: Product['category'];
// Line 9: name: string;
name: string;
// Line 10: description: string;
description: string;
// Line 11: longDescription: string;
longDescription: string;
// Line 12: price: number;
price: number;
// Line 13: priceUnit: string;
priceUnit: string;
// Line 14: image: string;
image: string;
// Line 15: images: string[];
images: string[];
// Line 16: badge?: string;
badge?: string;
// Line 17: inStock: boolean;
inStock: boolean;
// Line 18: featured: boolean;
featured: boolean;
// Line 19: weight?: string;
weight?: string;
// Line 20: tags: string[];
tags: string[];
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function localizeProduct(product: Product, t: Translator): LocalizedProduct {
// Line 24: const base = `products.${product.id}`;
const base = `products.${product.id}`;
// (blank line — separates logical blocks for readability)
// Return value from function
return {
// Property or array item — trailing comma allowed in TypeScript
...product,
// Property or array item — trailing comma allowed in TypeScript
name: t(`${base}.name`),
// Property or array item — trailing comma allowed in TypeScript
description: t(`${base}.description`),
// Property or array item — trailing comma allowed in TypeScript
longDescription: t(`${base}.longDescription`),
// Property or array item — trailing comma allowed in TypeScript
priceUnit: t(`priceUnit.${product.priceUnitKey}`),
// Property or array item — trailing comma allowed in TypeScript
badge: product.badgeKey ? t(`badges.${product.badgeKey}`) : undefined,
// 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)
// Named export — utility function other files can import
export function localizeCategory(
// Property or array item — trailing comma allowed in TypeScript
categoryId: Product['category'],
// Line 38: t: Translator
t: Translator
// Line 39: ): { name: string; description: string } {
): { name: string; description: string } {
// Return value from function
return {
// Property or array item — trailing comma allowed in TypeScript
name: t(`categories.${categoryId}.name`),
// Property or array item — trailing comma allowed in TypeScript
description: t(`categories.${categoryId}.description`),
// Closing brace — end of block (function, if, object, JSX)
};
// Closing brace — end of block (function, if, object, JSX)
}
@@ -0,0 +1,464 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/lib/products.ts
* NOT used by the app — read this to learn how the real file works
*/
// Import project module (@/ alias = src/ folder in tsconfig)
import { Product } from '@/types';
// Import from a relative file in the same project
import { IMAGES } from './images';
// (blank line — separates logical blocks for readability)
// Product catalog entry — demo data shown in shop and product pages
const P = IMAGES.products;
// (blank line — separates logical blocks for readability)
// Named export constant — shared config/data imported elsewhere
export const products: Product[] = [
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'chicken-whole',
// Property or array item — trailing comma allowed in TypeScript
slug: 'whole-chicken',
// Property or array item — trailing comma allowed in TypeScript
category: 'chicken',
// Property or array item — trailing comma allowed in TypeScript
price: 129,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perBird',
// Property or array item — trailing comma allowed in TypeScript
image: P['chicken-whole'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['chicken-whole'], P['chicken-breast']],
// Property or array item — trailing comma allowed in TypeScript
badgeKey: 'bestseller',
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: true,
// Property or array item — trailing comma allowed in TypeScript
weight: '1.21.5 kg',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'whole', 'popular'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'chicken-breast',
// Property or array item — trailing comma allowed in TypeScript
slug: 'chicken-breast',
// Property or array item — trailing comma allowed in TypeScript
category: 'chicken',
// Property or array item — trailing comma allowed in TypeScript
price: 99,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perPack',
// Property or array item — trailing comma allowed in TypeScript
image: P['chicken-breast'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['chicken-breast'], P['chicken-whole']],
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: true,
// Property or array item — trailing comma allowed in TypeScript
weight: '500g1kg',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'boneless', 'lean'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'chicken-thighs',
// Property or array item — trailing comma allowed in TypeScript
slug: 'chicken-thighs',
// Property or array item — trailing comma allowed in TypeScript
category: 'chicken',
// Property or array item — trailing comma allowed in TypeScript
price: 85,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perPack',
// Property or array item — trailing comma allowed in TypeScript
image: P['chicken-thighs'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['chicken-thighs']],
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: false,
// Property or array item — trailing comma allowed in TypeScript
weight: '500g1kg',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'juicy'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'chicken-wings',
// Property or array item — trailing comma allowed in TypeScript
slug: 'chicken-wings',
// Property or array item — trailing comma allowed in TypeScript
category: 'chicken',
// Property or array item — trailing comma allowed in TypeScript
price: 79,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perPack',
// Property or array item — trailing comma allowed in TypeScript
image: P['chicken-wings'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['chicken-wings']],
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: false,
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'party'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'beef-nihari',
// Property or array item — trailing comma allowed in TypeScript
slug: 'beef-for-nihari',
// Property or array item — trailing comma allowed in TypeScript
category: 'beef',
// Property or array item — trailing comma allowed in TypeScript
price: 149,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['beef-nihari'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['beef-nihari'], P['beef-steak']],
// Property or array item — trailing comma allowed in TypeScript
badgeKey: 'chefsPick',
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: true,
// Property or array item — trailing comma allowed in TypeScript
weight: '1 kg',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'traditional'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'beef-steak',
// Property or array item — trailing comma allowed in TypeScript
slug: 'premium-beef-steak',
// Property or array item — trailing comma allowed in TypeScript
category: 'beef',
// Property or array item — trailing comma allowed in TypeScript
price: 229,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['beef-steak'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['beef-steak'], P['beef-nihari']],
// Property or array item — trailing comma allowed in TypeScript
badgeKey: 'premium',
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: true,
// Property or array item — trailing comma allowed in TypeScript
weight: '1 kg',
// Property or array item — trailing comma allowed in TypeScript
tags: ['premium', 'steak'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'beef-mince',
// Property or array item — trailing comma allowed in TypeScript
slug: 'beef-mince',
// Property or array item — trailing comma allowed in TypeScript
category: 'beef',
// Property or array item — trailing comma allowed in TypeScript
price: 119,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['beef-mince'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['beef-mince']],
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: false,
// Property or array item — trailing comma allowed in TypeScript
weight: '500g1kg',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'mince'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'beef-boneless',
// Property or array item — trailing comma allowed in TypeScript
slug: 'boneless-beef-cubes',
// Property or array item — trailing comma allowed in TypeScript
category: 'beef',
// Property or array item — trailing comma allowed in TypeScript
price: 169,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['beef-boneless'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['beef-boneless']],
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: false,
// Property or array item — trailing comma allowed in TypeScript
weight: '1 kg',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'boneless'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'lamb-shoulder',
// Property or array item — trailing comma allowed in TypeScript
slug: 'lamb-shoulder',
// Property or array item — trailing comma allowed in TypeScript
category: 'lamb',
// Property or array item — trailing comma allowed in TypeScript
price: 189,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['lamb-shoulder'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['lamb-shoulder'], P['lamb-leg']],
// Property or array item — trailing comma allowed in TypeScript
badgeKey: 'popular',
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: true,
// Property or array item — trailing comma allowed in TypeScript
weight: '1 kg',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'traditional'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'lamb-leg',
// Property or array item — trailing comma allowed in TypeScript
slug: 'lamb-leg',
// Property or array item — trailing comma allowed in TypeScript
category: 'lamb',
// Property or array item — trailing comma allowed in TypeScript
price: 219,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['lamb-leg'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['lamb-leg']],
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: true,
// Property or array item — trailing comma allowed in TypeScript
weight: '12 kg',
// Property or array item — trailing comma allowed in TypeScript
tags: ['premium', 'roast'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'lamb-chops',
// Property or array item — trailing comma allowed in TypeScript
slug: 'lamb-chops',
// Property or array item — trailing comma allowed in TypeScript
category: 'lamb',
// Property or array item — trailing comma allowed in TypeScript
price: 249,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['lamb-chops'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['lamb-chops']],
// Property or array item — trailing comma allowed in TypeScript
badgeKey: 'premium',
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: false,
// Property or array item — trailing comma allowed in TypeScript
weight: '500g',
// Property or array item — trailing comma allowed in TypeScript
tags: ['premium', 'grill'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'lamb-mince',
// Property or array item — trailing comma allowed in TypeScript
slug: 'lamb-mince',
// Property or array item — trailing comma allowed in TypeScript
category: 'lamb',
// Property or array item — trailing comma allowed in TypeScript
price: 159,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['lamb-mince'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['lamb-mince']],
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: false,
// Property or array item — trailing comma allowed in TypeScript
weight: '500g1kg',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'mince'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'fish-salmon',
// Property or array item — trailing comma allowed in TypeScript
slug: 'atlantic-salmon-fillet',
// Property or array item — trailing comma allowed in TypeScript
category: 'fish',
// Property or array item — trailing comma allowed in TypeScript
price: 199,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['fish-salmon'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['fish-salmon'], P['fish-rohu']],
// Property or array item — trailing comma allowed in TypeScript
badgeKey: 'freshCatch',
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: true,
// Property or array item — trailing comma allowed in TypeScript
weight: '500g',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'fillet'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'fish-rohu',
// Property or array item — trailing comma allowed in TypeScript
slug: 'rohu-fish',
// Property or array item — trailing comma allowed in TypeScript
category: 'fish',
// Property or array item — trailing comma allowed in TypeScript
price: 119,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['fish-rohu'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['fish-rohu']],
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: true,
// Property or array item — trailing comma allowed in TypeScript
weight: '11.5 kg',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'whole'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'fish-prawns',
// Property or array item — trailing comma allowed in TypeScript
slug: 'jumbo-prawns',
// Property or array item — trailing comma allowed in TypeScript
category: 'fish',
// Property or array item — trailing comma allowed in TypeScript
price: 179,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['fish-prawns'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['fish-prawns']],
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: false,
// Property or array item — trailing comma allowed in TypeScript
weight: '500g',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'seafood'],
// Closing brace — end of block (function, if, object, JSX)
},
// Product catalog entry — demo data shown in shop and product pages
{
// Property or array item — trailing comma allowed in TypeScript
id: 'fish-basa',
// Property or array item — trailing comma allowed in TypeScript
slug: 'basa-fillet',
// Property or array item — trailing comma allowed in TypeScript
category: 'fish',
// Property or array item — trailing comma allowed in TypeScript
price: 109,
// Property or array item — trailing comma allowed in TypeScript
priceUnitKey: 'perKg',
// Property or array item — trailing comma allowed in TypeScript
image: P['fish-basa'],
// Property or array item — trailing comma allowed in TypeScript
images: [P['fish-basa']],
// Property or array item — trailing comma allowed in TypeScript
inStock: true,
// Property or array item — trailing comma allowed in TypeScript
featured: false,
// Property or array item — trailing comma allowed in TypeScript
weight: '500g',
// Property or array item — trailing comma allowed in TypeScript
tags: ['fresh', 'fillet', 'mild'],
// Closing brace — end of block (function, if, object, JSX)
},
// End of array literal
];
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function getProductBySlug(slug: string): Product | undefined {
// Return value from function
return products.find((p) => p.slug === slug);
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function getProductsByCategory(category: string): Product[] {
// Return value from function
return products.filter((p) => p.category === category);
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function getFeaturedProducts(): Product[] {
// Return value from function
return products.filter((p) => p.featured);
// Closing brace — end of block (function, if, object, JSX)
}
+59
View File
@@ -0,0 +1,59 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/lib/utils.ts
* NOT used by the app — read this to learn how the real file works
*/
// Import external package or local module
import { clsx, type ClassValue } from 'clsx';
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function cn(...inputs: ClassValue[]) {
// Return value from function
return clsx(inputs);
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function formatPrice(price: number, locale = 'sv-SE'): string {
// Return value from function
return new Intl.NumberFormat(locale, {
// Property or array item — trailing comma allowed in TypeScript
style: 'currency',
// Property or array item — trailing comma allowed in TypeScript
currency: 'SEK',
// Closing brace — end of block (function, if, object, JSX)
}).format(price);
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function getFormatLocale(locale: string): string {
// Line 15: const map: Record<string, string> = {
const map: Record<string, string> = {
// Property or array item — trailing comma allowed in TypeScript
en: 'en-US',
// Property or array item — trailing comma allowed in TypeScript
sv: 'sv-SE',
// Property or array item — trailing comma allowed in TypeScript
ur: 'ur-PK',
// Closing brace — end of block (function, if, object, JSX)
};
// Return value from function
return map[locale] ?? 'en-US';
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function formatDate(date: string): string {
// Return value from function
return new Intl.DateTimeFormat('en-US', {
// Property or array item — trailing comma allowed in TypeScript
year: 'numeric',
// Property or array item — trailing comma allowed in TypeScript
month: 'long',
// Property or array item — trailing comma allowed in TypeScript
day: 'numeric',
// Closing brace — end of block (function, if, object, JSX)
}).format(new Date(date));
// Closing brace — end of block (function, if, object, JSX)
}
+179
View File
@@ -0,0 +1,179 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/store/auth.ts
* NOT used by the app — read this to learn how the real file works
*/
// Zustand — simple global state store (cart, auth, locale)
import { create } from 'zustand';
// Zustand — simple global state store (cart, auth, locale)
import { persist } from 'zustand/middleware';
// Import project module (@/ alias = src/ folder in tsconfig)
import { Address, Order, User } from '@/types';
// Import project module (@/ alias = src/ folder in tsconfig)
import { DEMO_EMAIL } from '@/lib/constants';
// Import project module (@/ alias = src/ folder in tsconfig)
import { DEMO_ORDERS } from '@/lib/demo-orders';
// (blank line — separates logical blocks for readability)
// TypeScript interface — contract for object properties and methods
interface AuthState {
// Line 8: user: User | null;
user: User | null;
// Line 9: orders: Order[];
orders: Order[];
// Line 10: isAuthenticated: boolean;
isAuthenticated: boolean;
// Line 11: login: (email: string, password: string) => boolean;
login: (email: string, password: string) => boolean;
// Line 12: register: (data: {
register: (data: {
// Line 13: name: string;
name: string;
// Line 14: email: string;
email: string;
// Line 15: password: string;
password: string;
// Line 16: phone: string;
phone: string;
// Line 17: address: Address;
address: Address;
// Closing brace — end of block (function, if, object, JSX)
}) => boolean;
// Line 19: logout: () => void;
logout: () => void;
// Line 20: updateProfile: (data: Partial<User>) => void;
updateProfile: (data: Partial<User>) => void;
// Line 21: addOrder: (order: Order) => void;
addOrder: (order: Order) => void;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Line 24: const DEMO_USER: User = {
const DEMO_USER: User = {
// Property or array item — trailing comma allowed in TypeScript
id: 'demo-1',
// Property or array item — trailing comma allowed in TypeScript
name: 'Ahmed Khan',
// Property or array item — trailing comma allowed in TypeScript
email: DEMO_EMAIL,
// Property or array item — trailing comma allowed in TypeScript
phone: '+46 72 585 50 50',
// Line 29: address: {
address: {
// Property or array item — trailing comma allowed in TypeScript
street: 'Tingvallavägen 11',
// Property or array item — trailing comma allowed in TypeScript
city: 'Märsta',
// Property or array item — trailing comma allowed in TypeScript
state: 'Stockholm',
// Property or array item — trailing comma allowed in TypeScript
zip: '195 31',
// Closing brace — end of block (function, if, object, JSX)
},
// Property or array item — trailing comma allowed in TypeScript
createdAt: '2025-03-15T10:00:00.000Z',
// 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 useAuthStore = create<AuthState>()(
// Zustand persist — save store to localStorage between visits
persist(
// Line 40: (set, get) => ({
(set, get) => ({
// Property or array item — trailing comma allowed in TypeScript
user: null,
// Property or array item — trailing comma allowed in TypeScript
orders: [],
// Property or array item — trailing comma allowed in TypeScript
isAuthenticated: false,
// (blank line — separates logical blocks for readability)
// Line 45: login: (email, password) => {
login: (email, password) => {
// Conditional branch — different behavior based on runtime value
if (email === DEMO_EMAIL && password === 'demo123') {
// Line 47: const existingOrders = get().orders;
const existingOrders = get().orders;
// Line 48: set({
set({
// Property or array item — trailing comma allowed in TypeScript
user: DEMO_USER,
// Property or array item — trailing comma allowed in TypeScript
isAuthenticated: true,
// Property or array item — trailing comma allowed in TypeScript
orders: existingOrders.length > 0 ? existingOrders : DEMO_ORDERS,
// Closing brace — end of block (function, if, object, JSX)
});
// Return value from function
return true;
// Closing brace — end of block (function, if, object, JSX)
}
// Line 55: const stored = get().user;
const stored = get().user;
// Conditional branch — different behavior based on runtime value
if (stored && stored.email === email) {
// Line 57: set({ isAuthenticated: true });
set({ isAuthenticated: true });
// Return value from function
return true;
// Closing brace — end of block (function, if, object, JSX)
}
// Return value from function
return false;
// Closing brace — end of block (function, if, object, JSX)
},
// (blank line — separates logical blocks for readability)
// Line 63: register: (data) => {
register: (data) => {
// Line 64: const user: User = {
const user: User = {
// Property or array item — trailing comma allowed in TypeScript
id: `user-${Date.now()}`,
// Property or array item — trailing comma allowed in TypeScript
name: data.name,
// Property or array item — trailing comma allowed in TypeScript
email: data.email,
// Property or array item — trailing comma allowed in TypeScript
phone: data.phone,
// Property or array item — trailing comma allowed in TypeScript
address: data.address,
// Property or array item — trailing comma allowed in TypeScript
createdAt: new Date().toISOString(),
// Closing brace — end of block (function, if, object, JSX)
};
// Line 72: set({ user, isAuthenticated: true });
set({ user, isAuthenticated: true });
// Return value from function
return true;
// Closing brace — end of block (function, if, object, JSX)
},
// (blank line — separates logical blocks for readability)
// Property or array item — trailing comma allowed in TypeScript
logout: () => set({ isAuthenticated: false }),
// (blank line — separates logical blocks for readability)
// Line 78: updateProfile: (data) => {
updateProfile: (data) => {
// Line 79: const current = get().user;
const current = get().user;
// Conditional branch — different behavior based on runtime value
if (current) {
// Line 81: set({ user: { ...current, ...data } });
set({ user: { ...current, ...data } });
// 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)
// Line 85: addOrder: (order) => {
addOrder: (order) => {
// Line 86: set({ orders: [order, ...get().orders] });
set({ orders: [order, ...get().orders] });
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
}),
// Line 89: { name: 'kott-gard-auth' }
{ name: 'kott-gard-auth' }
// Line 90: )
)
// Line 91: );
);
+164
View File
@@ -0,0 +1,164 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/store/cart.ts
* NOT used by the app — read this to learn how the real file works
*/
// Zustand — simple global state store (cart, auth, locale)
import { create } from 'zustand';
// Zustand — simple global state store (cart, auth, locale)
import { persist } from 'zustand/middleware';
// Import project module (@/ alias = src/ folder in tsconfig)
import { CartItem, Product, ProductCustomization } from '@/types';
// Import project module (@/ alias = src/ folder in tsconfig)
import { getCartItemKey } from '@/lib/customization';
// (blank line — separates logical blocks for readability)
// TypeScript interface — contract for object properties and methods
interface CartState {
// Line 7: items: CartItem[];
items: CartItem[];
// Line 8: addItem: (
addItem: (
// Property or array item — trailing comma allowed in TypeScript
product: Product,
// Property or array item — trailing comma allowed in TypeScript
customization: ProductCustomization,
// Property or array item — trailing comma allowed in TypeScript
customizationLabel: string,
// Line 12: quantity?: number
quantity?: number
// Line 13: ) => void;
) => void;
// Line 14: removeItem: (id: string) => void;
removeItem: (id: string) => void;
// Line 15: updateQuantity: (id: string, quantity: number) => void;
updateQuantity: (id: string, quantity: number) => void;
// Line 16: clearCart: () => void;
clearCart: () => void;
// Line 17: getTotal: () => number;
getTotal: () => number;
// Line 18: getItemCount: () => number;
getItemCount: () => number;
// 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 useCartStore = create<CartState>()(
// Zustand persist — save store to localStorage between visits
persist(
// Line 23: (set, get) => ({
(set, get) => ({
// Property or array item — trailing comma allowed in TypeScript
items: [],
// (blank line — separates logical blocks for readability)
// Line 26: addItem: (product, customization, customizationLabel, qua...
addItem: (product, customization, customizationLabel, quantity = 1) => {
// Line 27: const id = getCartItemKey(product.id, customization);
const id = getCartItemKey(product.id, customization);
// Line 28: const label = customizationLabel;
const label = customizationLabel;
// Array.find — get first matching item or undefined
const existing = get().items.find((item) => item.id === id);
// (blank line — separates logical blocks for readability)
// Conditional branch — different behavior based on runtime value
if (existing) {
// Line 32: set({
set({
// Array.map — transform each item (often render a list of components)
items: get().items.map((item) =>
// Line 34: item.id === id
item.id === id
// Line 35: ? { ...item, quantity: item.quantity + quantity }
? { ...item, quantity: item.quantity + quantity }
// Line 36: : item
: item
// Property or array item — trailing comma allowed in TypeScript
),
// Closing brace — end of block (function, if, object, JSX)
});
// Closing brace — end of block (function, if, object, JSX)
} else {
// Line 40: set({
set({
// Line 41: items: [
items: [
// Property or array item — trailing comma allowed in TypeScript
...get().items,
// Line 43: {
{
// Property or array item — trailing comma allowed in TypeScript
id,
// Property or array item — trailing comma allowed in TypeScript
product,
// Property or array item — trailing comma allowed in TypeScript
quantity,
// Property or array item — trailing comma allowed in TypeScript
customization,
// Property or array item — trailing comma allowed in TypeScript
customizationLabel: label,
// Closing brace — end of block (function, if, object, JSX)
},
// End of array literal
],
// Closing brace — end of block (function, if, object, JSX)
});
// 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)
// Line 55: removeItem: (id) => {
removeItem: (id) => {
// Array.filter — keep items matching condition (search, category)
set({ items: get().items.filter((item) => item.id !== id) });
// Closing brace — end of block (function, if, object, JSX)
},
// (blank line — separates logical blocks for readability)
// Line 59: updateQuantity: (id, quantity) => {
updateQuantity: (id, quantity) => {
// Conditional branch — different behavior based on runtime value
if (quantity <= 0) {
// Line 61: get().removeItem(id);
get().removeItem(id);
// Line 62: return;
return;
// Closing brace — end of block (function, if, object, JSX)
}
// Line 64: set({
set({
// Array.map — transform each item (often render a list of components)
items: get().items.map((item) =>
// Line 66: item.id === id ? { ...item, quantity } : item
item.id === id ? { ...item, quantity } : item
// Property or array item — trailing comma allowed in TypeScript
),
// 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)
// Property or array item — trailing comma allowed in TypeScript
clearCart: () => set({ items: [] }),
// (blank line — separates logical blocks for readability)
// Line 73: getTotal: () =>
getTotal: () =>
// Array.reduce — accumulate single value (cart total, item count)
get().items.reduce(
// Property or array item — trailing comma allowed in TypeScript
(sum, item) => sum + item.product.price * item.quantity,
// Line 76: 0
0
// Property or array item — trailing comma allowed in TypeScript
),
// (blank line — separates logical blocks for readability)
// Line 79: getItemCount: () =>
getItemCount: () =>
// Array.reduce — accumulate single value (cart total, item count)
get().items.reduce((sum, item) => sum + item.quantity, 0),
// Closing brace — end of block (function, if, object, JSX)
}),
// Line 82: { name: 'kott-gard-cart' }
{ name: 'kott-gard-cart' }
// Line 83: )
)
// Line 84: );
);
@@ -0,0 +1,39 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/store/locale.ts
* NOT used by the app — read this to learn how the real file works
*/
// Zustand — simple global state store (cart, auth, locale)
import { create } from 'zustand';
// Zustand — simple global state store (cart, auth, locale)
import { persist } from 'zustand/middleware';
// Import project module (@/ alias = src/ folder in tsconfig)
import { DEFAULT_LOCALE, Locale } from '@/i18n/types';
// (blank line — separates logical blocks for readability)
// TypeScript interface — contract for object properties and methods
interface LocaleState {
// Line 6: locale: Locale;
locale: Locale;
// Line 7: setLocale: (locale: Locale) => void;
setLocale: (locale: Locale) => void;
// 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 useLocaleStore = create<LocaleState>()(
// Zustand persist — save store to localStorage between visits
persist(
// Line 12: (set) => ({
(set) => ({
// Property or array item — trailing comma allowed in TypeScript
locale: DEFAULT_LOCALE,
// Property or array item — trailing comma allowed in TypeScript
setLocale: (locale) => set({ locale }),
// Closing brace — end of block (function, if, object, JSX)
}),
// Line 16: { name: 'kott-gard-locale' }
{ name: 'kott-gard-locale' }
// Line 17: )
)
// Line 18: );
);
@@ -0,0 +1,81 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/store/wishlist.ts
* NOT used by the app — read this to learn how the real file works
*/
// Zustand — simple global state store (cart, auth, locale)
import { create } from 'zustand';
// Zustand — simple global state store (cart, auth, locale)
import { persist } from 'zustand/middleware';
// Import project module (@/ alias = src/ folder in tsconfig)
import { Product } from '@/types';
// (blank line — separates logical blocks for readability)
// TypeScript interface — contract for object properties and methods
interface WishlistState {
// Line 6: items: Product[];
items: Product[];
// Line 7: addItem: (product: Product) => void;
addItem: (product: Product) => void;
// Line 8: removeItem: (productId: string) => void;
removeItem: (productId: string) => void;
// Line 9: isInWishlist: (productId: string) => boolean;
isInWishlist: (productId: string) => boolean;
// Line 10: toggleItem: (product: Product) => void;
toggleItem: (product: Product) => void;
// 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 useWishlistStore = create<WishlistState>()(
// Zustand persist — save store to localStorage between visits
persist(
// Line 15: (set, get) => ({
(set, get) => ({
// Property or array item — trailing comma allowed in TypeScript
items: [],
// (blank line — separates logical blocks for readability)
// Line 18: addItem: (product) => {
addItem: (product) => {
// Conditional branch — different behavior based on runtime value
if (!get().isInWishlist(product.id)) {
// Line 20: set({ items: [...get().items, product] });
set({ items: [...get().items, product] });
// 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)
// Line 24: removeItem: (productId) => {
removeItem: (productId) => {
// Array.filter — keep items matching condition (search, category)
set({ items: get().items.filter((p) => p.id !== productId) });
// Closing brace — end of block (function, if, object, JSX)
},
// (blank line — separates logical blocks for readability)
// Line 28: isInWishlist: (productId) =>
isInWishlist: (productId) =>
// Property or array item — trailing comma allowed in TypeScript
get().items.some((p) => p.id === productId),
// (blank line — separates logical blocks for readability)
// Line 31: toggleItem: (product) => {
toggleItem: (product) => {
// Conditional branch — different behavior based on runtime value
if (get().isInWishlist(product.id)) {
// Line 33: get().removeItem(product.id);
get().removeItem(product.id);
// Closing brace — end of block (function, if, object, JSX)
} else {
// Line 35: get().addItem(product);
get().addItem(product);
// Closing brace — end of block (function, if, object, JSX)
}
// Closing brace — end of block (function, if, object, JSX)
},
// Closing brace — end of block (function, if, object, JSX)
}),
// Line 39: { name: 'kott-gard-wishlist' }
{ name: 'kott-gard-wishlist' }
// Line 40: )
)
// Line 41: );
);
+168
View File
@@ -0,0 +1,168 @@
/**
* ANNOTATED COPY — every line explained
* Source: src/types/index.ts
* NOT used by the app — read this to learn how the real file works
*/
// Export TypeScript type — defines data shape used across the app
export type Category = 'chicken' | 'beef' | 'lamb' | 'fish';
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export type CutCount = 4 | 8 | 10 | 12;
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export type CuttingStyleKey = 'nihari' | 'karahi' | 'qeema' | 'boneless' | 'steak';
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export type MeatCategory = 'chicken' | 'beef' | 'lamb';
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export interface MeatCustomization {
// Line 10: type: MeatCategory;
type: MeatCategory;
// Line 11: cuts: CutCount;
cuts: CutCount;
// Line 12: cuttingStyle: CuttingStyleKey;
cuttingStyle: CuttingStyleKey;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export type ProductCustomization = MeatCustomization | { type: 'fish' };
// (blank line — separates logical blocks for readability)
// Named export — utility function other files can import
export function isMeatCustomization(
// Line 18: customization: ProductCustomization
customization: ProductCustomization
// Line 19: ): customization is MeatCustomization {
): customization is MeatCustomization {
// Return value from function
return customization.type !== 'fish';
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export type PriceUnitKey = 'perBird' | 'perPack' | 'perKg';
// Export TypeScript type — defines data shape used across the app
export type BadgeKey = 'bestseller' | 'chefsPick' | 'premium' | 'popular' | 'freshCatch';
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export interface Product {
// Line 27: id: string;
id: string;
// Line 28: slug: string;
slug: string;
// Line 29: category: Category;
category: Category;
// Line 30: price: number;
price: number;
// Line 31: priceUnitKey: PriceUnitKey;
priceUnitKey: PriceUnitKey;
// Line 32: image: string;
image: string;
// Line 33: images: string[];
images: string[];
// Line 34: badgeKey?: BadgeKey;
badgeKey?: BadgeKey;
// Line 35: inStock: boolean;
inStock: boolean;
// Line 36: featured: boolean;
featured: boolean;
// Line 37: weight?: string;
weight?: string;
// Line 38: tags: string[];
tags: string[];
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export interface CartItem {
// Line 42: id: string;
id: string;
// Line 43: product: Product;
product: Product;
// Line 44: quantity: number;
quantity: number;
// Line 45: customization: ProductCustomization;
customization: ProductCustomization;
// Line 46: customizationLabel: string;
customizationLabel: string;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export interface User {
// Line 50: id: string;
id: string;
// Line 51: name: string;
name: string;
// Line 52: email: string;
email: string;
// Line 53: phone: string;
phone: string;
// Line 54: address: Address;
address: Address;
// Line 55: createdAt: string;
createdAt: string;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export interface Address {
// Line 59: street: string;
street: string;
// Line 60: city: string;
city: string;
// Line 61: state: string;
state: string;
// Line 62: zip: string;
zip: string;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export interface Order {
// Line 66: id: string;
id: string;
// Line 67: items: CartItem[];
items: CartItem[];
// Line 68: total: number;
total: number;
// Line 69: status: 'pending' | 'confirmed' | 'preparing' | 'out-for-...
status: 'pending' | 'confirmed' | 'preparing' | 'out-for-delivery' | 'delivered';
// Line 70: createdAt: string;
createdAt: string;
// Line 71: deliveryAddress: Address;
deliveryAddress: Address;
// Line 72: paymentMethod: string;
paymentMethod: string;
// Closing brace — end of block (function, if, object, JSX)
}
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export type SortOption = 'featured' | 'price-asc' | 'price-desc' | 'name';
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export type OfferBadgeKey = 'fresh' | 'halal';
// (blank line — separates logical blocks for readability)
// Export TypeScript type — defines data shape used across the app
export interface WeeklyOffer {
// Line 80: id: string;
id: string;
// Line 81: nameKey: string;
nameKey: string;
// Line 82: badgeKey: OfferBadgeKey;
badgeKey: OfferBadgeKey;
// Line 83: price: number;
price: number;
// Line 84: originalPrice?: number;
originalPrice?: number;
// Line 85: priceUnitKey: PriceUnitKey;
priceUnitKey: PriceUnitKey;
// Line 86: image: string;
image: string;
// Line 87: whatsappProduct: string;
whatsappProduct: string;
// Line 88: productSlug: string;
productSlug: string;
// Closing brace — end of block (function, if, object, JSX)
}
+272
View File
@@ -0,0 +1,272 @@
/**
* Generates Kottgard-Website-Guide.docx
* Run: node docs/generate-guide-docx.mjs
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import {
Document,
Packer,
Paragraph,
TextRun,
Table,
TableRow,
TableCell,
HeadingLevel,
AlignmentType,
BorderStyle,
WidthType,
ShadingType,
PageBreak,
LevelFormat,
} from 'docx';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const outPath = path.join(__dirname, 'Kottgard-Website-Guide.docx');
const border = { style: BorderStyle.SINGLE, size: 1, color: 'CCCCCC' };
const borders = { top: border, bottom: border, left: border, right: border };
const tableWidth = 9360;
function cell(text, width, fill = 'FFFFFF') {
return new TableCell({
borders,
width: { size: width, type: WidthType.DXA },
shading: { fill, type: ShadingType.CLEAR },
margins: { top: 80, bottom: 80, left: 120, right: 120 },
children: [new Paragraph({ children: [new TextRun(text)] })],
});
}
function headerRow(cols, widths) {
return new TableRow({
children: cols.map((t, i) => cell(t, widths[i], 'D5E8F0')),
});
}
function dataRow(cols, widths) {
return new TableRow({
children: cols.map((t, i) => cell(t, widths[i])),
});
}
function table(columnWidths, rows) {
return new Table({
width: { size: tableWidth, type: WidthType.DXA },
columnWidths,
rows,
});
}
function h1(text) {
return new Paragraph({ heading: HeadingLevel.HEADING_1, children: [new TextRun(text)] });
}
function h2(text) {
return new Paragraph({ heading: HeadingLevel.HEADING_2, children: [new TextRun(text)] });
}
function p(text) {
return new Paragraph({ spacing: { after: 200 }, children: [new TextRun(text)] });
}
function bullet(ref, text) {
return new Paragraph({
numbering: { reference: ref, level: 0 },
children: [new TextRun(text)],
});
}
const doc = new Document({
styles: {
default: { document: { run: { font: 'Arial', size: 22 } } },
paragraphStyles: [
{
id: 'Heading1',
name: 'Heading 1',
basedOn: 'Normal',
next: 'Normal',
quickFormat: true,
run: { size: 32, bold: true, font: 'Arial' },
paragraph: { spacing: { before: 240, after: 240 }, outlineLevel: 0 },
},
{
id: 'Heading2',
name: 'Heading 2',
basedOn: 'Normal',
next: 'Normal',
quickFormat: true,
run: { size: 26, bold: true, font: 'Arial' },
paragraph: { spacing: { before: 180, after: 180 }, outlineLevel: 1 },
},
],
},
numbering: {
config: [
{
reference: 'bullets',
levels: [
{
level: 0,
format: LevelFormat.BULLET,
text: '\u2022',
alignment: AlignmentType.LEFT,
style: { paragraph: { indent: { left: 720, hanging: 360 } } },
},
],
},
],
},
sections: [
{
properties: {
page: {
size: { width: 12240, height: 15840 },
margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 },
},
},
children: [
new Paragraph({
alignment: AlignmentType.CENTER,
spacing: { after: 400 },
children: [
new TextRun({ text: 'Kött Gård', bold: true, size: 48 }),
],
}),
new Paragraph({
alignment: AlignmentType.CENTER,
spacing: { after: 600 },
children: [
new TextRun({
text: 'Website Visual & Programming Guide',
size: 32,
color: '8B1F1F',
}),
],
}),
p('Premium Halal meat e-commerce — Märsta, Sweden'),
p('Document version: June 2026'),
p('Companion files: docs/annotated/ (line-by-line code comments)'),
new Paragraph({ children: [new PageBreak()] }),
h1('1. What is this website?'),
p(
'Kött Gård is a Next.js e-commerce front-end for ordering fresh Halal chicken, beef, lamb, and fish. Customers browse products, customize cuts (Nihari, Karahi, piece counts), add to cart, checkout, and view order history. Languages: Swedish, English, Urdu (RTL).'
),
h2('Demo login'),
table(
[3120, 6240],
[
headerRow(['Field', 'Value'], [3120, 6240]),
dataRow(['Email', 'demo@kottgard.se'], [3120, 6240]),
dataRow(['Password', 'demo123'], [3120, 6240]),
dataRow(['Orders', '3 sample orders in Account'], [3120, 6240]),
]
),
h1('2. Homepage layout (visual map)'),
p('When a customer opens https://kottgard.se/, they scroll through these sections:'),
table(
[800, 2200, 3160, 3200],
[
headerRow(['#', 'Section', 'File', 'Customer action'], [800, 2200, 3160, 3200]),
dataRow(['1', 'Hero', 'Hero.tsx', 'Shop now / WhatsApp'], [800, 2200, 3160, 3200]),
dataRow(['2', 'Trust badges', 'TrustBadges.tsx', 'Click → About'], [800, 2200, 3160, 3200]),
dataRow(['3', 'About preview', 'AboutPreview.tsx', 'Read more → /about'], [800, 2200, 3160, 3200]),
dataRow(['4', 'Categories', 'CategoryGrid.tsx', 'Chicken/Beef/Lamb/Fish'], [800, 2200, 3160, 3200]),
dataRow(['5', 'Featured', 'FeaturedProducts.tsx', 'Product cards'], [800, 2200, 3160, 3200]),
dataRow(['6', 'How it works', 'HowItWorks.tsx', 'Order steps'], [800, 2200, 3160, 3200]),
dataRow(['7', 'Weekly offers', 'WeeklyOffers.tsx', 'Deals → products'], [800, 2200, 3160, 3200]),
dataRow(['8', 'CTA', 'CTA.tsx', 'Browse selection'], [800, 2200, 3160, 3200]),
dataRow(['9', 'Social', 'SocialFollow.tsx', 'Facebook / Instagram'], [800, 2200, 3160, 3200]),
dataRow(['10', 'Contact', 'ContactPreview.tsx', 'Map / phone / about'], [800, 2200, 3160, 3200]),
]
),
h1('3. All pages & routes'),
table(
[2800, 3360, 3200],
[
headerRow(['URL', 'File', 'Description'], [2800, 3360, 3200]),
dataRow(['/', 'app/page.tsx', 'Homepage'], [2800, 3360, 3200]),
dataRow(['/shop', 'app/shop/page.tsx', 'Catalog + search + filters'], [2800, 3360, 3200]),
dataRow(['/product/[slug]', 'app/product/[slug]/page.tsx', 'Detail + customize'], [2800, 3360, 3200]),
dataRow(['/cart', 'app/cart/page.tsx', 'Shopping cart'], [2800, 3360, 3200]),
dataRow(['/checkout', 'app/checkout/page.tsx', 'Place order (demo)'], [2800, 3360, 3200]),
dataRow(['/login', 'app/login/page.tsx', 'Auth'], [2800, 3360, 3200]),
dataRow(['/account', 'app/account/page.tsx', 'Profile + orders'], [2800, 3360, 3200]),
dataRow(['/wishlist', 'app/wishlist/page.tsx', 'Saved items'], [2800, 3360, 3200]),
dataRow(['/about', 'app/about/page.tsx', 'Company + privacy + terms'], [2800, 3360, 3200]),
]
),
new Paragraph({ children: [new PageBreak()] }),
h1('4. Programming languages & frameworks'),
h2('TypeScript'),
p(
'TypeScript adds types to JavaScript. Example: Product interface ensures every product has id, slug, price. The compiler errors if you typo product.slugg.'
),
h2('React'),
p(
'React builds UI from components (functions returning JSX). State (useState) updates what users see. Props pass data parent → child.'
),
h2('Next.js 13 App Router'),
bullet('bullets', 'app/ folder = routes. page.tsx = page, layout.tsx = wrapper.'),
bullet('bullets', 'Server Components default — less JavaScript sent to browser.'),
bullet('bullets', "'use client' = component runs in browser (hooks, clicks)."),
h2('Zustand'),
p(
'Global stores: useCartStore, useAuthStore, useWishlistStore. persist middleware saves to localStorage so cart survives refresh.'
),
h2('Tailwind CSS'),
p(
'Classes like bg-brand-700 text-white px-4 py-2 style elements. Colors defined in tailwind.config.ts (burgundy, cream, gold).'
),
h1('5. Data flow diagram (text)'),
p('Homepage → Shop (filter URL) → Product page → addItem() → Cart store → Checkout → Order in Auth store → Account page'),
p('Images: lib/images.ts → products.ts → ProductCard / Hero → AppImage → next/image → Unsplash CDN'),
p('Text: i18n/locales/sv|en|ur.ts → useTranslation() → t("key") in components'),
h1('6. Annotated code (line-by-line)'),
p('Location: Kottgard/docs/annotated/'),
table(
[4200, 5160],
[
headerRow(['File', 'Explains'], [4200, 5160]),
dataRow(['01-homepage.annotated.tsx', 'How homepage composes sections'], [4200, 5160]),
dataRow(['02-root-layout.annotated.tsx', 'Fonts, SEO, shell layout'], [4200, 5160]),
dataRow(['03-shop-page.annotated.tsx', 'URL sync, useMemo filtering'], [4200, 5160]),
dataRow(['04-header.annotated.tsx', 'Navigation + Zustand badges'], [4200, 5160]),
dataRow(['05-images.annotated.ts', 'Unsplash URL builder'], [4200, 5160]),
dataRow(['06-cart-store.annotated.ts', 'Cart logic + persist'], [4200, 5160]),
]
),
p(
'Each line has comments explaining WHAT the code does and WHY. These are learning copies — not imported by the live app.'
),
h1('7. Brand colors'),
table(
[3120, 3120, 3120],
[
headerRow(['Name', 'Hex', 'Use'], [3120, 3120, 3120]),
dataRow(['Burgundy (brand)', '#8B1F1F', 'Header, buttons, footer'], [3120, 3120, 3120]),
dataRow(['Cream', 'Warm off-white', 'Page backgrounds'], [3120, 3120, 3120]),
dataRow(['Gold', 'Accent', 'Prices, badges, CTAs'], [3120, 3120, 3120]),
]
),
h1('8. How to run locally'),
bullet('bullets', 'npm install'),
bullet('bullets', 'npm run dev → http://localhost:3000'),
bullet('bullets', 'npm run build → production check'),
],
},
],
});
const buffer = await Packer.toBuffer(doc);
fs.writeFileSync(outPath, buffer);
console.log('Written:', outPath);
+10
View File
@@ -0,0 +1,10 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 2560],
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
};
module.exports = nextConfig;
+9497
View File
File diff suppressed because it is too large Load Diff
+34
View File
@@ -0,0 +1,34 @@
{
"name": "kott-gard",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"annotate": "node docs/annotate-all.mjs",
"images:populate": "bash scripts/populate-site-images.sh",
"images:fish": "bash scripts/update-fish-images.sh"
},
"dependencies": {
"clsx": "^2.1.0",
"lucide-react": "^0.344.0",
"next": "13.5.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zustand": "^4.5.2"
},
"devDependencies": {
"@types/node": "^20.11.0",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"autoprefixer": "^10.4.17",
"docx": "^9.7.1",
"eslint": "^8.56.0",
"eslint-config-next": "13.5.6",
"postcss": "^8.4.33",
"tailwindcss": "^3.4.1",
"typescript": "^5.3.3"
}
}
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
+64
View File
@@ -0,0 +1,64 @@
KÖTT GÅRD — ALL WEBSITE IMAGES (one folder)
============================================
Every image on the website comes from THIS folder only.
HOW TO CHANGE AN IMAGE
----------------------
1. Find the filename below (e.g. beef-steak.jpg)
2. Replace that file with your new photo (keep the SAME filename)
3. Save and refresh the browser (Cmd+Shift+R)
You do NOT need to edit any code for normal image swaps.
FILENAME GUIDE
--------------
BRAND
logo.jpeg Header & footer logo
HOME PAGE
hero.jpg Big background on home page
about.jpg About us section photo
weekly-offers.jpg Weekly offers section background
SHOP CATEGORIES (home page tiles)
category-chicken.jpg
category-beef.jpg
category-lamb.jpg
category-fish.jpg
PRODUCTS (shop, product page, cart)
chicken-whole.jpg Main photo
chicken-whole-2.jpg Second photo on product page (optional)
chicken-breast.jpg
chicken-breast-2.jpg
chicken-thighs.jpg
chicken-wings.jpg
beef-nihari.jpg
beef-nihari-2.jpg
beef-steak.jpg
beef-steak-2.jpg
beef-mince.jpg
beef-boneless.jpg
lamb-shoulder.jpg
lamb-shoulder-2.jpg
lamb-leg.jpg
lamb-chops.jpg
lamb-mince.jpg
fish-salmon.jpg
fish-salmon-2.jpg
fish-rohu.jpg
fish-prawns.jpg
fish-basa.jpg
TIPS
----
• Use JPG or JPEG, about 1400px wide for products, 2560px for hero
• Keep filenames lowercase with hyphens
• Files ending in -2.jpg are extra gallery images on the product page
• To re-download all images: npm run images:populate
Project path:
Kottgard/public/images/site/
Binary file not shown.

After

Width:  |  Height:  |  Size: 508 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 834 KiB

+53
View File
@@ -0,0 +1,53 @@
#!/bin/bash
# Re-download default images into public/images/site/ (only image folder used by the site)
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
SITE="$ROOT/public/images/site"
mkdir -p "$SITE"
dl() {
curl -fsSL "$1" -o "$2"
}
# Keep existing logo if present; otherwise skip (add logo.jpeg manually)
if [[ ! -f "$SITE/logo.jpeg" ]]; then
echo "Note: add logo.jpeg to $SITE manually if missing."
fi
dl "https://images.unsplash.com/photo-1607623814075-e51df1bdc82f?auto=format&fit=crop&w=2560&q=90" "$SITE/hero.jpg"
dl "https://images.unsplash.com/photo-1529692236671-f1f6cf9683ba?auto=format&fit=crop&w=1920&q=90" "$SITE/about.jpg"
cp "$SITE/hero.jpg" "$SITE/weekly-offers.jpg"
dl "https://images.unsplash.com/photo-1587593810167-a84920ea0781?auto=format&fit=crop&w=1400&q=90" "$SITE/category-chicken.jpg"
dl "https://images.unsplash.com/photo-1558030006-450675393462?auto=format&fit=crop&w=1400&q=90" "$SITE/category-beef.jpg"
dl "https://images.unsplash.com/photo-1615937657715-bc7b4b7962c1?auto=format&fit=crop&w=1400&q=90" "$SITE/category-lamb.jpg"
dl "https://images.unsplash.com/photo-1544551763-46a013bb70d5?auto=format&fit=crop&w=1400&q=90" "$SITE/category-fish.jpg"
dl "https://images.unsplash.com/photo-1621996346565-e3dbc646d9a9?auto=format&fit=crop&w=1400&q=90" "$SITE/chicken-whole.jpg"
dl "https://images.unsplash.com/photo-1587593810167-a84920ea0781?auto=format&fit=crop&w=1400&h=1000&crop=center&q=90" "$SITE/chicken-whole-2.jpg"
dl "https://images.unsplash.com/photo-1587593810167-a84920ea0781?auto=format&fit=crop&w=1400&h=1000&crop=center&q=90" "$SITE/chicken-breast.jpg"
cp "$SITE/chicken-whole.jpg" "$SITE/chicken-breast-2.jpg"
dl "https://images.unsplash.com/photo-1621996346565-e3dbc646d9a9?auto=format&fit=crop&w=1400&h=1100&crop=entropy&q=90" "$SITE/chicken-thighs.jpg"
dl "https://images.unsplash.com/photo-1587593810167-a84920ea0781?auto=format&fit=crop&w=1400&h=900&crop=top&q=90" "$SITE/chicken-wings.jpg"
dl "https://images.unsplash.com/photo-1546833999-b9f581a1996d?auto=format&fit=crop&w=1400&q=90" "$SITE/beef-nihari.jpg"
dl "https://images.unsplash.com/photo-1559847844-5315695dadae?auto=format&fit=crop&w=1400&q=90" "$SITE/beef-nihari-2.jpg"
dl "https://images.unsplash.com/photo-1559847844-5315695dadae?auto=format&fit=crop&w=1400&q=90" "$SITE/beef-steak.jpg"
dl "https://images.unsplash.com/photo-1546833999-b9f581a1996d?auto=format&fit=crop&w=1400&q=90" "$SITE/beef-steak-2.jpg"
dl "https://images.unsplash.com/photo-1603048297172-c92544798d5a?auto=format&fit=crop&w=1400&q=90" "$SITE/beef-mince.jpg"
dl "https://images.unsplash.com/photo-1558030006-450675393462?auto=format&fit=crop&w=1400&h=1000&crop=center&q=90" "$SITE/beef-boneless.jpg"
dl "https://images.unsplash.com/photo-1615937657715-bc7b4b7962c1?auto=format&fit=crop&w=1400&q=90" "$SITE/lamb-shoulder.jpg"
dl "https://images.unsplash.com/photo-1544025162-d76694265947?auto=format&fit=crop&w=1400&q=90" "$SITE/lamb-shoulder-2.jpg"
dl "https://images.unsplash.com/photo-1544025162-d76694265947?auto=format&fit=crop&w=1400&q=90" "$SITE/lamb-leg.jpg"
dl "https://images.unsplash.com/photo-1574672280600-4accfa5b6f98?auto=format&fit=crop&w=1400&q=90" "$SITE/lamb-chops.jpg"
dl "https://images.unsplash.com/photo-1574672280600-4accfa5b6f98?auto=format&fit=crop&w=1400&h=1000&crop=center&q=90" "$SITE/lamb-mince.jpg"
dl "https://images.unsplash.com/photo-1544551763-46a013bb70d5?auto=format&fit=crop&w=1400&q=90" "$SITE/fish-salmon.jpg"
dl "https://images.unsplash.com/photo-1504674900247-0877df9cc836?auto=format&fit=crop&w=1400&h=1100&crop=entropy&q=90" "$SITE/fish-salmon-2.jpg"
dl "https://images.unsplash.com/photo-1504674900247-0877df9cc836?auto=format&fit=crop&w=1400&h=1100&crop=entropy&q=90" "$SITE/fish-rohu.jpg"
dl "https://images.unsplash.com/photo-1565680018434-b513d5e5fd47?auto=format&fit=crop&w=1400&q=90" "$SITE/fish-prawns.jpg"
dl "https://images.unsplash.com/photo-1544551763-46a013bb70d5?auto=format&fit=crop&w=1400&h=900&crop=center&q=90" "$SITE/fish-basa.jpg"
echo "Done. $(ls -1 "$SITE" | wc -l | tr -d ' ') files in $SITE"
+14
View File
@@ -0,0 +1,14 @@
#!/bin/bash
# Replace fish images (frozen seafood style)
set -euo pipefail
SITE="$(cd "$(dirname "$0")/.." && pwd)/public/images/site"
dl() { curl -fsSL "$1" -o "$2"; }
dl "https://images.unsplash.com/photo-1559339352-11d035aa65de?auto=format&fit=crop&w=1400&q=90" "$SITE/category-fish.jpg"
dl "https://images.unsplash.com/photo-1559339352-11d035aa65de?auto=format&fit=crop&w=1400&h=1000&crop=center&q=90" "$SITE/fish-salmon.jpg"
dl "https://images.unsplash.com/photo-1544551763-46a013bb70d5?auto=format&fit=crop&w=1400&q=90" "$SITE/fish-salmon-2.jpg"
dl "https://images.unsplash.com/photo-1504674900247-0877df9cc836?auto=format&fit=crop&w=1400&h=1100&crop=entropy&q=90" "$SITE/fish-rohu.jpg"
dl "https://images.unsplash.com/photo-1565680018434-b513d5e5fd47?auto=format&fit=crop&w=1400&q=90" "$SITE/fish-prawns.jpg"
dl "https://images.unsplash.com/photo-1544551763-46a013bb70d5?auto=format&fit=crop&w=1400&h=900&crop=center&q=90" "$SITE/fish-basa.jpg"
echo "Fish images updated in $SITE"
+127
View File
@@ -0,0 +1,127 @@
'use client';
import Link from 'next/link';
import { ShieldCheck, Leaf, Award, Truck, Phone, Mail, MapPin } from 'lucide-react';
import { useTranslation } from '@/hooks/useTranslation';
import {
SITE_ADDRESS,
SITE_EMAIL,
SITE_PHONE_DISPLAY,
SITE_HOURS,
} from '@/lib/constants';
export default function AboutPage() {
const { t } = useTranslation();
const telHref = `tel:${SITE_PHONE_DISPLAY.replace(/\s/g, '')}`;
return (
<div>
<div className="bg-gradient-to-br from-brand-900 to-brand-950 px-4 py-20 text-white sm:px-6 lg:px-8">
<div className="mx-auto max-w-3xl text-center">
<h1 className="mb-4 font-display text-4xl font-bold">
{t('about.title', { name: t('site.name') })}
</h1>
<p className="text-lg text-brand-100">{t('about.subtitle')}</p>
</div>
</div>
<div className="mx-auto max-w-4xl px-4 py-16 sm:px-6 lg:px-8">
<section className="mb-16">
<h2 className="section-heading mb-4">{t('about.ourStory')}</h2>
<p className="mb-4 leading-relaxed text-gray-600">
{t('about.storyP1', { name: t('site.name') })}
</p>
<p className="leading-relaxed text-gray-600">{t('about.storyP2')}</p>
</section>
<section id="halal" className="mb-16">
<div className="mb-6 flex items-center gap-3">
<ShieldCheck className="h-8 w-8 text-brand-700" />
<h2 className="section-heading">{t('about.halalTitle')}</h2>
</div>
<p className="leading-relaxed text-gray-600">
{t('about.halalDesc', { name: t('site.name') })}
</p>
</section>
<section className="mb-16">
<div className="grid gap-6 sm:grid-cols-3">
{[
{ icon: Leaf, title: t('about.freshDaily'), desc: t('about.freshDailyDesc') },
{
icon: Award,
title: t('about.premiumQuality'),
desc: t('about.premiumQualityDesc'),
},
{
icon: Truck,
title: t('about.fastDelivery'),
desc: t('about.fastDeliveryDesc'),
},
].map((item) => (
<div key={item.title} className="card-premium p-6 text-center">
<item.icon className="mx-auto mb-3 h-8 w-8 text-brand-700" />
<h3 className="mb-1 font-semibold text-brand-900">{item.title}</h3>
<p className="text-sm text-gray-500">{item.desc}</p>
</div>
))}
</div>
</section>
<section id="delivery" className="mb-16">
<h2 className="section-heading mb-4">{t('about.deliveryTitle')}</h2>
<div className="space-y-3 text-gray-600">
<p>{t('about.delivery1')}</p>
<p>{t('about.delivery2')}</p>
<p>{t('about.delivery3')}</p>
<p>{t('about.delivery4')}</p>
</div>
</section>
<section id="contact" className="card-premium mb-16 p-8">
<h2 className="section-heading mb-6">{t('about.contactTitle')}</h2>
<div className="space-y-4">
<div className="flex items-center gap-3 text-gray-600">
<Phone className="h-5 w-5 text-brand-700" />
<a href={telHref} className="hover:text-brand-800">
{SITE_PHONE_DISPLAY}
</a>
</div>
<div className="flex items-center gap-3 text-gray-600">
<Mail className="h-5 w-5 text-brand-700" />
<a href={`mailto:${SITE_EMAIL}`} className="hover:text-brand-800">
{SITE_EMAIL}
</a>
</div>
<div className="flex items-start gap-3 text-gray-600">
<MapPin className="mt-0.5 h-5 w-5 shrink-0 text-brand-700" />
<span>
{SITE_ADDRESS}
<br />
{t('footer.hours', { hours: SITE_HOURS })}
</span>
</div>
</div>
<div className="mt-6">
<Link
href="/shop"
className="text-sm font-semibold text-brand-700 hover:text-brand-900"
>
{t('cta.button')}
</Link>
</div>
</section>
<section id="privacy" className="mb-16">
<h2 className="section-heading mb-4">{t('about.privacyTitle')}</h2>
<p className="leading-relaxed text-gray-600">{t('about.privacyText')}</p>
</section>
<section id="terms" className="mb-8">
<h2 className="section-heading mb-4">{t('about.termsTitle')}</h2>
<p className="leading-relaxed text-gray-600">{t('about.termsText')}</p>
</section>
</div>
</div>
);
}
+164
View File
@@ -0,0 +1,164 @@
'use client';
import { useEffect } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { User, Package, MapPin, Phone, Mail, LogOut, Heart } from 'lucide-react';
import Button from '@/components/ui/Button';
import { useAuth } from '@/presentation/hooks/useAuth';
import { formatPrice, formatDate, getFormatLocale } from '@/lib/utils';
import { useTranslation } from '@/hooks/useTranslation';
import { localizeProduct } from '@/lib/product-i18n';
export default function AccountPage() {
const router = useRouter();
const { user, isAuthenticated, orders, logout } = useAuth();
const { t, locale } = useTranslation();
useEffect(() => {
if (!isAuthenticated) {
router.push('/login');
}
}, [isAuthenticated, router]);
if (!isAuthenticated || !user) {
return null;
}
const handleLogout = () => {
logout();
router.push('/');
};
const fmt = (n: number) => formatPrice(n, getFormatLocale(locale));
return (
<div className="bg-gray-50">
<div className="border-b border-gray-100 bg-white">
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
<h1 className="section-heading">{t('account.title')}</h1>
<p className="text-gray-500">{t('account.welcome', { name: user.name })}</p>
</div>
</div>
<div className="mx-auto max-w-7xl px-4 py-10 sm:px-6 lg:px-8">
<div className="grid gap-8 lg:grid-cols-3">
<div className="space-y-6">
<div className="card-premium p-6">
<div className="mb-4 flex items-center gap-3">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-brand-100">
<User className="h-6 w-6 text-brand-700" />
</div>
<div>
<h2 className="font-display text-lg font-semibold">{user.name}</h2>
<p className="text-sm text-gray-500">
{t('account.memberSince', { date: formatDate(user.createdAt) })}
</p>
</div>
</div>
<div className="space-y-3 border-t border-gray-100 pt-4">
<div className="flex items-center gap-2 text-sm text-gray-600">
<Mail className="h-4 w-4 text-brand-600" />
{user.email}
</div>
<div className="flex items-center gap-2 text-sm text-gray-600">
<Phone className="h-4 w-4 text-brand-600" />
{user.phone}
</div>
<div className="flex items-start gap-2 text-sm text-gray-600">
<MapPin className="mt-0.5 h-4 w-4 shrink-0 text-brand-600" />
{user.address.street}, {user.address.city}, {user.address.state}{' '}
{user.address.zip}
</div>
</div>
</div>
<div className="card-premium p-4">
<Link
href="/wishlist"
className="flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium text-gray-600 transition-colors hover:bg-brand-50 hover:text-brand-700"
>
<Heart className="h-4 w-4" />
{t('account.myWishlist')}
</Link>
<button
onClick={handleLogout}
className="flex w-full items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium text-red-500 transition-colors hover:bg-red-50"
>
<LogOut className="h-4 w-4" />
{t('account.signOut')}
</button>
</div>
</div>
<div className="lg:col-span-2" id="orders">
<div className="card-premium p-6">
<div className="mb-6 flex items-center gap-2">
<Package className="h-5 w-5 text-brand-700" />
<h2 className="font-display text-lg font-semibold">
{t('account.orderHistory')}
</h2>
</div>
{orders.length === 0 ? (
<div className="py-12 text-center">
<p className="mb-2 text-gray-500">{t('account.noOrders')}</p>
<Link href="/shop">
<Button variant="primary">{t('account.startShopping')}</Button>
</Link>
</div>
) : (
<div className="space-y-4">
{orders.map((order) => (
<div
key={order.id}
className="rounded-xl border border-gray-100 p-4 transition-colors hover:border-brand-200"
>
<div className="mb-3 flex items-center justify-between">
<div>
<p className="text-sm font-semibold text-brand-900">{order.id}</p>
<p className="text-xs text-gray-400">
{formatDate(order.createdAt)}
</p>
</div>
<div className="text-end">
<p className="font-bold text-brand-800">{fmt(order.total)}</p>
<span className="inline-block rounded-full bg-brand-50 px-2 py-0.5 text-xs font-medium capitalize text-brand-700">
{t(`orderStatus.${order.status}`)}
</span>
</div>
</div>
<div className="space-y-1">
{order.items.map((item) => {
const localized = localizeProduct(item.product, t);
return (
<div
key={item.id}
className="flex justify-between text-sm text-gray-600"
>
<Link
href={`/product/${item.product.slug}`}
className="hover:text-brand-700"
>
{localized.name} × {item.quantity}
<span className="ms-2 text-xs text-gold-600">
({item.customizationLabel})
</span>
</Link>
<span>{fmt(item.product.price * item.quantity)}</span>
</div>
);
})}
</div>
</div>
))}
</div>
)}
</div>
</div>
</div>
</div>
</div>
);
}
+176
View File
@@ -0,0 +1,176 @@
'use client';
import AppImage from '@/components/ui/AppImage';
import Link from 'next/link';
import { Minus, Plus, Trash2, ShoppingBag, ArrowRight } from 'lucide-react';
import Button from '@/components/ui/Button';
import { useCart } from '@/presentation/hooks/useCart';
import { formatPrice, getFormatLocale } from '@/lib/utils';
import { useTranslation } from '@/hooks/useTranslation';
import { localizeProduct } from '@/lib/product-i18n';
export default function CartPage() {
const { items, updateQuantity, removeItem, summary } = useCart();
const { t, locale } = useTranslation();
const { subtotal: total, deliveryFee, grandTotal } = summary;
const fmt = (n: number) => formatPrice(n, getFormatLocale(locale));
if (items.length === 0) {
return (
<div className="mx-auto max-w-7xl px-4 py-20 text-center sm:px-6 lg:px-8">
<div className="mx-auto mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-brand-50">
<ShoppingBag className="h-10 w-10 text-brand-300" />
</div>
<h1 className="mb-2 font-display text-2xl font-bold text-brand-900">
{t('cart.empty')}
</h1>
<p className="mb-8 text-gray-500">{t('cart.emptyHint')}</p>
<Link href="/shop">
<Button variant="primary" size="lg">
{t('cart.startShopping')}
</Button>
</Link>
</div>
);
}
return (
<div className="bg-gray-50">
<div className="border-b border-gray-100 bg-white">
<div className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
<h1 className="section-heading">{t('cart.title')}</h1>
<p className="text-gray-500">
{t('cart.itemsCount', { count: items.length })}
</p>
</div>
</div>
<div className="mx-auto max-w-7xl px-4 py-10 sm:px-6 lg:px-8">
<div className="grid gap-8 lg:grid-cols-3">
<div className="space-y-4 lg:col-span-2">
{items.map((item) => {
const localized = localizeProduct(item.product, t);
return (
<div key={item.id} className="card-premium flex gap-4 p-4 sm:p-6">
<div className="relative h-24 w-24 shrink-0 overflow-hidden rounded-xl sm:h-28 sm:w-28">
<AppImage
src={item.product.image}
alt={localized.name}
fill
className="object-cover"
sizes="(max-width: 640px) 96px, 112px"
/>
</div>
<div className="flex flex-1 flex-col">
<div className="flex items-start justify-between">
<div>
<Link
href={`/product/${item.product.slug}`}
className="font-display text-lg font-semibold text-brand-900 hover:text-brand-700"
>
{localized.name}
</Link>
<p className="mt-1 text-xs uppercase tracking-wider text-brand-600">
{t(`categories.${item.product.category}.name`)}
</p>
</div>
<button
onClick={() => removeItem(item.id)}
className="rounded-lg p-2 text-gray-400 transition-colors hover:bg-red-50 hover:text-red-500"
aria-label={t('cart.removeItem')}
>
<Trash2 className="h-4 w-4" />
</button>
</div>
<div className="mt-2 inline-flex items-center gap-1.5 rounded-full bg-gold-50 px-3 py-1">
<span className="text-xs font-medium text-gold-700">
{t('cart.customization')}
</span>
<span className="text-xs font-semibold text-gold-800">
{item.customizationLabel}
</span>
</div>
<div className="mt-auto flex items-center justify-between pt-3">
<div className="flex items-center rounded-lg border border-gray-200">
<button
onClick={() => updateQuantity(item.id, item.quantity - 1)}
className="flex h-9 w-9 items-center justify-center text-gray-500 hover:text-brand-700"
aria-label={t('product.decreaseQty')}
>
<Minus className="h-3 w-3" />
</button>
<span className="w-8 text-center text-sm font-semibold">
{item.quantity}
</span>
<button
onClick={() => updateQuantity(item.id, item.quantity + 1)}
className="flex h-9 w-9 items-center justify-center text-gray-500 hover:text-brand-700"
aria-label={t('product.increaseQty')}
>
<Plus className="h-3 w-3" />
</button>
</div>
<span className="text-lg font-bold text-brand-800">
{fmt(item.product.price * item.quantity)}
</span>
</div>
</div>
</div>
);
})}
</div>
<div>
<div className="card-premium sticky top-24 p-6">
<h2 className="mb-4 font-display text-lg font-semibold text-brand-900">
{t('cart.orderSummary')}
</h2>
<div className="space-y-3 border-b border-gray-100 pb-4">
<div className="flex justify-between text-sm">
<span className="text-gray-500">{t('cart.subtotal')}</span>
<span className="font-medium">{fmt(total)}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-500">{t('cart.delivery')}</span>
<span className="font-medium">
{deliveryFee === 0 ? (
<span className="text-brand-700">{t('cart.free')}</span>
) : (
fmt(deliveryFee)
)}
</span>
</div>
{deliveryFee > 0 && (
<p className="text-xs text-gray-400">{t('cart.freeDeliveryHint')}</p>
)}
</div>
<div className="flex justify-between py-4">
<span className="font-semibold text-brand-900">{t('cart.total')}</span>
<span className="text-xl font-bold text-brand-800">{fmt(grandTotal)}</span>
</div>
<Link href="/checkout">
<Button variant="gold" size="lg" className="w-full">
{t('cart.proceedCheckout')}
<ArrowRight className="h-4 w-4 rtl:rotate-180" />
</Button>
</Link>
<Link
href="/shop"
className="mt-3 block text-center text-sm font-medium text-brand-600 hover:text-brand-800"
>
{t('cart.continueShopping')}
</Link>
</div>
</div>
</div>
</div>
</div>
);
}
+352
View File
@@ -0,0 +1,352 @@
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import AppImage from '@/components/ui/AppImage';
import Link from 'next/link';
import { Lock, CreditCard, Truck, CheckCircle, ChevronLeft } from 'lucide-react';
import Button from '@/components/ui/Button';
import { useCart } from '@/presentation/hooks/useCart';
import { useAuth } from '@/presentation/hooks/useAuth';
import { useCheckout } from '@/presentation/hooks/useCheckout';
import { formatPrice, getFormatLocale } from '@/lib/utils';
import { useTranslation } from '@/hooks/useTranslation';
import { localizeProduct } from '@/lib/product-i18n';
export default function CheckoutPage() {
const router = useRouter();
const { items, summary } = useCart();
const { user, isAuthenticated } = useAuth();
const { placeOrder } = useCheckout();
const { t, locale } = useTranslation();
const [paymentMethod, setPaymentMethod] = useState('card');
const [isProcessing, setIsProcessing] = useState(false);
const [orderComplete, setOrderComplete] = useState(false);
const [orderId, setOrderId] = useState('');
const [form, setForm] = useState({
name: user?.name || '',
email: user?.email || '',
phone: user?.phone || '',
street: user?.address.street || '',
city: user?.address.city || '',
state: user?.address.state || '',
zip: user?.address.zip || '',
cardNumber: '',
expiry: '',
cvv: '',
});
const { subtotal: total, deliveryFee, grandTotal } = summary;
const fmt = (n: number) => formatPrice(n, getFormatLocale(locale));
if (items.length === 0 && !orderComplete) {
return (
<div className="mx-auto max-w-7xl px-4 py-20 text-center">
<h1 className="mb-4 font-display text-2xl font-bold">{t('checkout.noItems')}</h1>
<Link href="/shop">
<Button>{t('checkout.goToShop')}</Button>
</Link>
</div>
);
}
if (orderComplete) {
return (
<div className="mx-auto max-w-lg px-4 py-20 text-center">
<div className="mx-auto mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-brand-50">
<CheckCircle className="h-12 w-12 text-brand-700" />
</div>
<h1 className="mb-2 font-display text-3xl font-bold text-brand-900">
{t('checkout.orderConfirmed')}
</h1>
<p className="mb-2 text-gray-500">{t('checkout.thankYou')}</p>
<p className="mb-8 text-sm font-medium text-brand-700">
{t('checkout.orderId', { id: orderId })}
</p>
<div className="flex flex-col gap-3 sm:flex-row sm:justify-center">
<Link href="/account">
<Button variant="primary">{t('checkout.viewOrders')}</Button>
</Link>
<Link href="/shop">
<Button variant="secondary">{t('cart.continueShopping')}</Button>
</Link>
</div>
</div>
);
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsProcessing(true);
await new Promise((resolve) => setTimeout(resolve, 1500));
const { order } = placeOrder({
deliveryAddress: {
street: form.street,
city: form.city,
state: form.state,
zip: form.zip,
},
paymentMethod,
});
setOrderId(order.id);
setOrderComplete(true);
setIsProcessing(false);
};
const updateField = (field: string, value: string) => {
setForm((prev) => ({ ...prev, [field]: value }));
};
return (
<div className="bg-gray-50">
<div className="mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
<Link
href="/cart"
className="mb-6 inline-flex items-center gap-1 text-sm font-medium text-gray-500 hover:text-brand-700"
>
<ChevronLeft className="h-4 w-4 rtl:rotate-180" />
{t('checkout.backToCart')}
</Link>
<h1 className="section-heading mb-8">{t('checkout.title')}</h1>
<form onSubmit={handleSubmit}>
<div className="grid gap-8 lg:grid-cols-3">
<div className="space-y-6 lg:col-span-2">
{!isAuthenticated && (
<div className="card-premium p-6">
<p className="text-sm text-gray-600">
{t('checkout.haveAccount')}{' '}
<Link href="/login" className="font-semibold text-brand-700 hover:underline">
{t('checkout.signIn')}
</Link>{' '}
{t('checkout.fasterCheckout')}
</p>
</div>
)}
<div className="card-premium p-6">
<div className="mb-4 flex items-center gap-2">
<Truck className="h-5 w-5 text-brand-700" />
<h2 className="font-display text-lg font-semibold">
{t('checkout.deliveryDetails')}
</h2>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<div className="sm:col-span-2">
<label className="label-text">{t('checkout.fullName')}</label>
<input
required
className="input-field"
value={form.name}
onChange={(e) => updateField('name', e.target.value)}
/>
</div>
<div>
<label className="label-text">{t('checkout.email')}</label>
<input
required
type="email"
className="input-field"
value={form.email}
onChange={(e) => updateField('email', e.target.value)}
/>
</div>
<div>
<label className="label-text">{t('checkout.phone')}</label>
<input
required
type="tel"
className="input-field"
value={form.phone}
onChange={(e) => updateField('phone', e.target.value)}
/>
</div>
<div className="sm:col-span-2">
<label className="label-text">{t('checkout.street')}</label>
<input
required
className="input-field"
value={form.street}
onChange={(e) => updateField('street', e.target.value)}
/>
</div>
<div>
<label className="label-text">{t('checkout.city')}</label>
<input
required
className="input-field"
value={form.city}
onChange={(e) => updateField('city', e.target.value)}
/>
</div>
<div>
<label className="label-text">{t('checkout.state')}</label>
<input
required
className="input-field"
value={form.state}
onChange={(e) => updateField('state', e.target.value)}
/>
</div>
<div>
<label className="label-text">{t('checkout.zip')}</label>
<input
required
className="input-field"
value={form.zip}
onChange={(e) => updateField('zip', e.target.value)}
/>
</div>
</div>
</div>
<div className="card-premium p-6">
<div className="mb-4 flex items-center gap-2">
<CreditCard className="h-5 w-5 text-brand-700" />
<h2 className="font-display text-lg font-semibold">{t('checkout.payment')}</h2>
</div>
<div className="mb-4 flex gap-3">
{['card', 'cod'].map((method) => (
<button
key={method}
type="button"
onClick={() => setPaymentMethod(method)}
className={`rounded-lg border-2 px-4 py-2 text-sm font-medium transition-all ${
paymentMethod === method
? 'border-brand-700 bg-brand-700 text-white'
: 'border-gray-200 text-gray-600 hover:border-brand-300'
}`}
>
{method === 'card'
? t('checkout.creditCard')
: t('checkout.cashOnDelivery')}
</button>
))}
</div>
{paymentMethod === 'card' && (
<div className="grid gap-4 sm:grid-cols-2">
<div className="sm:col-span-2">
<label className="label-text">{t('checkout.cardNumber')}</label>
<input
required
className="input-field"
placeholder="1234 5678 9012 3456"
value={form.cardNumber}
onChange={(e) => updateField('cardNumber', e.target.value)}
/>
</div>
<div>
<label className="label-text">{t('checkout.expiry')}</label>
<input
required
className="input-field"
placeholder="MM/YY"
value={form.expiry}
onChange={(e) => updateField('expiry', e.target.value)}
/>
</div>
<div>
<label className="label-text">{t('checkout.cvv')}</label>
<input
required
className="input-field"
placeholder="123"
value={form.cvv}
onChange={(e) => updateField('cvv', e.target.value)}
/>
</div>
</div>
)}
</div>
</div>
<div>
<div className="card-premium sticky top-24 p-6">
<h2 className="mb-4 font-display text-lg font-semibold">
{t('cart.orderSummary')}
</h2>
<div className="mb-4 max-h-60 space-y-3 overflow-y-auto">
{items.map((item) => {
const localized = localizeProduct(item.product, t);
return (
<div key={item.id} className="flex gap-3">
<div className="relative h-12 w-12 shrink-0 overflow-hidden rounded-lg">
<AppImage
src={item.product.image}
alt={localized.name}
fill
className="object-cover"
sizes="48px"
/>
</div>
<div className="flex-1">
<p className="text-sm font-medium text-brand-900">
{localized.name}
</p>
<p className="text-xs text-gold-700">
{item.customizationLabel}
</p>
<p className="text-xs text-gray-400">
{t('checkout.qty', { count: item.quantity })}
</p>
</div>
<span className="text-sm font-medium">
{fmt(item.product.price * item.quantity)}
</span>
</div>
);
})}
</div>
<div className="space-y-2 border-t border-gray-100 pt-4">
<div className="flex justify-between text-sm">
<span className="text-gray-500">{t('cart.subtotal')}</span>
<span>{fmt(total)}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-gray-500">{t('cart.delivery')}</span>
<span>
{deliveryFee === 0 ? t('cart.free') : fmt(deliveryFee)}
</span>
</div>
<div className="flex justify-between pt-2 text-lg font-bold">
<span>{t('cart.total')}</span>
<span className="text-brand-800">{fmt(grandTotal)}</span>
</div>
</div>
<Button
type="submit"
variant="gold"
size="lg"
className="mt-6 w-full"
disabled={isProcessing}
>
<Lock className="h-4 w-4" />
{isProcessing
? t('checkout.processing')
: t('checkout.pay', { amount: fmt(grandTotal) })}
</Button>
<p className="mt-3 flex items-center justify-center gap-1 text-xs text-gray-400">
<Lock className="h-3 w-3" />
{t('checkout.secure')}
</p>
</div>
</div>
</div>
</form>
</div>
</div>
);
}

Some files were not shown because too many files have changed in this diff Show More