# Backyard Tours & Events — Project Guide

## Overview

This is a **headless WordPress + Next.js** project.

- **WordPress** (this repo root) is the CMS and content authoring surface. Editors build pages using **ACF Pro** flexible content fields — each layout in the flexible field corresponds to one section component on the frontend.
- **Next.js** (`frontend/`) is the public-facing site. It fetches page data from the WordPress REST API and renders dynamic pages by mapping each ACF flexible-content layout to a matching React section component.
- **Future i18n:** WPML will be added on the WordPress side to manage Arabic/English content. Until then the site ships Arabic-only.

```
┌──────────────────────────────┐         ┌─────────────────────────────────┐
│  WordPress (headless CMS)    │  REST   │  Next.js (frontend/)            │
│                              │  ───►   │                                 │
│  Page → ACF Flexible Content │         │  fetch /wp-json/...             │
│    ├─ Hero Section           │         │    → map layout name → <Hero/>  │
│    ├─ Services Grid          │         │    → map layout name → <Grid/>  │
│    └─ Gallery                │         │    → map layout name → <Gallery/>│
└──────────────────────────────┘         └─────────────────────────────────┘
```

## Architecture

### WordPress side (this directory)

- WordPress core + `wp-content/` live at the repo root. Managed by [WordPress Studio](https://developer.wordpress.com/studio/) — see `STUDIO.md` for the `studio` CLI workflow.
- All `wp` commands must be prefixed with `studio` (e.g. `studio wp plugin install …`).
- **ACF Pro** is the page builder. Pages use a single **Flexible Content** field named `sections`. Each available layout in that field represents a section component (Hero, ServicesGrid, Gallery, CTA, etc.).
- The REST API exposes ACF data via the official ACF-to-REST integration. Each page response contains an `acf.sections` array of layout objects in author-chosen order.
- We will **not** ship a WordPress theme that renders front-end HTML — WordPress is API-only. A minimal block theme can still exist for admin parity but should render nothing useful on the public side (or redirect to the Next.js origin).
- Authentication for previews/draft fetching: application passwords (or JWT plugin if needed).

### Next.js side (`frontend/`)

- App Router, single Arabic locale, `lang="ar" dir="rtl"` set in `src/app/layout.tsx`.
- Reusable chrome lives in `src/components/`:
  - `SiteHeader`, `MobileMenu`, `SiteFooter`, `WhatsAppFab` — render on every route via the root layout.
  - `ClientInit` — handles sticky header, mobile-menu toggling, and active-nav highlighting on route change.
  - `PageBoot` — loads Swiper, GLightbox, and Lucide from npm and exposes them on `window`; used by legacy lift-and-shift pages until they are replaced by ACF-driven dynamic pages.
  - `nav-links.ts` — single source of truth for nav items, WhatsApp URL, phone, email. **Will be replaced by data fetched from WordPress** (menus API or a "Site Settings" options page in ACF).
- Static `/about`, `/contact`, `/events`, etc. routes currently live in `src/app/<route>/page.tsx` as a lift-and-shift of the original HTML. They will be **removed and replaced by a single dynamic route** that fetches the page from WordPress (see "Migration plan" below).

## ACF Flexible Content → React component contract

Each ACF flexible-content layout maps 1:1 to a React section component. The contract is:

| WordPress | Frontend |
|---|---|
| ACF layout name (machine name, snake_case) | `frontend/src/sections/<PascalCase>.tsx` |
| ACF sub-fields (each layout's schema) | Component props (typed) |
| Field order in admin | Render order in `<PageRenderer/>` |
| Field group label | Editorial label only — not used in code |

### Naming convention

- ACF layout machine name: `hero`, `services_grid`, `gallery_coverflow`, `cta_strip`
- Component file: `frontend/src/sections/Hero.tsx`, `ServicesGrid.tsx`, `GalleryCoverflow.tsx`, `CtaStrip.tsx`
- Component default export: `Hero`, `ServicesGrid`, etc.

### The section registry

A single registry at `frontend/src/sections/registry.ts` maps layout names → components. The renderer iterates `acf.sections` and looks up each entry. Unknown layouts must fall back to a `<SectionFallback>` that logs a warning (in dev) but never crashes the page.

### Strict typing

Each section gets a Zod schema in `frontend/src/sections/schemas.ts`. The page response is validated at fetch time so:

- Unknown layouts are caught immediately.
- ACF nullables (`false` when empty) are normalized.
- Components receive a clean, typed props object.

## Migration plan

The current pages under `frontend/src/app/<route>/page.tsx` are **lift-and-shift** from the source HTML in `/html`. They will be retired progressively:

1. **Phase A — Component extraction (in progress).** Identify each visual section in the existing HTML, build it as a typed React component under `frontend/src/sections/`. No WordPress involvement yet — components accept props.
2. **Phase B — ACF schema.** For each component, design the matching ACF flexible-content layout (sub-fields with sensible defaults, repeaters where needed, image fields returning IDs we resolve to full URLs).
3. **Phase C — Dynamic route.** Add `frontend/src/app/[[...slug]]/page.tsx` that:
   - Resolves the WordPress page by slug (`pages?slug=...`).
   - Validates `acf.sections` with the schema registry.
   - Renders each layout via the section registry.
4. **Phase D — Remove lift-and-shift pages.** Once a route is fully ACF-backed, delete the static `page.tsx` in `src/app/<route>/` and let the catch-all serve it.
5. **Phase E — Menus, site settings, footer.** Replace `nav-links.ts` with data from WordPress (REST menus endpoint or an ACF Options page).
6. **Phase F — WPML.** Add Arabic/English via WPML. Routes become `/ar/...` and `/en/...` again, with a locale-aware fetcher.

## REST API surface (target)

- `GET /wp-json/wp/v2/pages?slug=<slug>` — page lookup. Response includes `acf.sections`.
- `GET /wp-json/wp/v2/pages/<id>?_embed` — full page with featured media.
- `GET /wp-json/wp/v2/menus/v1/menus/<location>` — primary navigation (via WP REST Menus or similar).
- `GET /wp-json/acf/v3/options/options` — global site settings (WhatsApp URL, phone, social links). To be defined.
- `GET /wp-json/wp/v2/media/<id>` — used to resolve image IDs returned by ACF.

All requests are server-side from Next.js (Server Components or route handlers). We cache aggressively with `revalidate` and `tag`-based invalidation; a WordPress save hook will purge tags via a small webhook endpoint in Next.

## Local development

### WordPress

- Site is managed by Studio. See `STUDIO.md` for the workflow.
- Required plugins (install via `studio wp plugin install …`):
  - `advanced-custom-fields-pro` (paid — license needed, install manually or via ACF's official download)
  - A REST CORS plugin or a small mu-plugin that allows requests from the Next dev origin
  - `wp-rest-cache` (optional, for performance once content stabilises)
  - WPML (later, when adding English)

### Next.js

```bash
cd frontend
npm install
npm run dev
```

The dev server expects:

```
NEXT_PUBLIC_WP_URL=http://<studio-site-host>:<port>
WP_REVALIDATE_SECRET=<random string for webhook>
```

Put these in `frontend/.env.local`. Get the Studio URL with `studio site status`.

### Lift-and-shift converter (legacy)

`frontend/scripts/convert-html.mjs` regenerates the lift-and-shift pages from `/html/*.html` into `src/app/*/page.tsx`. It is **not** part of the long-term build — it exists so we can keep the original HTML pages working visually while the ACF-driven migration proceeds. Re-run with:

```bash
cd frontend && node scripts/convert-html.mjs
```

It strips `<header>`, `.mobile-menu`, `<footer>`, and `.fab-wa` (which live as React components in the layout) and injects only the unique middle content.

## Rules of thumb

- **No content in code.** Anything an editor might want to change — copy, images, links, ordering — belongs in ACF, not in a React file. Components are dumb renderers; ACF is the source of truth.
- **One layout per section, one component per layout.** Don't create god-components that branch on a "variant" sub-field unless variants are truly minor cosmetic shifts; if there are meaningfully different layouts, ship them as separate ACF layouts and separate components.
- **Validate at the boundary.** Always parse the ACF response with the section's Zod schema before passing to the component. Don't pass raw `any` props through.
- **Images:** ACF should return image IDs; resolve to full URLs (with srcset where possible) at fetch time. Use `next/image` with the WordPress origin whitelisted in `next.config.ts` → `images.remotePatterns`.
- **No client-side WordPress fetching** for first-paint content. Server Components only. Client fetching is fine for things like form submissions or interactive filters.
- **Keep WordPress headless.** Never add a public WordPress theme that competes with the Next.js front-end. Editors only see the admin.
- **Single language now.** All copy is Arabic. Do not add language switchers or English fallbacks until WPML is wired up — that's a deliberate, separate phase.

## Repository layout

```
backyard/
├── CLAUDE.md                  ← this file
├── STUDIO.md                  ← WordPress Studio CLI workflow
├── AGENTS.md                  ← agent guidance pointer
├── wp-admin/, wp-includes/    ← WordPress core (do not edit)
├── wp-content/
│   ├── plugins/               ← ACF Pro, REST plugins, custom mu-plugins
│   ├── themes/                ← minimal headless theme (admin-only)
│   ├── mu-plugins/sqlite-*    ← Studio SQLite drop-in (do not delete)
│   └── db.php                 ← SQLite drop-in (do not delete)
├── html/                      ← original static HTML (source of truth for visuals during migration)
└── frontend/                  ← Next.js app
    ├── src/
    │   ├── app/
    │   │   ├── page.tsx                  ← handcrafted home (idiomatic React)
    │   │   ├── [...slug]/page.tsx        ← catch-all for ACF-driven pages
    │   │   ├── preview/[layout]/page.tsx ← per-layout dev preview
    │   │   └── api/revalidate/route.ts   ← on-demand cache revalidation
    │   ├── components/        ← reusable chrome (header, footer, etc.)
    │   ├── sections/          ← one component per ACF layout (registered via _load.ts)
    │   ├── lib/wp/            ← REST client, image normalizer, page fetcher, seed reader
    │   └── styles/            ← global + section CSS
    └── public/                ← assets copied from /html/assets and /html/uploads
```

### WordPress mu-plugins

| File | Role |
|---|---|
| `wp-content/mu-plugins/backyard-sections.php` | Loader for the section system |
| `wp-content/mu-plugins/backyard-sections/backyard-sections.php` | Registers the `Page Builder` ACF flexible-content field on `page` post type and loads each layout file |
| `wp-content/mu-plugins/backyard-sections/seed-content.json` | Single source of truth — content + composition + per-layout defaults |
| `wp-content/mu-plugins/backyard-sections/sections/*.php` | One file per layout, registers the ACF sub-fields with defaults from JSON |
| `wp-content/mu-plugins/backyard-sections/seed-pages.php` | WP-CLI command `wp backyard seed-pages` |
| `wp-content/mu-plugins/backyard-revalidate.php` | On `save_post`/`before_delete_post` pings Next.js `/api/revalidate` |

## Where to look first when…

- Editing a section's HTML/CSS: `frontend/src/sections/<Section>.tsx` (target) or `html/<page>.html` (legacy).
- Adding a new section: create the ACF layout in WordPress admin, then create the matching component in `frontend/src/sections/`, then register it in `registry.ts` and add its Zod schema in `schemas.ts`.
- Changing navigation: today `frontend/src/components/nav-links.ts`. Later: WordPress Menus or an ACF Options page.
- Adding a plugin: `studio wp plugin install <slug> --activate`.
- Debugging WordPress: enable debug logging with `studio site set --debug-log --debug-display`, tail `wp-content/debug.log`.
- Debugging Next.js: `npm run dev` in `frontend/`, watch the terminal and the browser overlay.
