advdoors/docs/PLAN.md
Снесарев Максим a240d523e1 init
2026-04-01 22:34:50 +03:00

12 KiB

ADVdoors.ru Modern Site Rebuild

Rebuild advdoors.ru as a modern, self-hosted product catalog with cart/order functionality and an intuitive admin panel, using a Turborepo + pnpm monorepo with Next.js + Payload CMS + PostgreSQL + MinIO, deployed with Docker Compose. Includes a scraper app for migrating data from the old site.

Current State

The existing advdoors.ru is a dated product catalog for Finnish doors (KASKI, SWEDOOR/JELD-WEN, ALAVUS, Abloy). It has ~200-500 products across categories (exterior doors, interior doors, accessories), with product pages containing images, prices, discounts, technical specs, and availability. The repo is a fresh start.

Stack

Core: Turborepo + pnpm monorepo

The project uses a Turborepo monorepo managed by pnpm workspaces. This keeps the main web app, the migration scraper, and shared packages in one repo with fast, cached builds.

  • Turborepo -- orchestrates builds, dev, lint, typecheck across apps/packages with caching
  • pnpm -- fast, disk-efficient package manager with strict dependency isolation

Apps and Packages

  • apps/web -- Next.js 15 + Payload CMS 3 (storefront + admin in one app)
  • apps/scraper -- Node.js CLI tool to crawl advdoors.ru and import data into the new site
  • packages/shared -- Shared TypeScript types, constants (brands, availability statuses, etc.)
  • packages/tsconfig -- Shared TypeScript configurations
  • packages/eslint-config -- Shared ESLint configurations

Web App: Next.js 15 + Payload CMS 3

Payload CMS v3 embeds directly inside a Next.js application -- one codebase, one deployment:

  • Payload CMS 3 provides the admin panel (/admin), database ORM, authentication, media management, and API layer -- all as a Next.js plugin, not a separate service
  • Next.js 15 (App Router) provides SSR/SSG for SEO, React Server Components for performance, and the storefront UI
  • PostgreSQL 16 as the database (Payload's Drizzle adapter)
  • MinIO as S3-compatible object storage for product images and media from day one
  • Tailwind CSS v4 for styling the storefront
  • TypeScript throughout

Scraper App

A standalone Node.js CLI tool in apps/scraper for one-time data migration:

  • Crawls advdoors.ru catalog pages, following pagination
  • Extracts product data: name, article number, price, discount, images, specs, category, availability
  • Downloads product images and uploads them to MinIO
  • Creates products/categories in Payload CMS via its REST or Local API
  • Imports shared types from packages/shared to ensure data consistency
  • Uses Cheerio for HTML parsing, got/undici for HTTP

Why this stack over alternatives

  • Strapi + Next.js -- Two separate apps to deploy/maintain; Payload v3 integrates into Next.js directly
  • Medusa.js -- Full e-commerce engine -- overkill when no payment processing is needed
  • WordPress + WooCommerce -- Not modern, poor DX, PHP stack
  • Directus + Nuxt -- Good option, but two apps; Payload's admin UI is more polished for this use case

Deployment: Docker Compose (self-hosted)

graph LR
    subgraph docker [Docker Compose]
        Nginx["Nginx (reverse proxy + SSL)"]
        App["Next.js + Payload CMS"]
        DB["PostgreSQL 16"]
        MinIO["MinIO (S3 storage)"]
    end
    Browser --> Nginx
    Nginx --> App
    Nginx -->|"static media"| MinIO
    App --> DB
    App -->|"upload/fetch media"| MinIO
  • Nginx -- reverse proxy, SSL termination (Let's Encrypt), static asset caching, proxies /media to MinIO
  • App -- single Next.js container serving both storefront and /admin panel
  • PostgreSQL 16 -- product data, orders, users, media metadata
  • MinIO -- S3-compatible object storage for all product images and media uploads; Payload's S3 storage adapter connects directly to it; Nginx serves media publicly with caching headers

Data Model (Payload Collections)

Products

  • name (text, localized if needed later)
  • slug (auto-generated from name)
  • sku / articleNumber (text, e.g., "73146")
  • category (relationship to Categories)
  • brand (select: KASKI, ALAVUS, SWEDOOR/JELD-WEN, etc.)
  • images (array of uploads, with gallery support)
  • price (number, rubles)
  • discountPrice (number, optional)
  • availability (select: in-stock / made-to-order / coming-soon)
  • shortDescription (text)
  • technicalSpecs (rich text -- for detailed specs like on the current site)
  • options (array of {name, priceModifier, description} -- for paid customizations)
  • relatedProducts (relationship, self-referencing)
  • seoMeta (meta title, description, OG image)

Categories

  • name (text)
  • slug (auto)
  • parent (self-relationship for hierarchy: "Exterior Doors" > "With Glass")
  • description (rich text)
  • image (upload)

Orders

  • orderNumber (auto-incrementing)
  • items (array of {product, quantity, priceAtOrder})
  • customer (group: name, phone, email, comment)
  • status (select: new / in-progress / completed / cancelled)
  • createdAt (auto)
  • Triggers email notification to admin on new order

Pages (for static content)

  • title, slug, content (rich text), seoMeta
  • Used for: About, Delivery, Installation, Warranty, Contacts

SiteSettings (global)

  • phone, whatsapp, telegram, email
  • address
  • workingHours
  • footerText
  • socialLinks

Media (Payload uploads with S3/MinIO)

  • Payload's @payloadcms/storage-s3 adapter stores all uploads in MinIO
  • Auto image resizing/optimization via Sharp
  • WebP generation for thumbnails and display sizes
  • MinIO bucket: advdoors-media, public-read policy for product images

Storefront Pages

Public pages (Next.js App Router)

  • / -- Hero section, featured products, brand highlights, "why choose Finnish doors" section
  • /catalog -- All products with sidebar filters (category, brand, price range, availability), search, pagination
  • /catalog/[categorySlug] -- Category-filtered view
  • /product/[slug] -- Product detail: image gallery (lightbox), specs, price, options, "add to cart", related products
  • /cart -- Cart contents, quantity adjustment, order form (name, phone, email, comment), submit order
  • /[pageSlug] -- Dynamic content pages (about, delivery, installation, warranty, contacts)

Admin panel (Payload CMS, /admin)

Out-of-the-box from Payload:

  • Dashboard with order count, recent orders
  • Product CRUD with image upload, drag-and-drop reordering
  • Category management with tree view
  • Order management with status updates
  • Page content editor (rich text with embedded images)
  • Site settings editor
  • User management (admin accounts)

The admin UI is modern, responsive, and intuitive enough for a non-technical user -- it resembles familiar CMS interfaces.


Key Features

For customers

  • Fast, responsive, mobile-first design
  • Product search (full-text via PostgreSQL)
  • Category filtering + price range + brand filters
  • Image galleries with zoom
  • Shopping cart (persisted in localStorage, synced on order submit)
  • Order form with phone/email (no payment -- order goes to admin)
  • WhatsApp / Telegram click-to-chat buttons
  • SEO: server-rendered pages, meta tags, structured data (JSON-LD Product schema), sitemap.xml

For the admin (father)

  • Simple login at /admin
  • Add/edit products with drag-and-drop image upload
  • Set prices and discounts
  • Manage categories
  • View and manage incoming orders (with email notifications)
  • Edit content pages (about, delivery, etc.)
  • Update contact info and site settings

Technical

  • Turborepo cached builds (pnpm turbo build -- only rebuilds what changed)
  • Docker Compose one-command deployment (docker compose up -d)
  • Automatic SSL via Let's Encrypt (Nginx + certbot)
  • MinIO for all media storage (S3-compatible, self-hosted, with web console at :9001)
  • Database backups (pg_dump cron job in Docker)
  • Image optimization (Sharp, WebP auto-conversion)
  • Rate limiting on order submission
  • CSRF protection

Project Structure (Turborepo + pnpm)

advdoors/
  apps/
    web/                        # Next.js 15 + Payload CMS 3
      src/
        app/                    # Next.js App Router
          (frontend)/           # Route group for public pages
            page.tsx            # Home
            catalog/
            product/
            cart/
            [slug]/             # Dynamic content pages
          (payload)/            # Payload admin routes (auto-generated)
        collections/            # Payload collection definitions
          Products.ts
          Categories.ts
          Orders.ts
          Pages.ts
          Media.ts
        globals/                # Payload globals
          SiteSettings.ts
        components/             # React components (storefront UI)
        lib/                    # Utilities, cart logic, API helpers
        payload.config.ts       # Payload CMS configuration
      public/                   # Static assets (favicon, robots.txt)
      Dockerfile
      next.config.ts
      tailwind.config.ts
      package.json
    scraper/                    # Migration scraper CLI
      src/
        index.ts                # Entry point
        crawl.ts                # Page crawler (pagination, link following)
        extract.ts              # HTML parser (Cheerio) for product data
        import.ts               # Payload API client for creating records
        download-media.ts       # Image downloader + MinIO uploader
      package.json
  packages/
    shared/                     # Shared types, constants, brand lists
      src/
        types.ts                # Product, Category, Order types
        constants.ts            # Brands, availability statuses
      package.json
    tsconfig/                   # Shared tsconfig bases
      base.json
      nextjs.json
      node.json
      package.json
    eslint-config/              # Shared ESLint configs
      base.js
      next.js
      package.json
  docker/
    docker-compose.yml          # PostgreSQL + MinIO + App + Nginx
    docker-compose.dev.yml      # Dev overrides (hot reload, exposed ports)
    nginx.conf
    .env.example
  turbo.json                    # Turborepo pipeline config
  pnpm-workspace.yaml           # pnpm workspace definition
  package.json                  # Root package.json (scripts: dev, build, lint)
  .gitignore

Implementation Phases

Phase 1: Foundation (monorepo + infrastructure)

  • Initialize Turborepo + pnpm workspace with apps/web, apps/scraper, packages/*
  • Scaffold Next.js 15 + Payload CMS 3 in apps/web
  • Define data model: collections (Products, Categories, Orders, Pages, Media) and globals (SiteSettings)
  • Docker Compose: PostgreSQL 16 + MinIO + App + Nginx
  • Configure Payload S3 storage adapter pointing to MinIO
  • Shared types in packages/shared
  • Basic admin panel working, can create products and upload images to MinIO

Phase 2: Storefront

  • Home page design and implementation
  • Catalog page with sidebar filters (category, brand, price range) and pagination
  • Product detail page with image gallery, specs, pricing, options
  • Content pages (about, delivery, installation, warranty, contacts)
  • Responsive mobile-first design with Tailwind CSS v4

Phase 3: Cart and Orders

  • Shopping cart (client-side state + UI)
  • Order form and submission
  • Order management in admin panel
  • Email notifications on new orders

Phase 4: Scraper / Data Migration

  • Build scraper in apps/scraper using Cheerio + undici
  • Crawl advdoors.ru: all catalog pages, follow pagination, extract category structure
  • Extract per-product: name, SKU, prices, images, specs, availability, options
  • Download images and upload to MinIO
  • Import all data into Payload CMS via Local API
  • Validate imported data against the live site

Phase 5: Polish and Deploy

  • SEO (meta tags, structured data, sitemap)
  • Image optimization pipeline
  • Performance optimization (caching, ISR)
  • Nginx config with SSL
  • Database backup strategy
  • Production deployment

Open Questions / Future Considerations

  • Multi-language: Currently Russian only. Payload supports localization if needed later.
  • Analytics: Yandex.Metrika integration (simple script tag).
  • CDN: Not needed initially for a Russian-focused site, but can add later.
  • MinIO replication: Single-node MinIO is fine to start; can add erasure coding or replication later.
  • CI/CD: Turborepo's remote caching could be added with a self-hosted cache server if build times grow.