# Profen Engineering — React SPA

A React single-page application that recreates [profeneng.com](https://profeneng.com) (WordPress + Elementor + Rakar theme) as a fast, portable frontend.

## Architecture

```
src/
├── main.jsx                  Entry — imports CSS (variables → kit → theme → layout → pages → services → dynamic), mounts React
├── App.jsx                   Router (BrowserRouter, Suspense, ErrorBoundary, lazy-loaded pages, 404 route)
├── config.js                 Central config: WhatsApp phone, brand colors, PAGE_IDS, SPA_ROUTES, POST_ID_TO_SLUG, SERVICE_SIDEBAR_ITEMS, MENU_ITEM_IDS
├── components/
│   ├── Layout.jsx            Shared layout (Header + Outlet + Footer + WhatsAppButton + preloader + page‑id body class)
│   ├── SafeHtml.jsx          Parses HTML strings into React elements via html-react-parser (replaces dangerouslySetInnerHTML)
│   ├── ErrorBoundary.jsx     Catches render crashes, shows Try Again button
│   ├── Loading.jsx           Spinner for Suspense fallback
│   ├── WhatsAppButton.jsx    Floating WhatsApp widget
│   └── sections/
│       ├── Header.jsx        Proper React component with <Link> nav, active state from useLocation()
│       ├── Footer.jsx        Proper React component with contact data, social links, service links
│       ├── Breadcrumb.jsx    Reusable breadcrumb trail
│       ├── SectionTitle.jsx  Section heading with subtitle & icon
│       ├── Button.jsx        Reusable styled button
│       ├── PhoneWidget.jsx   Phone call widget
│       ├── FeatureCards.jsx  Feature card grid
│       ├── ContactForm.jsx   "Get a Quote" form
│       ├── ContactInfo.jsx   Address/phone/email display
│       ├── GoogleMap.jsx     Embedded Google Maps iframe
│       ├── Divider.jsx       Section divider
│       ├── ProjectFilterGrid.jsx  Filterable project grid
│       ├── ServiceContentBlocks.jsx  Renders structured service content (paragraphs, headings, lists)
│       ├── ServiceGridSection.jsx   Service listing grid
│       ├── ServiceBreadcrumb.jsx    Service detail breadcrumb
│       ├── ServiceImage.jsx         Service detail featured image
│       ├── ServiceBody.jsx          Service detail body (content blocks)
│       └── ServiceSidebar.jsx       Service detail sidebar nav
├── pages/
│   ├── Home.jsx              Page: /  — uses SafeHtml (parses home-content.html)
│   ├── About.jsx             Page: /about — uses SafeHtml (parses about-content.html)
│   ├── Services.jsx          Page: /service — uses SafeHtml (parses service-content.html)
│   ├── ServiceDetail.jsx     Page: /service/:slug — fully componentized, reads services.json
│   ├── Project.jsx           Page: /project — fully componentized, uses ProjectFilterGrid
│   └── Contact.jsx           Page: /contact — fully componentized, uses section components
├── hooks/
│   ├── useLayoutInit.js      Re-initializes Elementor theme JS on route change (requestAnimationFrame polling)
│   ├── useNavFix.js          Intercepts links inside a ref for proper SPA routing
│   └── useThemeAssets.js     Injects <meta> and <link> tags (OG, canonical, favicon) on mount
├── data/                     Content sources
│   ├── home-content.html     Home page HTML content (parsed by SafeHtml)
│   ├── about-content.html    About page HTML content (parsed by SafeHtml)
│   ├── service-content.html  Services listing HTML content (parsed by SafeHtml)
│   └── services.json         Structured data for 9 service detail pages (contentBlocks, images, metadata)
└── css/                      Build-time CSS imports (all bundled into one index-*.css)
    ├── variables.css         :root CSS custom properties for brand colors (imported first)
    ├── kit.css               Elementor kit CSS (global styles)
    ├── theme.css             Rakar theme overrides
    ├── layout.css            Header (elementor-945) + Footer (elementor-1098 / elementor-128) CSS
    ├── pages.css             Combined CSS for all 5 main pages
    ├── services.css          Combined CSS for all 9 service detail pages
    ├── dynamic.css           Dynamic overrides using var(--brand-*) with fallbacks
    └── pages/                Individual page CSS files (development reference — edit these, then regenerate combined)
        ├── page-23.css       Home page
        ├── page-31.css       About page
        ├── page-32.css       Services listing
        ├── page-36.css       Project page
        ├── page-48.css       Contact page
        └── service-*.css     Individual service detail pages (9 files)
```

## Strategy: Hybrid React + WordPress Content

The app uses a **hybrid approach**:

1. **Componentized pages** (Contact, Project, ServiceDetail) use proper React section components with structured data — no HTML strings.
2. **Transitional pages** (Home, About, Services) render raw Elementor HTML from `src/data/*.html` files, parsed at runtime by `html-react-parser` via `SafeHtml.jsx` — **no `dangerouslySetInnerHTML`** anywhere in the app.
3. **Styling** comes from two places:
   - Build-time CSS imports (`src/css/*.css`) — Elementor's page-specific CSS bundled by Vite
   - Runtime assets from `public/wp-content/` — theme stylesheets, fonts, images
4. **Behavior** comes from the original Rakar theme JS loaded via `<script>` tags in `index.html`
5. **Theme re-initialization** on route change is handled by `useLayoutInit.js` via `requestAnimationFrame` polling

### Why This Works

Elementor pages are self-contained: each page has a unique CSS class (`.elementor-<postId>`) and the HTML references it. By loading ALL page CSS at once and the correct page HTML per route, we get pixel-perfect WordPress output without running PHP.

### Migration Path

The codebase is in active migration from "raw Elementor HTML" to "proper React components":

| Page | Approach | Status |
|------|----------|--------|
| Contact | Full React components (ContactForm, ContactInfo, GoogleMap) | ✅ Complete |
| Project | Full React components (ProjectFilterGrid) | ✅ Complete |
| ServiceDetail | Structured data from `services.json` + React components | ✅ Complete |
| Home | `SafeHtml` parsing `home-content.html` | 🔄 Transitional |
| About | `SafeHtml` parsing `about-content.html` | 🔄 Transitional |
| Services | `SafeHtml` parsing `service-content.html` | 🔄 Transitional |

## Section Components & Data Flow

### Componentized Pages (Contact, Project, ServiceDetail)

These pages compose reusable section components rather than rendering raw HTML:

```
Contact.jsx
  ├── Breadcrumb          props: title, items[]
  ├── SectionTitle        props: subtitle, heading, alignment
  ├── ContactInfo         props: items[]  ({ icon, label, value, link })
  ├── ContactForm         props: heading
  ├── GoogleMap           props: src, title
  └── Divider

ServiceDetail.jsx
  ├── ServiceBreadcrumb   props: title
  ├── ServiceImage        props: src, srcSet, width, height
  ├── ServiceBody         props: blocks[]  (from services.json → contentBlocks)
  └── ServiceSidebar      props: activeSlug  (renders SERVICE_SIDEBAR_ITEMS from config.js)

Project.jsx
  └── ProjectFilterGrid   (self-contained, reads project data from public/wp-content/)
```

All section components return null/empty gracefully when props are missing — no hard crashes.

### SafeHtml Pages (Home, About, Services)

```
Home.jsx
  └── SafeHtml
        └── html-react-parser(require('home-content.html'))
              └── useNavFix(ref) — intercepts <a> clicks for SPA navigation

About.jsx  (same pattern)
Services.jsx  (same pattern)
```

### Central Configuration (`config.js`)

All magic values live in one file:
- **Brand colors**: `BRAND_PRIMARY`, `BRAND_DARK`, `BRAND_NAVY` (from `.env` or defaults)
- **WhatsApp**: `WHATSAPP_PHONE` (from `.env`)
- **Routes**: `SPA_ROUTES`, `PAGE_IDS` (path → WordPress post ID)
- **Services**: `POST_ID_TO_SLUG`, `SERVICE_SIDEBAR_ITEMS`, `MENU_ITEM_IDS`

## Key Fixes & Workarounds

| Issue | Solution |
|-------|----------|
| Elementor lazyload hides background images | Removed `.e-con.e-parent:nth-of-type(n+4):not(.e-lazyloaded)` CSS that applies `background-image: none !important` (was in `theme.css` — already cleaned) |
| Elfsight WhatsApp plugin crashes page | Replaced with custom `WhatsAppButton.jsx` component |
| Theme JS not found on route change | `useLayoutInit.js` polls with `requestAnimationFrame` until `window.rakar_content_load_scripts` is available |
| SPA links reload page | `useNavFix.js` intercepts same-site links, uses `react-router-dom`'s `useNavigate`; Header nav uses `<Link>` components |
| Missing Elementor JS dependencies | Added `webpack.runtime.min.js` and `frontend-modules.min.js` to `index.html` |
| Waypoint plugin 404 | Created empty shim at `public/wp-includes/js/waypoint.min.js` |

## Style Organization

### CSS Import Order (in `main.jsx`)

```js
import './css/variables.css'   // 1. Brand color variables (first so everything else can use them)
import './css/kit.css'         // 2. Elementor kit global styles
import './css/theme.css'       // 3. Rakar theme overrides
import './css/layout.css'      // 4. Header + Footer layout
import './css/pages.css'       // 5. Main page styles
import './css/services.css'    // 6. Service detail styles
import './css/dynamic.css'     // 7. Dynamic overrides using var(--brand-*) with fallbacks
```

**Order matters** — `variables.css` is first so `dynamic.css` (which uses `var(--brand-*)`) resolves correctly.

### Brand Color System

Colors are defined once in `.env`, flow through `config.js`, and land in CSS custom properties:

```
.env                    →  config.js          →  variables.css
VITE_BRAND_PRIMARY      →  BRAND_PRIMARY      →  --brand-primary
VITE_BRAND_DARK         →  BRAND_DARK         →  --brand-dark
VITE_BRAND_NAVY         →  BRAND_NAVY         →  --brand-navy
```

`dynamic.css` uses these variables with fallbacks (e.g., `var(--brand-primary, #FA2D39)`) so it works even if variables.css is somehow absent.

### Regenerating Combined CSS

After editing individual page files in `src/css/pages/`:

```bash
cat src/css/pages/page-*.css > src/css/pages.css
cat src/css/pages/service-*.css > src/css/services.css
```

## Updating Content

### HTML-based pages (Home, About, Services)
1. Download updated HTML from WordPress
2. Replace the corresponding file in `src/data/`
3. Update the CSS in `src/css/pages/` if Elementor page CSS changed
4. Rebuild: `npm run build`

### Componentized pages (Contact, Project, ServiceDetail)
1. Edit the relevant section component in `src/components/sections/`
2. For service detail content, edit `src/data/services.json`
3. For contact data, edit `Contact.jsx` directly
4. Rebuild: `npm run build`

### Brand Colors
Edit `.env` (or create from `.env.example`), then rebuild:
```
VITE_WHATSAPP_PHONE=971564148980
VITE_BRAND_PRIMARY=#FA2D39
VITE_BRAND_DARK=#101840
VITE_BRAND_NAVY=#0a004c
```

## Setup & Development

```bash
cd react-app
npm install
npm run dev          # Vite dev server (HMR enabled)
```

## Build & Deploy

```bash
npm run build        # Outputs to dist/  — 0 errors, 0 warnings
```

The site is served via a **Python3 HTTP server** on port 8011:

```bash
cd dist && python3 -m http.server 8011
```

No CI/CD pipeline is configured — deploy is manual.

### Production File Structure

```
dist/
├── index.html                          7.6 KB │ gzip: 2.1 KB
├── assets/index-*.css                 74.0 KB │ gzip: 5.4 KB
├── assets/index-*.js                 257.3 KB │ gzip: 80.9 KB  (main bundle: router, components, CSS)
├── assets/Home-*.js                   69.7 KB │ gzip: 10.8 KB
├── assets/SafeHtml-*.js               27.8 KB │ gzip: 10.1 KB  (html-react-parser chunk)
├── assets/Services-*.js               29.8 KB │ gzip: 4.5 KB
├── assets/About-*.js                  26.5 KB │ gzip: 3.9 KB
├── assets/ServiceDetail-*.js          25.1 KB │ gzip: 7.0 KB
├── assets/Contact-*.js                 9.2 KB │ gzip: 2.3 KB
├── assets/Project-*.js                 4.0 KB │ gzip: 1.5 KB
└── assets/Breadcrumb-*.js              0.6 KB │ gzip: 0.3 KB
```

All other assets (theme CSS/JS, Elementor assets, images) are served from `public/wp-content/` and `public/wp-includes/`.

The build produces **0 errors, 0 warnings**. If you see warnings, fix them before deploying.

## Dependencies

| Package | Version | Purpose |
|---------|---------|---------|
| react | ^19.2.6 | UI framework |
| react-dom | ^19.2.6 | DOM renderer |
| react-router-dom | ^7.17.0 | SPA routing |
| html-react-parser | ^6.1.4 | Safe HTML-to-React parsing |
| @fortawesome/fontawesome-free | ^7.3.0 | Icon library |
| vite | ^8.0.12 | Build tool |
| @vitejs/plugin-react | ^6.0.1 | Vite React plugin |
