15 KiB
15 KiB
ManoonOils Storefront - AGENTS.md
Project Overview
ManoonOils is a premium natural cosmetics e-commerce storefront built as a headless Next.js application. It serves the Serbian market primarily (with German, French, and English localization) selling natural oils and serums for hair and skin care.
Current Status: Migrated to Next.js headless storefront with Saleor backend.
Production URL: https://manoonoils.com (Next.js storefront) Saleor API: https://api.manoonoils.com Saleor Dashboard: https://dashboard.manoonoils.com
Tech Stack
| Layer | Technology | Version |
|---|---|---|
| Framework | Next.js (App Router) | 16.1.6 |
| Language | TypeScript | 5.x |
| React | React | 19.2.3 |
| Styling | Tailwind CSS | 4.x |
| State Management | Zustand | 5.x |
| i18n | next-intl | 4.8.3 |
| API Client | Apollo Client | 4.1.6 |
| Backend | Saleor (GraphQL) | Cloud-hosted |
| Deployment | Docker + K3s (Kubernetes) | - |
| Object Storage | MinIO | - |
| Analytics | OpenPanel + Rybbit | - |
| Resend | - |
Development Environment
Setup Commands
# Install dependencies
npm install
# Start development server
npm run dev
# Server runs on http://localhost:3000
# Build for production
npm run build
# Run tests
npm test # Interactive mode
npm run test:run # Single run
npm run test:coverage # With coverage
# Run e2e tests
npm run test:e2e # Headless
npm run test:e2e:ui # With UI
# Linting
npm run lint
Environment Variables
Create .env.local with:
NEXT_PUBLIC_SALEOR_API_URL=https://api.manoonoils.com/graphql/
NEXT_PUBLIC_SITE_URL=https://manoonoils.com
NEXT_PUBLIC_OPENPANEL_CLIENT_ID=your-client-id
NEXT_PUBLIC_RYBBIT_HOST=https://rybbit.nodecrew.me
NEXT_PUBLIC_RYBBIT_SITE_ID=your-site-id
Architecture
Directory Structure
src/
├── app/ # Next.js App Router
│ ├── [locale]/ # Localized routes (sr, en, de, fr)
│ │ ├── page.tsx # Homepage
│ │ ├── products/
│ │ │ ├── page.tsx # Product listing
│ │ │ └── [slug]/ # Product detail
│ │ ├── solutions/ # Programmatic SEO pages
│ │ │ ├── page.tsx # Solutions hub
│ │ │ ├── [slug]/ # Oil-for-concern pages
│ │ │ ├── by-oil/ # Browse by oil
│ │ │ └── by-concern/ # Browse by concern
│ │ ├── about/
│ │ ├── contact/
│ │ └── checkout/
│ ├── api/ # API routes
│ ├── layout.tsx # Root layout
│ ├── sitemap.ts # Dynamic sitemap
│ └── robots.ts # Robots.txt
├── components/
│ ├── cart/ # Cart drawer
│ ├── home/ # Homepage sections
│ ├── layout/ # Header, Footer, MobileMenu
│ ├── payment/ # Payment components
│ ├── product/ # Product cards, details
│ ├── programmatic-seo/ # SEO page templates
│ ├── providers/ # Context providers
│ ├── seo/ # Schema markup components
│ ├── solutions/ # Breadcrumb, etc.
│ └── ui/ # Reusable UI (Drawer, Marquee)
├── lib/
│ ├── analytics/ # Analytics tracking
│ ├── config/ # Configuration (shipping, payments)
│ ├── i18n/ # Locale config, metadata
│ ├── programmatic-seo/ # SEO data loading, types
│ ├── saleor/ # GraphQL queries, mutations
│ ├── seo/ # Keywords, schema
│ └── services/ # Business logic services
├── stores/ # Zustand stores
├── types/ # TypeScript types
└── i18n/ # Translation messages
Supported Locales
| Locale | Code | Saleor Mapping | Flag |
|---|---|---|---|
| Serbian | sr |
SR | 🇷🇸 |
| English | en |
EN | 🇬🇧 |
| German | de |
EN | 🇩🇪 |
| French | fr |
EN | 🇫🇷 |
Default Locale: Serbian (sr)
URL Structure:
- Serbian:
https://manoonoils.com/(root) or/sr/ - English:
https://manoonoils.com/en/ - German:
https://manoonoils.com/de/ - French:
https://manoonoils.com/fr/
Existing Features
1. Homepage (/)
- Hero video section
- Problem/solution narrative
- Product showcase
- How it works
- Before/after gallery
- Testimonials
- Newsletter signup
- Trust badges
- Exit intent popup (email capture)
- Ticker bar with promotions
2. Product Listing (/products)
- Grid layout with product cards
- Filtering and sorting
- Localization support
- SEO-optimized with keywords
3. Product Detail (/products/[slug])
- Product images (from Saleor/MinIO)
- Pricing and variants
- Add to cart functionality
- Product benefits display
- Bundle selector (2x, 3x packs)
- Related products
- Product reviews
- SEO schema markup (ProductSchema)
4. Cart & Checkout
- Cart Drawer: Slide-out cart with quantity controls
- Checkout Page:
- Shipping address form
- Shipping method selector
- Payment method (Cash on Delivery - COD)
- Order summary
- Analytics tracking
- Free Shipping: Orders over 10,000 RSD
5. Programmatic SEO (/solutions)
- Solutions Hub: Entry point to all solution pages
- Oil-for-Concern Pages: 40 pages (10 pairs × 4 locales)
- Example:
/sr/solutions/arganovo-ulje-za-bore - Example:
/en/solutions/argan-oil-for-wrinkles
- Example:
- Browse by Oil:
/solutions/by-oil - Browse by Concern:
/solutions/by-concern - Content: JSON-driven with localized slugs, titles, descriptions, FAQs
- Schema: FAQSchema for rich snippets
6. Static Pages
- About: Brand story, mission
- Contact: Contact form
7. Internationalization
- Full i18n with next-intl
- 4 language translations
- SEO metadata per locale
- Hreflang tags for all pages
- Locale-aware routing
8. SEO
- Dynamic sitemap.xml (48+ URLs)
- Canonical URLs with locale prefix
- OpenGraph tags
- Twitter Card tags
- Product schema markup
- Organization schema
- FAQ schema (programmatic pages)
- Breadcrumb schema
- Keywords strategy per page type
9. Analytics & Tracking
- OpenPanel: User behavior analytics
- Rybbit: Session replay and tracking
- Client-side script injection
- Proxy configuration for privacy
- Mautic: Email marketing tracking
- Custom Analytics:
- Add to cart events
- Checkout funnel
- Cart views
- Remove from cart
10. Email
- Resend integration for transactional emails
- Email capture popup on exit intent
Data Architecture
Programmatic SEO Content
data/
├── taxonomy/
│ ├── oils.json # 5 oils with metadata
│ └── concerns.json # 9 skin concerns
└── content/
└── oil-for-concern/ # 10 content files
├── argan-oil-wrinkles.json
├── argan-oil-dry-skin.json
├── argan-oil-under-eye-bags.json
├── jojoba-oil-acne.json
├── jojoba-oil-oily-skin.json
├── rosehip-oil-wrinkles.json
├── rosehip-oil-dark-spots.json
├── rosehip-oil-acne-scars.json
├── sea-buckthorn-oil-hyperpigmentation.json
└── sweet-almond-oil-sensitive-skin.json
Content JSON Structure
Each file contains localized content:
pageTitle(per locale)metaTitle/metaDescription(per locale)oilName/concernName(per locale)whyThisWorks(per locale)keyBenefits,howToApply(per locale arrays)faqs(localized Q&A)localizedSlugs(sr, en, de, fr)
API Routes
| Route | Purpose |
|---|---|
/api/analytics/track-order |
Order completion tracking |
/api/email-capture |
Email subscription endpoint |
/api/geoip |
GeoIP detection for localization |
/api/rybbit/track |
Rybbit analytics proxy |
E-commerce Backend (Saleor)
Current Integration
- Products: Fetched via GraphQL
- Variants: Size/options support
- Checkout: Saleor checkout API
- Payments: Cash on Delivery (COD) only
- Orders: Tracked in Saleor
Shipping Configuration
// src/lib/config/shipping.ts
FREE_SHIPPING_THRESHOLD_RSD = 10000; // 10,000 RSD
DEFAULT_SHIPPING_COST_RSD = 500; // 500 RSD
Payment Methods
- Cash on Delivery (COD) - Available
- Credit Card - Coming soon
- Bank Transfer - Coming later
Infrastructure
Deployment Stack
- Container: Docker (node:20-slim)
- Orchestration: K3s (Kubernetes)
- Ingress: Traefik
- Object Storage: MinIO
- Databases:
- PostgreSQL (Saleor)
- Redis (cache + task queue)
Domains & Routing
manoonoils.com → Next.js storefront (production)
api.manoonoils.com → Saleor GraphQL API
dashboard.manoonoils.com → Saleor Admin Dashboard
minio-api.nodecrew.me → MinIO Object Storage
Image Domains (Next.js)
manoonoils.comminio-api.nodecrew.meapi.manoonoils.com**.saleor.cloudimages.unsplash.com
Build Output
output: 'standalone'(Next.js standalone mode)- Static files served from
.next/static/ - Port: 3000
Planned Features (Roadmap)
Phase 1: Essential (Current Focus)
- Saleor core products integration
- Programmatic SEO (40 pages)
- Multi-language support (4 locales)
- Cart and checkout flow
- Analytics tracking
- Product reviews (Judge.me or custom)
- Upsells & cross-sells
- AJAX add to cart (no page reload)
- Mautic abandoned cart recovery
Phase 2: Growth (1-3 months)
- Email marketing campaigns (Mautic)
- Wishlist/favorites
- Product bundles
- Recently viewed products
- Back in stock notifications
Phase 3: Advanced (6+ months)
- Loyalty/rewards program
- AI/ML product recommendations
- Subscription/recurring products
- Product comparison
- Quick view modal
- Dynamic pricing/volume discounts
Code Conventions
File Naming
- Components:
PascalCase.tsx - Utilities:
camelCase.ts - API routes:
route.ts - Pages:
page.tsx - Layouts:
layout.tsx
Styling
- Tailwind CSS utility classes
- Custom colors:
[#1A1A1A],[#FAF9F7],[#666666], etc. - Responsive breakpoints:
sm:,md:,lg:
State Management
- Zustand for global state (cart, checkout)
- React state for local component state
API Calls
- Apollo Client for GraphQL (Saleor)
- REST for custom API routes
Error Handling
- Error boundaries for React components
- Try/catch for async operations
- Console logging for build-time errors
Testing
Unit Tests
- Framework: Vitest
- Location:
src/__tests__/unit/ - Run:
npm run test:run
Integration Tests
- Location:
src/__tests__/integration/ - API route testing with MSW
E2E Tests
- Framework: Playwright
- Location: Root
e2e/directory - Run:
npm run test:e2e
SEO Checklist
Implemented
- Dynamic sitemap.xml (auto-generated)
- Robots.txt
- Canonical URLs
- Hreflang tags (all 4 locales)
- OpenGraph meta tags
- Twitter Card meta tags
- Product schema (JSON-LD)
- Organization schema
- FAQ schema (programmatic pages)
- Breadcrumb schema
- Keywords per page type
- Localized metadata
Pending
- Blog/Content marketing pages
- Review schema (when reviews added)
- Article schema (for blog posts)
Analytics Events
Tracked Events
| Event | Trigger | Data |
|---|---|---|
add_to_cart |
Click "Add to Cart" | Product ID, name, price, quantity |
remove_from_cart |
Click remove button | Product ID, name, quantity |
view_cart |
Open cart drawer | Total, item count, currency |
begin_checkout |
Start checkout | Cart contents |
purchase |
Complete order | Order ID, total, items |
page_view |
Page load | URL, referrer |
Important Notes
Cache Strategy
- Static pages:
public, max-age=3600, stale-while-revalidate=86400 - Checkout/cart pages: No cache
- API routes: No cache
- Middleware applies cache headers to all localized pages except checkout/cart/api
Image Optimization
- Next.js Image component with automatic optimization
- Avif and WebP formats supported
- Responsive sizing with deviceSizes and imageSizes
Security
- Environment variables for sensitive config
- No secrets in client-side code
- CORS configured for API routes
Performance
output: 'standalone'for Docker optimization- Image optimization via Next.js
- Code splitting by route
- Lazy loading for below-fold content
Common Tasks
Adding a New Programmatic SEO Page
- Add content JSON to
data/content/oil-for-concern/ - Run
node scripts/validate-taxonomy.js - Run
node scripts/generate-urls.js - Build and verify:
npm run build
Adding a New Locale
- Add locale to
SUPPORTED_LOCALESinsrc/lib/i18n/locales.ts - Add translations to
src/i18n/messages/[locale].json - Add SEO keywords in
src/lib/seo/keywords/locales/[locale].ts - Add page metadata in
src/lib/i18n/pageMetadata.ts - Update sitemap logic if needed
Adding a New Product
- Add product in Saleor Dashboard
- Upload images to MinIO
- Set pricing and variants
- Build will auto-generate product pages
Updating Shipping Threshold
- Edit
src/lib/config/shipping.ts - Change
FREE_SHIPPING_THRESHOLD_RSD - Rebuild and redeploy
Troubleshooting
Build Issues
- Check
NEXT_PUBLIC_SALEOR_API_URLis set - Verify MinIO is accessible
- Run
npm run lintbefore building
Locale Issues
- Check locale cookie (
NEXT_LOCALE) - Verify messages JSON files exist
- Check
SUPPORTED_LOCALESarray
Cart Issues
- Verify Saleor checkout API is accessible
- Check
saleorCheckoutStore.tsfor errors - Clear browser localStorage if needed
Image Issues
- Check MinIO bucket permissions
- Verify image domains in
next.config.ts - Check image URLs are HTTPS
Contact & Resources
- Repository: Private Git repository
- Saleor Dashboard: https://dashboard.manoonoils.com
- MinIO Console: https://minio-api.nodecrew.me
- Analytics: OpenPanel + Rybbit
- Email Service: Resend
Last updated: April 2026 Maintained by: ManoonOils Development Team