From 284dcfa1209b40808b9cbbd8200d266e7b395845 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 25 Jun 2026 09:23:48 +0000 Subject: [PATCH] Add shahikitchen v1.0 website code from zip --- README.md | 69 + docs/CLEAN-ARCHITECTURE.md | 365 + docs/Kottgard-Website-Guide.docx | Bin 0 -> 11989 bytes docs/annotate-all.mjs | 195 + docs/annotated/README.md | 63 + .../src/app/about/page.annotated.tsx | 249 + .../src/app/account/page.annotated.tsx | 321 + .../annotated/src/app/cart/page.annotated.tsx | 348 + .../src/app/checkout/page.annotated.tsx | 701 ++ docs/annotated/src/app/layout.annotated.tsx | 174 + .../src/app/login/page.annotated.tsx | 384 + .../annotated/src/app/not-found.annotated.tsx | 44 + docs/annotated/src/app/page.annotated.tsx | 58 + .../src/app/product/[slug]/page.annotated.tsx | 434 + .../src/app/shop/layout.annotated.tsx | 48 + .../annotated/src/app/shop/page.annotated.tsx | 275 + .../src/app/wishlist/page.annotated.tsx | 103 + .../home/AboutPreview.annotated.tsx | 132 + .../src/components/home/CTA.annotated.tsx | 95 + .../home/CategoryGrid.annotated.tsx | 110 + .../home/ContactPreview.annotated.tsx | 233 + .../home/FeaturedProducts.annotated.tsx | 89 + .../src/components/home/Hero.annotated.tsx | 184 + .../components/home/HowItWorks.annotated.tsx | 139 + .../home/SocialFollow.annotated.tsx | 132 + .../components/home/TrustBadges.annotated.tsx | 109 + .../home/WeeklyOffers.annotated.tsx | 255 + .../components/layout/Footer.annotated.tsx | 355 + .../components/layout/Header.annotated.tsx | 254 + .../layout/LanguageBanner.annotated.tsx | 129 + .../layout/LanguageSwitcher.annotated.tsx | 135 + .../layout/LocaleAttributes.annotated.tsx | 33 + .../CustomizationSelector.annotated.tsx | 202 + .../product/ProductCard.annotated.tsx | 174 + .../components/shop/ShopFilters.annotated.tsx | 214 + .../src/components/ui/AppImage.annotated.tsx | 25 + .../src/components/ui/Button.annotated.tsx | 76 + .../src/components/ui/Logo.annotated.tsx | 74 + .../src/hooks/useTranslation.annotated.ts | 27 + docs/annotated/src/i18n/index.annotated.ts | 85 + .../src/i18n/locales/en.annotated.ts | 934 ++ .../src/i18n/locales/sv.annotated.ts | 934 ++ .../src/i18n/locales/ur.annotated.ts | 936 ++ docs/annotated/src/i18n/types.annotated.ts | 28 + docs/annotated/src/lib/constants.annotated.ts | 74 + .../src/lib/customization.annotated.ts | 114 + .../src/lib/demo-orders.annotated.ts | 128 + docs/annotated/src/lib/images.annotated.ts | 170 + docs/annotated/src/lib/offers.annotated.ts | 78 + .../src/lib/product-i18n.annotated.ts | 88 + docs/annotated/src/lib/products.annotated.ts | 464 + docs/annotated/src/lib/utils.annotated.ts | 59 + docs/annotated/src/store/auth.annotated.ts | 179 + docs/annotated/src/store/cart.annotated.ts | 164 + docs/annotated/src/store/locale.annotated.ts | 39 + .../annotated/src/store/wishlist.annotated.ts | 81 + docs/annotated/src/types/index.annotated.ts | 168 + docs/generate-guide-docx.mjs | 272 + next-env.d.ts | 5 + next.config.js | 21 + package-lock.json | 9497 +++++++++++++++++ package.json | 37 + postcss.config.js | 6 + public/images/site/README.txt | 64 + public/images/site/_originals/about.jpg | Bin 0 -> 717969 bytes .../images/site/_originals/beef-boneless.jpg | Bin 0 -> 395157 bytes public/images/site/_originals/beef-mince.jpg | Bin 0 -> 361428 bytes .../images/site/_originals/beef-nihari-2.jpg | Bin 0 -> 567431 bytes public/images/site/_originals/beef-nihari.jpg | Bin 0 -> 567431 bytes .../images/site/_originals/beef-steak-2.jpg | Bin 0 -> 815342 bytes public/images/site/_originals/beef-steak.jpg | Bin 0 -> 815342 bytes .../images/site/_originals/category-beef.jpg | Bin 0 -> 395157 bytes .../site/_originals/category-chicken.jpg | Bin 0 -> 736638 bytes .../images/site/_originals/category-fish.jpg | Bin 0 -> 806169 bytes .../images/site/_originals/category-lamb.jpg | Bin 0 -> 384391 bytes .../site/_originals/chicken-breast copy.jpg | Bin 0 -> 1486986 bytes .../site/_originals/chicken-breast-2.jpg | Bin 0 -> 395941 bytes .../images/site/_originals/chicken-breast.jpg | Bin 0 -> 395941 bytes .../images/site/_originals/chicken-thighs.jpg | Bin 0 -> 290783 bytes .../site/_originals/chicken-whole-1.jpg | Bin 0 -> 819746 bytes .../site/_originals/chicken-whole-2.jpg | Bin 0 -> 819746 bytes .../images/site/_originals/chicken-whole.jpg | Bin 0 -> 819746 bytes .../images/site/_originals/chicken-wings.jpg | Bin 0 -> 211579 bytes public/images/site/_originals/fish-basa.jpg | Bin 0 -> 649762 bytes public/images/site/_originals/fish-prawns.jpg | Bin 0 -> 538920 bytes public/images/site/_originals/fish-rohu.jpg | Bin 0 -> 1015671 bytes .../images/site/_originals/fish-salmon-2.jpg | Bin 0 -> 659720 bytes public/images/site/_originals/fish-salmon.jpg | Bin 0 -> 762176 bytes public/images/site/_originals/hero.jpg | Bin 0 -> 709334 bytes public/images/site/_originals/lamb-chops.jpg | Bin 0 -> 402965 bytes .../images/site/_originals/lamb-leg copy.jpg | Bin 0 -> 219170 bytes public/images/site/_originals/lamb-leg.jpg | Bin 0 -> 219170 bytes public/images/site/_originals/lamb-mince.jpg | Bin 0 -> 361428 bytes .../site/_originals/lamb-shoulder-2.jpg | Bin 0 -> 384391 bytes .../images/site/_originals/lamb-shoulder.jpg | Bin 0 -> 384391 bytes .../images/site/_originals/weekly-offers.jpg | Bin 0 -> 709334 bytes public/images/site/about.jpg | Bin 0 -> 100576 bytes public/images/site/beef-boneless.jpg | Bin 0 -> 100974 bytes public/images/site/beef-mince.jpg | Bin 0 -> 100827 bytes public/images/site/beef-nihari-2.jpg | Bin 0 -> 100820 bytes public/images/site/beef-nihari.jpg | Bin 0 -> 100820 bytes public/images/site/beef-steak-2.jpg | Bin 0 -> 101578 bytes public/images/site/beef-steak.jpg | Bin 0 -> 101578 bytes public/images/site/category-beef.jpg | Bin 0 -> 100974 bytes public/images/site/category-chicken.jpg | Bin 0 -> 100631 bytes public/images/site/category-fish.jpg | Bin 0 -> 100566 bytes public/images/site/category-lamb.jpg | Bin 0 -> 101492 bytes public/images/site/chicken-breast copy.jpg | Bin 0 -> 1486986 bytes public/images/site/chicken-breast-2.jpg | Bin 0 -> 100693 bytes public/images/site/chicken-breast.jpg | Bin 0 -> 100693 bytes public/images/site/chicken-thighs.jpg | Bin 0 -> 99907 bytes public/images/site/chicken-whole-1.jpg | Bin 0 -> 819746 bytes public/images/site/chicken-whole-2.jpg | Bin 0 -> 100116 bytes public/images/site/chicken-whole.jpg | Bin 0 -> 100116 bytes public/images/site/chicken-wings.jpg | Bin 0 -> 101756 bytes public/images/site/fish-basa.jpg | Bin 0 -> 101954 bytes public/images/site/fish-prawns.jpg | Bin 0 -> 101716 bytes public/images/site/fish-rohu.jpg | Bin 0 -> 99293 bytes public/images/site/fish-salmon-2.jpg | Bin 0 -> 99990 bytes public/images/site/fish-salmon.jpg | Bin 0 -> 102232 bytes public/images/site/hero.jpg | Bin 0 -> 101250 bytes public/images/site/lamb-chops.jpg | Bin 0 -> 101212 bytes public/images/site/lamb-leg copy.jpg | Bin 0 -> 219170 bytes public/images/site/lamb-leg.jpg | Bin 0 -> 101456 bytes public/images/site/lamb-mince.jpg | Bin 0 -> 100827 bytes public/images/site/lamb-shoulder-2.jpg | Bin 0 -> 101492 bytes public/images/site/lamb-shoulder.jpg | Bin 0 -> 101492 bytes public/images/site/logo.jpeg | Bin 0 -> 68754 bytes public/images/site/weekly-offers.jpg | Bin 0 -> 101250 bytes scripts/branded-beef-boneless.py | 130 + scripts/build.sh | 9 + scripts/dev.sh | 14 + scripts/optimize-site-images.py | 118 + scripts/populate-site-images.sh | 53 + scripts/process-all-site-images.py | 213 + scripts/update-fish-images.sh | 14 + src/app/about/page.tsx | 130 + src/app/account/page.tsx | 164 + src/app/cart/page.tsx | 176 + src/app/checkout/page.tsx | 352 + src/app/globals.css | 75 + src/app/home/page.tsx | 16 + src/app/layout.tsx | 97 + src/app/login/page.tsx | 196 + src/app/not-found.tsx | 21 + src/app/page.tsx | 12 + src/app/product/[slug]/page.tsx | 224 + src/app/shop/layout.tsx | 23 + src/app/shop/page.tsx | 111 + src/app/wishlist/page.tsx | 51 + src/application/container.ts | 64 + src/application/dtos/CartSummary.ts | 7 + src/application/dtos/LocalizedProduct.ts | 19 + src/application/ports/IAuthRepository.ts | 16 + src/application/ports/ICartRepository.ts | 18 + src/application/ports/IProductRepository.ts | 12 + src/application/ports/ITranslationService.ts | 9 + src/application/ports/IWishlistRepository.ts | 12 + src/application/use-cases/auth/Login.ts | 9 + src/application/use-cases/auth/Register.ts | 10 + src/application/use-cases/cart/AddToCart.ts | 18 + .../use-cases/cart/GetCartSummary.ts | 25 + .../use-cases/cart/RemoveFromCart.ts | 9 + .../use-cases/cart/UpdateCartQuantity.ts | 12 + .../use-cases/catalog/FilterProducts.ts | 52 + .../use-cases/catalog/GetProductBySlug.ts | 13 + .../use-cases/catalog/LocalizeProduct.ts | 28 + .../use-cases/checkout/PlaceOrder.ts | 37 + .../use-cases/wishlist/ToggleWishlist.ts | 10 + src/components/about/AboutIntro.tsx | 57 + src/components/home/CTA.tsx | 44 + src/components/home/CategoryGrid.tsx | 55 + src/components/home/ContactPreview.tsx | 117 + src/components/home/FeaturedProducts.tsx | 130 + src/components/home/Hero.tsx | 101 + src/components/home/HomeView.tsx | 17 + src/components/home/HowItWorks.tsx | 68 + src/components/home/SocialFollow.tsx | 66 + src/components/home/TrustBadges.tsx | 85 + src/components/home/WeeklyOffers.tsx | 130 + src/components/layout/Footer.tsx | 180 + src/components/layout/Header.tsx | 158 + src/components/layout/LanguageBanner.tsx | 73 + src/components/layout/LanguageSwitcher.tsx | 68 + src/components/layout/LocaleAttributes.tsx | 16 + .../product/CustomizationSelector.tsx | 102 + src/components/product/ProductCard.tsx | 88 + src/components/shop/ShopFilters.tsx | 109 + src/components/ui/AppImage.tsx | 11 + src/components/ui/Button.tsx | 116 + src/components/ui/Logo.tsx | 36 + src/domain/constants/commerce.ts | 7 + src/domain/entities/index.ts | 100 + src/domain/services/AuthDomainService.ts | 34 + src/domain/services/CartDomainService.ts | 48 + src/domain/services/CatalogDomainService.ts | 77 + .../services/CustomizationDomainService.ts | 72 + src/domain/services/DeliveryFeeService.ts | 21 + src/domain/services/OrderDomainService.ts | 33 + src/hooks/useTranslation.ts | 13 + src/i18n/index.ts | 66 + src/i18n/locales/ar.ts | 467 + src/i18n/locales/en.ts | 467 + src/i18n/locales/fa.ts | 467 + src/i18n/locales/sv.ts | 467 + src/i18n/locales/tr.ts | 467 + src/i18n/locales/ur.ts | 468 + src/i18n/types.ts | 16 + src/infrastructure/data/demo-orders.data.ts | 68 + src/infrastructure/data/products.data.ts | 197 + .../i18n/I18nTranslationService.ts | 18 + src/infrastructure/images/catalog.ts | 116 + src/infrastructure/images/index.ts | 80 + .../persistence/zustand/authStore.ts | 107 + .../persistence/zustand/cartStore.ts | 70 + .../persistence/zustand/localeStore.ts | 18 + .../persistence/zustand/wishlistStore.ts | 51 + .../repositories/InMemoryProductRepository.ts | 30 + src/lib/constants.ts | 39 + src/lib/customization.ts | 19 + src/lib/demo-orders.ts | 1 + src/lib/images.ts | 6 + src/lib/offers.ts | 37 + src/lib/product-i18n.ts | 33 + src/lib/products.ts | 18 + src/lib/utils.ts | 32 + src/presentation/hooks/useAuth.ts | 25 + src/presentation/hooks/useCart.ts | 39 + src/presentation/hooks/useCatalog.ts | 37 + src/presentation/hooks/useCheckout.ts | 13 + src/presentation/hooks/useWishlist.ts | 19 + src/store/auth.ts | 5 + src/store/cart.ts | 5 + src/store/locale.ts | 1 + src/store/wishlist.ts | 5 + src/types/index.ts | 4 + tailwind.config.ts | 85 + tsconfig.json | 20 + tsconfig.tsbuildinfo | 1 + 239 files changed, 30527 insertions(+) create mode 100644 README.md create mode 100644 docs/CLEAN-ARCHITECTURE.md create mode 100644 docs/Kottgard-Website-Guide.docx create mode 100644 docs/annotate-all.mjs create mode 100644 docs/annotated/README.md create mode 100644 docs/annotated/src/app/about/page.annotated.tsx create mode 100644 docs/annotated/src/app/account/page.annotated.tsx create mode 100644 docs/annotated/src/app/cart/page.annotated.tsx create mode 100644 docs/annotated/src/app/checkout/page.annotated.tsx create mode 100644 docs/annotated/src/app/layout.annotated.tsx create mode 100644 docs/annotated/src/app/login/page.annotated.tsx create mode 100644 docs/annotated/src/app/not-found.annotated.tsx create mode 100644 docs/annotated/src/app/page.annotated.tsx create mode 100644 docs/annotated/src/app/product/[slug]/page.annotated.tsx create mode 100644 docs/annotated/src/app/shop/layout.annotated.tsx create mode 100644 docs/annotated/src/app/shop/page.annotated.tsx create mode 100644 docs/annotated/src/app/wishlist/page.annotated.tsx create mode 100644 docs/annotated/src/components/home/AboutPreview.annotated.tsx create mode 100644 docs/annotated/src/components/home/CTA.annotated.tsx create mode 100644 docs/annotated/src/components/home/CategoryGrid.annotated.tsx create mode 100644 docs/annotated/src/components/home/ContactPreview.annotated.tsx create mode 100644 docs/annotated/src/components/home/FeaturedProducts.annotated.tsx create mode 100644 docs/annotated/src/components/home/Hero.annotated.tsx create mode 100644 docs/annotated/src/components/home/HowItWorks.annotated.tsx create mode 100644 docs/annotated/src/components/home/SocialFollow.annotated.tsx create mode 100644 docs/annotated/src/components/home/TrustBadges.annotated.tsx create mode 100644 docs/annotated/src/components/home/WeeklyOffers.annotated.tsx create mode 100644 docs/annotated/src/components/layout/Footer.annotated.tsx create mode 100644 docs/annotated/src/components/layout/Header.annotated.tsx create mode 100644 docs/annotated/src/components/layout/LanguageBanner.annotated.tsx create mode 100644 docs/annotated/src/components/layout/LanguageSwitcher.annotated.tsx create mode 100644 docs/annotated/src/components/layout/LocaleAttributes.annotated.tsx create mode 100644 docs/annotated/src/components/product/CustomizationSelector.annotated.tsx create mode 100644 docs/annotated/src/components/product/ProductCard.annotated.tsx create mode 100644 docs/annotated/src/components/shop/ShopFilters.annotated.tsx create mode 100644 docs/annotated/src/components/ui/AppImage.annotated.tsx create mode 100644 docs/annotated/src/components/ui/Button.annotated.tsx create mode 100644 docs/annotated/src/components/ui/Logo.annotated.tsx create mode 100644 docs/annotated/src/hooks/useTranslation.annotated.ts create mode 100644 docs/annotated/src/i18n/index.annotated.ts create mode 100644 docs/annotated/src/i18n/locales/en.annotated.ts create mode 100644 docs/annotated/src/i18n/locales/sv.annotated.ts create mode 100644 docs/annotated/src/i18n/locales/ur.annotated.ts create mode 100644 docs/annotated/src/i18n/types.annotated.ts create mode 100644 docs/annotated/src/lib/constants.annotated.ts create mode 100644 docs/annotated/src/lib/customization.annotated.ts create mode 100644 docs/annotated/src/lib/demo-orders.annotated.ts create mode 100644 docs/annotated/src/lib/images.annotated.ts create mode 100644 docs/annotated/src/lib/offers.annotated.ts create mode 100644 docs/annotated/src/lib/product-i18n.annotated.ts create mode 100644 docs/annotated/src/lib/products.annotated.ts create mode 100644 docs/annotated/src/lib/utils.annotated.ts create mode 100644 docs/annotated/src/store/auth.annotated.ts create mode 100644 docs/annotated/src/store/cart.annotated.ts create mode 100644 docs/annotated/src/store/locale.annotated.ts create mode 100644 docs/annotated/src/store/wishlist.annotated.ts create mode 100644 docs/annotated/src/types/index.annotated.ts create mode 100644 docs/generate-guide-docx.mjs create mode 100644 next-env.d.ts create mode 100644 next.config.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 public/images/site/README.txt create mode 100644 public/images/site/_originals/about.jpg create mode 100644 public/images/site/_originals/beef-boneless.jpg create mode 100644 public/images/site/_originals/beef-mince.jpg create mode 100644 public/images/site/_originals/beef-nihari-2.jpg create mode 100644 public/images/site/_originals/beef-nihari.jpg create mode 100644 public/images/site/_originals/beef-steak-2.jpg create mode 100644 public/images/site/_originals/beef-steak.jpg create mode 100644 public/images/site/_originals/category-beef.jpg create mode 100644 public/images/site/_originals/category-chicken.jpg create mode 100644 public/images/site/_originals/category-fish.jpg create mode 100644 public/images/site/_originals/category-lamb.jpg create mode 100644 public/images/site/_originals/chicken-breast copy.jpg create mode 100644 public/images/site/_originals/chicken-breast-2.jpg create mode 100644 public/images/site/_originals/chicken-breast.jpg create mode 100644 public/images/site/_originals/chicken-thighs.jpg create mode 100644 public/images/site/_originals/chicken-whole-1.jpg create mode 100644 public/images/site/_originals/chicken-whole-2.jpg create mode 100644 public/images/site/_originals/chicken-whole.jpg create mode 100644 public/images/site/_originals/chicken-wings.jpg create mode 100644 public/images/site/_originals/fish-basa.jpg create mode 100644 public/images/site/_originals/fish-prawns.jpg create mode 100644 public/images/site/_originals/fish-rohu.jpg create mode 100644 public/images/site/_originals/fish-salmon-2.jpg create mode 100644 public/images/site/_originals/fish-salmon.jpg create mode 100644 public/images/site/_originals/hero.jpg create mode 100644 public/images/site/_originals/lamb-chops.jpg create mode 100644 public/images/site/_originals/lamb-leg copy.jpg create mode 100644 public/images/site/_originals/lamb-leg.jpg create mode 100644 public/images/site/_originals/lamb-mince.jpg create mode 100644 public/images/site/_originals/lamb-shoulder-2.jpg create mode 100644 public/images/site/_originals/lamb-shoulder.jpg create mode 100644 public/images/site/_originals/weekly-offers.jpg create mode 100644 public/images/site/about.jpg create mode 100644 public/images/site/beef-boneless.jpg create mode 100644 public/images/site/beef-mince.jpg create mode 100644 public/images/site/beef-nihari-2.jpg create mode 100644 public/images/site/beef-nihari.jpg create mode 100644 public/images/site/beef-steak-2.jpg create mode 100644 public/images/site/beef-steak.jpg create mode 100644 public/images/site/category-beef.jpg create mode 100644 public/images/site/category-chicken.jpg create mode 100644 public/images/site/category-fish.jpg create mode 100644 public/images/site/category-lamb.jpg create mode 100644 public/images/site/chicken-breast copy.jpg create mode 100644 public/images/site/chicken-breast-2.jpg create mode 100644 public/images/site/chicken-breast.jpg create mode 100644 public/images/site/chicken-thighs.jpg create mode 100644 public/images/site/chicken-whole-1.jpg create mode 100644 public/images/site/chicken-whole-2.jpg create mode 100644 public/images/site/chicken-whole.jpg create mode 100644 public/images/site/chicken-wings.jpg create mode 100644 public/images/site/fish-basa.jpg create mode 100644 public/images/site/fish-prawns.jpg create mode 100644 public/images/site/fish-rohu.jpg create mode 100644 public/images/site/fish-salmon-2.jpg create mode 100644 public/images/site/fish-salmon.jpg create mode 100644 public/images/site/hero.jpg create mode 100644 public/images/site/lamb-chops.jpg create mode 100644 public/images/site/lamb-leg copy.jpg create mode 100644 public/images/site/lamb-leg.jpg create mode 100644 public/images/site/lamb-mince.jpg create mode 100644 public/images/site/lamb-shoulder-2.jpg create mode 100644 public/images/site/lamb-shoulder.jpg create mode 100644 public/images/site/logo.jpeg create mode 100644 public/images/site/weekly-offers.jpg create mode 100644 scripts/branded-beef-boneless.py create mode 100755 scripts/build.sh create mode 100755 scripts/dev.sh create mode 100644 scripts/optimize-site-images.py create mode 100755 scripts/populate-site-images.sh create mode 100644 scripts/process-all-site-images.py create mode 100755 scripts/update-fish-images.sh create mode 100644 src/app/about/page.tsx create mode 100644 src/app/account/page.tsx create mode 100644 src/app/cart/page.tsx create mode 100644 src/app/checkout/page.tsx create mode 100644 src/app/globals.css create mode 100644 src/app/home/page.tsx create mode 100644 src/app/layout.tsx create mode 100644 src/app/login/page.tsx create mode 100644 src/app/not-found.tsx create mode 100644 src/app/page.tsx create mode 100644 src/app/product/[slug]/page.tsx create mode 100644 src/app/shop/layout.tsx create mode 100644 src/app/shop/page.tsx create mode 100644 src/app/wishlist/page.tsx create mode 100644 src/application/container.ts create mode 100644 src/application/dtos/CartSummary.ts create mode 100644 src/application/dtos/LocalizedProduct.ts create mode 100644 src/application/ports/IAuthRepository.ts create mode 100644 src/application/ports/ICartRepository.ts create mode 100644 src/application/ports/IProductRepository.ts create mode 100644 src/application/ports/ITranslationService.ts create mode 100644 src/application/ports/IWishlistRepository.ts create mode 100644 src/application/use-cases/auth/Login.ts create mode 100644 src/application/use-cases/auth/Register.ts create mode 100644 src/application/use-cases/cart/AddToCart.ts create mode 100644 src/application/use-cases/cart/GetCartSummary.ts create mode 100644 src/application/use-cases/cart/RemoveFromCart.ts create mode 100644 src/application/use-cases/cart/UpdateCartQuantity.ts create mode 100644 src/application/use-cases/catalog/FilterProducts.ts create mode 100644 src/application/use-cases/catalog/GetProductBySlug.ts create mode 100644 src/application/use-cases/catalog/LocalizeProduct.ts create mode 100644 src/application/use-cases/checkout/PlaceOrder.ts create mode 100644 src/application/use-cases/wishlist/ToggleWishlist.ts create mode 100644 src/components/about/AboutIntro.tsx create mode 100644 src/components/home/CTA.tsx create mode 100644 src/components/home/CategoryGrid.tsx create mode 100644 src/components/home/ContactPreview.tsx create mode 100644 src/components/home/FeaturedProducts.tsx create mode 100644 src/components/home/Hero.tsx create mode 100644 src/components/home/HomeView.tsx create mode 100644 src/components/home/HowItWorks.tsx create mode 100644 src/components/home/SocialFollow.tsx create mode 100644 src/components/home/TrustBadges.tsx create mode 100644 src/components/home/WeeklyOffers.tsx create mode 100644 src/components/layout/Footer.tsx create mode 100644 src/components/layout/Header.tsx create mode 100644 src/components/layout/LanguageBanner.tsx create mode 100644 src/components/layout/LanguageSwitcher.tsx create mode 100644 src/components/layout/LocaleAttributes.tsx create mode 100644 src/components/product/CustomizationSelector.tsx create mode 100644 src/components/product/ProductCard.tsx create mode 100644 src/components/shop/ShopFilters.tsx create mode 100644 src/components/ui/AppImage.tsx create mode 100644 src/components/ui/Button.tsx create mode 100644 src/components/ui/Logo.tsx create mode 100644 src/domain/constants/commerce.ts create mode 100644 src/domain/entities/index.ts create mode 100644 src/domain/services/AuthDomainService.ts create mode 100644 src/domain/services/CartDomainService.ts create mode 100644 src/domain/services/CatalogDomainService.ts create mode 100644 src/domain/services/CustomizationDomainService.ts create mode 100644 src/domain/services/DeliveryFeeService.ts create mode 100644 src/domain/services/OrderDomainService.ts create mode 100644 src/hooks/useTranslation.ts create mode 100644 src/i18n/index.ts create mode 100644 src/i18n/locales/ar.ts create mode 100644 src/i18n/locales/en.ts create mode 100644 src/i18n/locales/fa.ts create mode 100644 src/i18n/locales/sv.ts create mode 100644 src/i18n/locales/tr.ts create mode 100644 src/i18n/locales/ur.ts create mode 100644 src/i18n/types.ts create mode 100644 src/infrastructure/data/demo-orders.data.ts create mode 100644 src/infrastructure/data/products.data.ts create mode 100644 src/infrastructure/i18n/I18nTranslationService.ts create mode 100644 src/infrastructure/images/catalog.ts create mode 100644 src/infrastructure/images/index.ts create mode 100644 src/infrastructure/persistence/zustand/authStore.ts create mode 100644 src/infrastructure/persistence/zustand/cartStore.ts create mode 100644 src/infrastructure/persistence/zustand/localeStore.ts create mode 100644 src/infrastructure/persistence/zustand/wishlistStore.ts create mode 100644 src/infrastructure/repositories/InMemoryProductRepository.ts create mode 100644 src/lib/constants.ts create mode 100644 src/lib/customization.ts create mode 100644 src/lib/demo-orders.ts create mode 100644 src/lib/images.ts create mode 100644 src/lib/offers.ts create mode 100644 src/lib/product-i18n.ts create mode 100644 src/lib/products.ts create mode 100644 src/lib/utils.ts create mode 100644 src/presentation/hooks/useAuth.ts create mode 100644 src/presentation/hooks/useCart.ts create mode 100644 src/presentation/hooks/useCatalog.ts create mode 100644 src/presentation/hooks/useCheckout.ts create mode 100644 src/presentation/hooks/useWishlist.ts create mode 100644 src/store/auth.ts create mode 100644 src/store/cart.ts create mode 100644 src/store/locale.ts create mode 100644 src/store/wishlist.ts create mode 100644 src/types/index.ts create mode 100644 tailwind.config.ts create mode 100644 tsconfig.json create mode 100644 tsconfig.tsbuildinfo diff --git a/README.md b/README.md new file mode 100644 index 0000000..a1c21f2 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +# Kött Gård — Premium Halal Meat Delivery + +A modern, premium e-commerce website for 100% Halal meat delivery. Built with Next.js, TypeScript, and Tailwind CSS. + +## Features + +- **Premium Design** — Green, white, and gold color palette with luxurious typography +- **Multi-language** — Swedish (default), English, and Urdu with RTL support +- **Product Customization** + - Chicken: Choose quantity in pieces (4, 8, 10, 12, 14) + - Beef & Lamb: Choose cutting style (Nihari, Karahi, Qeema, Boneless, Steak) + - Fish: Standard professional cut +- **Full E-commerce Flow** — Shop, product details, cart, secure checkout +- **User Accounts** — Registration, login, order history +- **Wishlist** — Save favorite products +- **Responsive** — Mobile-first design +- **SEO Optimized** — Meta tags, semantic HTML, Open Graph + +## Getting Started + +```bash +npm install +npm run dev +``` + +Open [http://localhost:3000](http://localhost:3000). + +### Demo Account + +- Email: `demo@kottgard.se` +- Password: `demo123` + +### Languages + +Use the language bar or globe icon to switch between: +- **Svenska** (Swedish) — default +- **English** +- **اردو** (Urdu) — RTL +- **العربية** (Arabic) — RTL +- **فارسی** (Persian) — RTL +- **Türkçe** (Turkish) + +## Tech Stack + +- **Framework:** Next.js 13 (App Router) +- **Language:** TypeScript +- **Styling:** Tailwind CSS +- **State:** Zustand (persisted cart, auth, wishlist, locale) +- **i18n:** Custom translation system with locale store +- **Icons:** Lucide React + +## Project Structure (Clean Architecture) + +``` +src/ +├── domain/ # Entities + business rules (no React/Next) +├── application/ # Use cases, ports, container.ts +├── infrastructure/ # Zustand, product data, i18n adapters +├── presentation/ # useCart, useAuth, useCatalog hooks +├── app/ # Next.js pages (thin UI) +├── components/ # React UI components +├── i18n/ # Translation dictionaries (en, sv, ur) +├── hooks/ # useTranslation +├── lib/ # Backward-compatible facades +├── store/ # Backward-compatible re-exports +└── types/ # Re-exports domain entities +``` + +See **[docs/CLEAN-ARCHITECTURE.md](docs/CLEAN-ARCHITECTURE.md)** for the full step-by-step guide. \ No newline at end of file diff --git a/docs/CLEAN-ARCHITECTURE.md b/docs/CLEAN-ARCHITECTURE.md new file mode 100644 index 0000000..05d551e --- /dev/null +++ b/docs/CLEAN-ARCHITECTURE.md @@ -0,0 +1,365 @@ +# Kött Gård — Clean Architecture Guide + +This document explains **what we changed**, **why**, and **how to work in the new structure**. + +--- + +## What is Clean Architecture? + +Clean Architecture splits code into **layers**. Inner layers hold business rules. Outer layers hold frameworks (React, Next.js, Zustand, localStorage). + +**Golden rule:** Dependencies point **inward only**. + +``` +Presentation → Application → Domain +Infrastructure → Application → Domain + +Domain imports NOTHING from outside itself. +``` + +--- + +## Before vs After + +### Before (mixed responsibilities) + +``` +page.tsx → filter products, calculate delivery fee, create orders +store/cart.ts → persistence + merge logic + totals +lib/products.ts → data + queries in one file +``` + +Problems: +- Delivery fee `total > 500 ? 0 : 49` was copy-pasted in cart **and** checkout +- Shop filtering logic lived inside the React page +- Hard to swap localStorage for a real API later + +### After (separated layers) + +| Layer | Folder | Job | +|-------|--------|-----| +| **Domain** | `src/domain/` | Business rules — cart math, delivery fee, order IDs | +| **Application** | `src/application/` | Use cases + ports (interfaces) | +| **Infrastructure** | `src/infrastructure/` | Zustand, product data file, i18n adapter | +| **Presentation** | `src/app/`, `src/components/`, `src/presentation/hooks/` | UI only — calls use cases | + +--- + +## Layer diagram + +```mermaid +flowchart TB + subgraph Presentation + P1[app/pages] + P2[components] + P3[presentation/hooks] + end + + subgraph Application + A1[use-cases] + A2[ports interfaces] + A3[container.ts] + end + + subgraph Domain + D1[entities] + D2[domain services] + end + + subgraph Infrastructure + I1[Zustand stores] + I2[InMemoryProductRepository] + I3[products.data.ts] + end + + P1 --> P3 + P3 --> A3 + A3 --> A1 + A1 --> A2 + A1 --> D2 + A2 -.implemented by.- I1 + A2 -.implemented by.- I2 + I2 --> I3 + D2 --> D1 +``` + +--- + +## Step-by-step: what we did + +### Step 1 — Domain entities (`src/domain/entities/`) + +**What:** Moved `Product`, `CartItem`, `Order`, `User`, etc. from `types/` into the domain. + +**Why:** Entities are the core vocabulary of the business. They must not depend on React or Next.js. + +**Example:** `Product` describes a meat item (price, category, slug) — not how it is rendered. + +``` +src/domain/entities/index.ts +``` + +`src/types/index.ts` now **re-exports** domain entities so old imports still work. + +--- + +### Step 2 — Domain services (`src/domain/services/`) + +**What:** Pure functions / classes for business rules. + +| Service | Rule extracted from | +|---------|---------------------| +| `CartDomainService` | `store/cart.ts` — add item, merge duplicates, totals | +| `DeliveryFeeService` | `cart/page.tsx` + `checkout/page.tsx` — 49 kr fee, free over 500 kr | +| `CustomizationDomainService` | `lib/customization.ts` — cut styles, cart line keys | +| `OrderDomainService` | `checkout/page.tsx` — order ID format, order object shape | +| `AuthDomainService` | `store/auth.ts` — demo password check, user creation | +| `CatalogDomainService` | `shop/page.tsx` — filter by category, search, sort | + +**Why:** One place per rule. Change delivery threshold once → cart and checkout both update. + +**Example — delivery fee (single source of truth):** + +```typescript +// src/domain/constants/commerce.ts +export const FREE_DELIVERY_THRESHOLD_SEK = 500; +export const STANDARD_DELIVERY_FEE_SEK = 49; + +// src/domain/services/DeliveryFeeService.ts +static calculateDeliveryFee(subtotal: number): number { + return subtotal > FREE_DELIVERY_THRESHOLD_SEK ? 0 : STANDARD_DELIVERY_FEE_SEK; +} +``` + +--- + +### Step 3 — Application ports (`src/application/ports/`) + +**What:** TypeScript **interfaces** describing what the app needs — not how it is stored. + +| Port | Contract | +|------|----------| +| `IProductRepository` | `findAll()`, `findBySlug()`, `findFeatured()` | +| `ICartRepository` | `getItems()`, `addItem()`, `clearCart()` | +| `IAuthRepository` | `login()`, `register()`, `addOrder()` | +| `IWishlistRepository` | `toggleItem()`, `isInWishlist()` | +| `ITranslationService` | `translate(key)` | + +**Why:** Today products live in a static array. Tomorrow they might come from Shopify or a database. **Only the infrastructure adapter changes** — use cases stay the same. + +--- + +### Step 4 — Use cases (`src/application/use-cases/`) + +**What:** One class per user action. Orchestrates domain + ports. + +| Use case | Replaces logic in | +|----------|-------------------| +| `FilterProductsUseCase` | Shop page filtering/sorting | +| `GetProductBySlugUseCase` | `getProductBySlug()` calls | +| `LocalizeProductUseCase` | `lib/product-i18n.ts` | +| `AddToCartUseCase` | Cart add button | +| `GetCartSummaryUseCase` | Cart totals + delivery | +| `PlaceOrderUseCase` | Checkout submit handler | +| `LoginUseCase` / `RegisterUseCase` | Login page | + +**Example — checkout flow:** + +``` +User clicks Pay + → PlaceOrderUseCase.execute() + → OrderDomainService.createOrder() [domain] + → authRepository.addOrder() [port] + → cartRepository.clearCart() [port] +``` + +File: `src/application/use-cases/checkout/PlaceOrder.ts` + +--- + +### Step 5 — Infrastructure (`src/infrastructure/`) + +**What:** Concrete implementations of ports + framework code. + +| File | Role | +|------|------| +| `data/products.data.ts` | Static 16-product catalog | +| `repositories/InMemoryProductRepository.ts` | Implements `IProductRepository` | +| `persistence/zustand/cartStore.ts` | Zustand + `ZustandCartRepository` | +| `persistence/zustand/authStore.ts` | Auth persistence + demo user | +| `persistence/zustand/wishlistStore.ts` | Wishlist persistence | +| `i18n/I18nTranslationService.ts` | Wraps existing `i18n/` dictionaries | + +**Why Zustand stays:** It is an **infrastructure detail** (browser storage). The application layer only sees `ICartRepository`. + +--- + +### Step 6 — Composition root (`src/application/container.ts`) + +**What:** One file that wires everything together. + +```typescript +export const container = new ApplicationContainer(); +// container.addToCart.execute(...) +// container.getCartSummary.execute() +// container.placeOrder.execute(...) +``` + +**Why:** Pages and hooks do not `new InMemoryProductRepository()` themselves. That would couple UI to infrastructure. The container is the **only** place that knows concrete classes. + +--- + +### Step 7 — Presentation hooks (`src/presentation/hooks/`) + +**What:** React-friendly API on top of the container. + +| Hook | Use in | +|------|--------| +| `useCart()` | Cart page, Header, product page | +| `useAuth()` | Login, account, checkout | +| `useWishlist()` | Product card, wishlist page | +| `useCheckout()` | Checkout submit | +| `useCatalogFilter()` | Shop page | + +**Example:** + +```typescript +// Old (page knew about Zustand internals + business math) +const { getTotal } = useCartStore(); +const deliveryFee = total > 500 ? 0 : 49; + +// New (page uses use case result) +const { summary } = useCart(); +const { subtotal, deliveryFee, grandTotal } = summary; +``` + +--- + +### Step 8 — Backward-compatible facades + +Old paths still work so nothing breaks silently: + +| Old import | Now points to | +|------------|---------------| +| `@/types` | `@/domain/entities` | +| `@/lib/customization` | `CustomizationDomainService` | +| `@/lib/products` | `container.productRepository` | +| `@/store/cart` | infrastructure + `useCart` hook | + +--- + +## New folder map + +``` +src/ +├── domain/ +│ ├── entities/ # Product, Order, CartItem… +│ ├── constants/ # FREE_DELIVERY_THRESHOLD_SEK +│ └── services/ # CartDomainService, DeliveryFeeService… +│ +├── application/ +│ ├── ports/ # IProductRepository, ICartRepository… +│ ├── use-cases/ # AddToCart, PlaceOrder, FilterProducts… +│ ├── dtos/ # LocalizedProduct, CartSummary +│ └── container.ts # ★ wires everything +│ +├── infrastructure/ +│ ├── data/ # products.data.ts, demo-orders.data.ts +│ ├── repositories/ # InMemoryProductRepository +│ ├── persistence/zustand/ # Stores + repository adapters +│ └── i18n/ # I18nTranslationService +│ +├── presentation/ +│ └── hooks/ # useCart, useAuth, useCatalog… +│ +├── app/ # Thin pages (UI + hooks) +├── components/ # Visual components +├── hooks/useTranslation.ts # Still used for t('key') +├── lib/ # Facades (backward compat) +├── store/ # Re-exports (backward compat) +└── types/ # Re-exports (backward compat) +``` + +--- + +## How to add a feature (cheat sheet) + +### Change delivery fee rule +1. Edit `src/domain/constants/commerce.ts` +2. Done — `GetCartSummaryUseCase` and `PlaceOrderUseCase` pick it up automatically + +### Add a new product +1. Add entry in `src/infrastructure/data/products.data.ts` +2. Add translations in `src/i18n/locales/*.ts` + +### Add a new page action (e.g. "apply coupon") +1. Add rule in `src/domain/services/` if it is business logic +2. Add port method if it needs storage +3. Create `src/application/use-cases/cart/ApplyCoupon.ts` +4. Register in `container.ts` +5. Expose via `useCart()` or new hook +6. Call from page/component + +### Replace static products with an API +1. Create `src/infrastructure/repositories/ApiProductRepository.ts` implementing `IProductRepository` +2. In `container.ts`, swap `InMemoryProductRepository` → `ApiProductRepository` +3. **No changes** to shop page, use cases, or domain + +--- + +## Dependency rules (memorize this) + +| Layer | Can import | +|-------|------------| +| Domain | Only domain | +| Application | Domain + its own ports/DTOs | +| Infrastructure | Application ports + Domain | +| Presentation | Application container + hooks + React | + +| Layer | Cannot import | +|-------|---------------| +| Domain | React, Next, Zustand, `app/`, `components/` | +| Application | Zustand, `page.tsx`, JSX | +| Use cases | Concrete repositories (only interfaces) | + +--- + +## Request flow example: Add to cart + +```mermaid +sequenceDiagram + participant Page as product/page.tsx + participant Hook as useCart() + participant UC as AddToCartUseCase + participant Repo as ZustandCartRepository + participant Domain as CartDomainService + participant Store as Zustand persist + + Page->>Hook: addItem(product, customization, label) + Hook->>UC: execute(...) + UC->>Repo: addItem(...) + Repo->>Domain: addItem(items, product, ...) + Domain-->>Repo: newItems[] + Repo->>Store: setItems(newItems) + Store-->>Page: React re-render +``` + +--- + +## Verify the project + +```bash +cd /Users/apple/Desktop/code/Kottgard +npm run build # must pass +npm run dev # test at http://localhost:3000 +``` + +Demo login: `demo@kottgard.se` / `demo123` + +--- + +## Further reading + +- Uncle Bob — Clean Architecture (concentric circles diagram) +- This project's annotated code: `docs/annotated/src/` +- Beginner folder map: `~/Desktop/Kottgard-Project-Learn/` \ No newline at end of file diff --git a/docs/Kottgard-Website-Guide.docx b/docs/Kottgard-Website-Guide.docx new file mode 100644 index 0000000000000000000000000000000000000000..52037aef16a5af8f17f78bbf88eba46962ac5197 GIT binary patch literal 11989 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')) + return `${indent}// JSX element — HTML-like tag becomes React component in browser`; + if (t.startsWith('<>') || t === '') + return `${indent}// React Fragment — group elements without extra wrapper DOM node`; + if (t.startsWith('{/*')) + return `${indent}// JSX comment — not visible in browser`; + if (t.startsWith('className=')) + return `${indent}// Tailwind CSS utility classes — styling (colors, spacing, layout)`; + if (t.startsWith('href=')) + return `${indent}// Link target URL — internal route or external https://`; + if (t.startsWith('onClick=')) + return `${indent}// Click handler — runs when user clicks (must be client component)`; + if (t.startsWith('onChange=')) + return `${indent}// Change handler — runs when input/select value changes`; + if (t.startsWith('aria-')) + return `${indent}// Accessibility attribute — screen readers and assistive tech`; + if (t.startsWith('}')) + return `${indent}// Closing brace — end of block (function, if, object, JSX)`; + if (t.startsWith(']') || t.startsWith('];')) + return `${indent}// End of array literal`; + if (t.endsWith(',') && !t.includes('//')) + return `${indent}// Property or array item — trailing comma allowed in TypeScript`; + if (relPath.includes('locales/') && t.match(/^\s{2}\w+:/)) + return `${indent}// i18n translation section — keys used by t('section.key') in components`; + if (relPath.includes('locales/') && t.includes("name:") || relPath.includes('locales/') && t.includes("title:")) + return `${indent}// Translated UI string for current language (sv / en / ur)`; + if (relPath.includes('products.ts')) + return `${indent}// Product catalog entry — demo data shown in shop and product pages`; + if (t.includes('persist(')) + return `${indent}// Zustand persist — save store to localStorage between visits`; + + return `${indent}// Line ${lineNum}: ${t.length > 60 ? t.slice(0, 57) + '...' : t || 'code'}`; +} + +function annotateFile(content, relPath) { + const lines = content.split('\n'); + const ext = relPath.endsWith('.tsx') ? 'tsx' : 'ts'; + const header = [ + `/**`, + ` * ANNOTATED COPY — every line explained`, + ` * Source: src/${relPath}`, + ` * NOT used by the app — read this to learn how the real file works`, + ` */`, + '', + ]; + + const body = lines.map((line, i) => { + const comment = explainLine(line, i + 1, relPath); + if (line.trim().startsWith('//') && comment === line) return line; + if (line.trim() === '' && comment.includes('blank')) return comment; + return `${comment}\n${line}`; + }); + + return header.join('\n') + body.join('\n') + '\n'; +} + +function walk(dir, base = '') { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + let count = 0; + for (const e of entries) { + const rel = base ? `${base}/${e.name}` : e.name; + const full = path.join(dir, e.name); + if (e.isDirectory()) { + count += walk(full, rel); + } else if (/\.(ts|tsx)$/.test(e.name)) { + const content = fs.readFileSync(full, 'utf8'); + const annotated = annotateFile(content, rel); + const outName = e.name.replace(/\.(tsx?)$/, '.annotated.$1'); + const outDir = path.join(OUT, base); + fs.mkdirSync(outDir, { recursive: true }); + fs.writeFileSync(path.join(outDir, outName), annotated); + count++; + } + } + return count; +} + +// Remove old flat annotated files (replaced by src/ mirror) +const oldFlat = path.join(__dirname, 'annotated'); +for (const f of fs.readdirSync(oldFlat)) { + if (f.endsWith('.annotated.ts') || f.endsWith('.annotated.tsx')) { + if (!f.startsWith('0')) continue; + try { fs.unlinkSync(path.join(oldFlat, f)); } catch (_) {} + } +} + +const n = walk(SRC); +console.log(`Annotated ${n} files → docs/annotated/src/`); \ No newline at end of file diff --git a/docs/annotated/README.md b/docs/annotated/README.md new file mode 100644 index 0000000..3e892f2 --- /dev/null +++ b/docs/annotated/README.md @@ -0,0 +1,63 @@ +# Annotated Source Code — Every Line Explained + +This folder contains **annotated copies** of **every** file in `src/`. Each line of the original code is followed or preceded by a comment explaining: + +- **What** the syntax means (TypeScript, React, Next.js) +- **Why** it exists in the Kött Gård project + +Production code in `src/` stays clean. Learning comments live here only. + +## Regenerate all annotations + +```bash +node docs/annotate-all.mjs +``` + +Run this after you change source files to refresh the annotated copies. + +## Folder mirror + +``` +docs/annotated/src/ ← you are here (annotated) +src/ ← real app code (no line comments) +``` + +| Annotated path | Original path | +|----------------|---------------| +| `docs/annotated/src/app/page.annotated.tsx` | `src/app/page.tsx` | +| `docs/annotated/src/lib/products.annotated.ts` | `src/lib/products.ts` | +| `docs/annotated/src/store/cart.annotated.ts` | `src/store/cart.ts` | +| … | (52 files total) | + +## File categories + +| Folder | What it contains | +|--------|------------------| +| `app/` | Pages and routes (Next.js App Router) | +| `components/` | Reusable UI (home sections, layout, product cards) | +| `lib/` | Data, images, constants, helpers | +| `store/` | Zustand global state (cart, auth, wishlist, locale) | +| `i18n/` | Translations (Swedish, English, Urdu) | +| `hooks/` | Custom React hooks | +| `types/` | TypeScript interfaces | + +## How to read + +1. Open the **original** file in `src/` in your editor. +2. Open the matching **`.annotated.ts`** or **`.annotated.tsx`** file side-by-side. +3. Read the gray `//` comment above each line, then the code. + +## Programming languages in this project + +| Language | Role | +|----------|------| +| **TypeScript** | Types catch errors before run; interfaces for Product, Cart, Order | +| **React 18** | Components + JSX UI; hooks for state and effects | +| **Next.js 13** | File-based routing, layouts, metadata, image optimization | +| **Tailwind CSS** | Utility classes for burgundy/cream/gold design | +| **Zustand** | Global stores with `persist` → localStorage | + +## Related documents + +- Word guide: `docs/Kottgard-Website-Guide.docx` +- Visual guide: `../kottgard documentation/16-website-visual-and-code-guide.md` \ No newline at end of file diff --git a/docs/annotated/src/app/about/page.annotated.tsx b/docs/annotated/src/app/about/page.annotated.tsx new file mode 100644 index 0000000..011e6e1 --- /dev/null +++ b/docs/annotated/src/app/about/page.annotated.tsx @@ -0,0 +1,249 @@ +/** + * ANNOTATED COPY — every line explained + * Source: src/app/about/page.tsx + * NOT used by the app — read this to learn how the real file works + */ +// Next.js: this file runs in the BROWSER (needs useState, onClick, localStorage, etc.) +'use client'; +// (blank line — separates logical blocks for readability) +// Next.js Link — fast client-side navigation without full page reload +import Link from 'next/link'; +// Lucide icons — lightweight SVG icon components +import { ShieldCheck, Leaf, Award, Truck, Phone, Mail, MapPin } from 'lucide-react'; +// Import project module (@/ alias = src/ folder in tsconfig) +import { useTranslation } from '@/hooks/useTranslation'; +// Import external package or local module +import { + // Property or array item — trailing comma allowed in TypeScript + SITE_ADDRESS, + // Property or array item — trailing comma allowed in TypeScript + SITE_EMAIL, + // Property or array item — trailing comma allowed in TypeScript + SITE_PHONE_DISPLAY, + // Property or array item — trailing comma allowed in TypeScript + SITE_HOURS, +// Closing brace — end of block (function, if, object, JSX) +} from '@/lib/constants'; +// (blank line — separates logical blocks for readability) +// Default export — main component/page Next.js or other files import +export default function AboutPage() { + // Custom hook — returns t() translator and current locale + const { t } = useTranslation(); + // Line 15: const telHref = `tel:${SITE_PHONE_DISPLAY.replace(/\s/g, ... + const telHref = `tel:${SITE_PHONE_DISPLAY.replace(/\s/g, '')}`; +// (blank line — separates logical blocks for readability) + // Return JSX — describes UI tree React renders to the DOM + return ( + // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +
+ // JSX element — HTML-like tag becomes React component in browser +

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

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

{t('about.subtitle')}

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

{t('about.ourStory')}

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

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

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

{t('about.storyP2')}

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

{t('about.halalTitle')}

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

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

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

{item.title}

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

{item.desc}

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

{t('about.deliveryTitle')}

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

{t('about.delivery1')}

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

{t('about.delivery2')}

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

{t('about.delivery3')}

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

{t('about.delivery4')}

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

{t('about.contactTitle')}

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

{t('about.privacyTitle')}

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

{t('about.privacyText')}

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

{t('about.termsTitle')}

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

{t('about.termsText')}

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

{t('account.title')}

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

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

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

{user.name}

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

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

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

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

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

{t('account.noOrders')}

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

{order.id}

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

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

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

{fmt(order.total)}

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

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

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

{t('cart.emptyHint')}

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

{t('cart.title')}

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

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

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

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

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

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

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

{t('cart.freeDeliveryHint')}

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

{t('checkout.noItems')}

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

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

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

{t('checkout.thankYou')}

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

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

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

{t('checkout.title')}

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

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

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

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

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

{t('checkout.payment')}

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

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

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

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

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

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

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

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

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

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

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