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

321 lines
12 KiB
Markdown

# 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)
```mermaid
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.