# 🧭 WooNooW β€” Single Source of Truth (S.O.P.) This document defines the **Standard Operating Procedure** for developing, maintaining, and collaborating on the **WooNooW** project β€” ensuring every AI Agent or human collaborator follows the same workflow and conventions. --- ## 1. 🎯 Project Intent WooNooW modernizes WooCommerce **without migration**, delivering a Hybrid + SPA experience for both **storefront** and **admin**, while keeping compatibility with legacy WooCommerce addons. > **Goal:** β€œReimagine WooCommerce for now β€” faster, modern, reversible.” --- ## 1.1 πŸ“ Documentation Standards ### Progress & Testing Documentation **All progress notes and reports MUST be added to:** - `PROGRESS_NOTE.md` - Consolidated progress tracking with timestamps **All test checklists MUST be added to:** - `TESTING_CHECKLIST.md` - Comprehensive testing requirements **Feature-specific documentation:** - Create dedicated `.md` files for major features (e.g., `PAYMENT_GATEWAY_INTEGRATION.md`) - Link to these files from `PROGRESS_NOTE.md` - Include implementation details, code examples, and testing steps **Documentation Rules:** 1. βœ… Update `PROGRESS_NOTE.md` after completing any major feature 2. βœ… Add test cases to `TESTING_CHECKLIST.md` before implementation 3. βœ… Use consistent formatting (emojis, headings, code blocks) 4. βœ… Include "Last synced" timestamp in GMT+7 5. βœ… Reference file paths and line numbers for code changes --- ## 2. 🧱 Core Principles 1. **Zero Data Migration** β€” All data remains in WooCommerce’s database schema. 2. **Safe Activation/Deactivation** β€” Deactivating WooNooW restores vanilla Woo instantly. 3. **HPOS-First Architecture** β€” Mandatory use of WooCommerce HPOS. 4. **Hybrid by Default** β€” SSR + React SPA islands for Cart, Checkout, and My‑Account. 5. **Full SPA Option** β€” Optional React-only mode for performance-critical sites. 6. **Compat Layer** β€” HookBridge & SlotRenderer preserve legacy addon behavior. 7. **Async System** β€” MailQueue & async actions replace blocking PHP tasks. --- ## 3. βš™οΈ Tech Stack Reference | Layer | Technology | |-------|-------------| | Backend | PHP 8.2+, WordPress, WooCommerce (HPOS), Action Scheduler | | Frontend | React 18 + TypeScript, Vite, React Query, Tailwind CSS + Shadcn UI, Recharts | | Architecture | Modular PSR‑4 autoload, REST‑driven logic, SPA hydration islands | | Build | Composer + NPM + ESM scripts | | Packaging | `scripts/package-zip.mjs` | | Deployment | LocalWP for dev, Coolify for staging | --- ## 4. 🧩 Folder Structure ``` woonoow/ β”œβ”€ woonoow.php # main plugin file (WordPress entry) β”œβ”€ includes/ # PSR‑4 classes β”‚ β”œβ”€ Core/ # Bootstrap, Datastores, Mail, Hooks β”‚ β”œβ”€ Api/ # REST endpoints β”‚ β”œβ”€ Admin/ # Menus, asset loaders β”‚ β”œβ”€ Compat/ # Compatibility shims & hook mirrors β”‚ └─ … β”œβ”€ admin-spa/ # React admin interface β”œβ”€ customer-spa/ # React customer interface β”œβ”€ scripts/ # automation scripts β”‚ └─ package-zip.mjs β”œβ”€ dist/ # build output β”œβ”€ composer.json β”œβ”€ package.json β”œβ”€ README.md └─ PROJECT_SOP.md # this file ``` --- ## 5. 🧰 Development Workflow ### 5.1 Environment Setup 1. Use **LocalWP** or **Docker** (PHP 8.2+, MySQL 8, Redis optional). 2. Clone or mount `woonoow` folder into `/wp-content/plugins/`. 3. Ensure WooCommerce is installed and active. 4. Activate WooNooW in wp-admin β†’ β€œPlugins.” ### 5.2 Build & Test Commands ```bash npm run build # build both admin & customer SPAs npm run pack # create woonoow.zip for release composer dump-autoload ``` ### 5.3 Plugin Packaging - The release ZIP must contain only: ``` woonoow.php includes/ admin-spa/dist/ customer-spa/dist/ composer.json package.json phpcs.xml README.md ``` - Build ZIP using: ```bash node scripts/package-zip.mjs ``` ### 5.4 Commit Convention Use conventional commits: ``` feat(api): add checkout quote endpoint fix(core): prevent duplicate email send on async queue refactor(admin): improve SPA routing ``` ### 5.5 Branching - `main` β€” stable, production-ready - `dev` β€” development staging - `feature/*` β€” specific features or fixes ### 5.6 Admin SPA Template Pattern The WooNooW Admin SPA follows a consistent layout structure ensuring a predictable UI across all routes: **Structure** ``` Admin-SPA β”œβ”€β”€ App Bar [Branding | Version | Server Connectivity | Global Buttons (Fullscreen)] β”œβ”€β”€ Menu Bar (Main Menu) [Normal (Tabbed Overflow-X-Auto)] [Fullscreen (Sidebar)] β”œβ”€β”€ Submenu Bar (Tabbed Overflow-X-Auto, context-sensitive) └── Page Template β”œβ”€β”€ Page Tool Bar (Page filters, CRUD buttons, Back button) └── Page Content (Data tables, cards, forms) ``` **Behavioral Notes** - `App Bar`: Persistent across all routes; contains global controls (fullscreen, server, user menu). - `Menu Bar`: Primary navigation for main sections (Dashboard, Orders, Products, etc.); sticky with overflow-x scroll. - `Submenu Bar`: Context-sensitive secondary tabs under the main menu. - `Page Tool Bar`: Contains functional filters and actions relevant to the current page. - `Page Content`: Hosts the page bodyβ€”tables, analytics, and CRUD forms. - In Fullscreen mode, `Menu Bar` becomes a collapsible sidebar while all others remain visible. - Sticky layout rules ensure `App Bar` and `Menu Bar` remain fixed while content scrolls independently. ### 5.7 Mobile Responsiveness & UI Controls WooNooW enforces a mobile‑first responsive standard across all SPA interfaces to ensure usability on small screens. **Control Sizing Standard (`.ui-ctrl`)** - All interactive controls β€” input, select, button, and dropdown options β€” must include the `.ui-ctrl` class or equivalent utility for consistent sizing. - Default height: `h-11` (mobile), `md:h-9` (desktop). - This sizing improves tap area accessibility and maintains visual alignment between mobile and desktop. **Responsive Layout Rules** - On mobile view, even in fullscreen mode, the layout uses **Topbar navigation** instead of Sidebar for better reachability. - The Sidebar layout is applied **only** in desktop fullscreen mode. - Sticky top layers (`App Bar`, `Menu Bar`) remain visible while sub‑content scrolls independently. - Tables and grids must support horizontal scroll (`overflow-x-auto`) and collapse to cards when screen width < 640px. **Tokens & Global Styles** - File: `admin-spa/src/ui/tokens.css` defines base CSS variables for control sizing. - File: `admin-spa/src/index.css` imports `./ui/tokens.css` and applies the `.ui-ctrl` rules globally. These rules ensure consistent UX across device classes while maintaining WooNooW’s design hierarchy. ### 5.8 Error Handling & User Notifications WooNooW implements a centralized, user-friendly error handling system that ensures consistent UX across all features. **Core Principles** 1. **Never expose technical details** to end users (no "API 500", stack traces, or raw error codes) 2. **Use appropriate notification types** based on context 3. **Provide actionable feedback** with clear next steps 4. **Maintain consistency** across all pages and features **Notification Types** | Context | Component | Use Case | Example | |---------|-----------|----------|---------| | **Page Load Errors** | `` | Query failures, data fetch errors | "Failed to load orders" with retry button | | **Action Errors** | `toast.error()` | Mutation failures, form submissions | "Failed to create order. Please check all required fields." | | **Action Success** | `toast.success()` | Successful mutations | "Order created successfully" | | **Inline Validation** | `` | Form field errors | "Email address is required" | **Implementation** ```typescript // For mutations (create, update, delete) import { showErrorToast, showSuccessToast } from '@/lib/errorHandling'; const mutation = useMutation({ mutationFn: OrdersApi.create, onSuccess: (data) => { showSuccessToast('Order created successfully', `Order #${data.number} created`); }, onError: (error) => { showErrorToast(error); // Automatically extracts user-friendly message } }); // For queries (page loads) import { ErrorCard } from '@/components/ErrorCard'; import { getPageLoadErrorMessage } from '@/lib/errorHandling'; if (query.isError) { return query.refetch()} />; } ``` **Error Message Mapping** Backend errors are mapped to user-friendly messages in `lib/errorHandling.ts`: ```typescript const friendlyMessages = { 'no_items': 'Please add at least one product to the order', 'create_failed': 'Failed to create order. Please check all required fields.', 'update_failed': 'Failed to update order. Please check all fields.', 'not_found': 'The requested item was not found', 'forbidden': 'You do not have permission to perform this action', }; ``` **Toast Configuration** - **Position:** Bottom-right - **Duration:** 4s (success), 6s (errors) - **Theme:** Light mode with colored backgrounds - **Colors:** Green (success), Red (error), Amber (warning), Blue (info) **Files** - `admin-spa/src/lib/errorHandling.ts` β€” Centralized error utilities - `admin-spa/src/components/ErrorCard.tsx` β€” Page load error component - `admin-spa/src/components/ui/sonner.tsx` β€” Toast configuration ### 5.9 Data Validation & Required Fields WooNooW enforces strict validation rules to ensure data integrity and provide clear feedback to users. **Order Creation Validation** All orders must include: | Field | Requirement | Error Message | |-------|-------------|---------------| | **Products** | At least 1 product | "At least one product is required" | | **Billing First Name** | Required | "Billing first name is required" | | **Billing Last Name** | Required | "Billing last name is required" | | **Billing Email** | Required & valid format | "Billing email is required" / "Billing email is not valid" | | **Billing Address** | Required | "Billing address is required" | | **Billing City** | Required | "Billing city is required" | | **Billing Postcode** | Required | "Billing postcode is required" | | **Billing Country** | Required | "Billing country is required" | **Backend Validation Response** When validation fails, the API returns: ```json { "error": "validation_failed", "message": "Please complete all required fields", "fields": [ "Billing first name is required", "Billing email is required", "Billing address is required" ] } ``` **Frontend Display** The error handling utility automatically formats field errors as a bulleted list: ``` ❌ Please complete all required fields β€’ Billing first name is required β€’ Billing email is required β€’ Billing address is required β€’ Billing city is required β€’ Billing postcode is required ``` Each field error appears as a bullet point on its own line, making it easy for users to scan and see exactly what needs to be fixed. **Implementation Location** - Backend validation: `includes/Api/OrdersController.php` create() method - Frontend handling: `admin-spa/src/lib/errorHandling.ts` getErrorMessage() ### 5.10 Internationalization (i18n) WooNooW follows WordPress translation standards to ensure all user-facing strings are translatable. **Text Domain:** `woonoow` **Backend (PHP)** Use WordPress translation functions: ```php // Simple translation __( 'Billing first name', 'woonoow' ) // Translation with sprintf sprintf( __( '%s is required', 'woonoow' ), $field_label ) // Translators comment for context /* translators: %s: field label */ sprintf( __( '%s is required', 'woonoow' ), $label ) ``` **Frontend (TypeScript/React)** Use the i18n utility wrapper: ```typescript import { __, sprintf } from '@/lib/i18n'; // Simple translation __('Failed to load data') // Translation with sprintf (placeholders) sprintf(__('Order #%s created'), orderNumber) sprintf(__('Edit Order #%s'), orderId) // In components

{sprintf(__('Order #%s'), order.number)}

// In error messages const title = __('Please complete all required fields'); const message = sprintf(__('Order #%s has been created'), data.number); ``` **Translation Files** - Backend strings: Extracted to `languages/woonoow.pot` - Frontend strings: Loaded via `wp.i18n` (WordPress handles this) - Translation utilities: `admin-spa/src/lib/i18n.ts` **Best Practices** 1. **Never hardcode user-facing strings** - Always use translation functions 2. **Use translators comments** for context when using placeholders 3. **Keep strings simple** - Avoid complex concatenation 4. **Test in English first** - Ensure strings make sense before translation --- ## 5.11 Loading States WooNooW provides a **consistent loading UI system** across the application to ensure a polished user experience. **Component:** `admin-spa/src/components/LoadingState.tsx` ### Loading Components **1. LoadingState (Default)**a ```typescript import { LoadingState } from '@/components/LoadingState'; // Default loading // Custom message // Different sizes // default // Full screen overlay ``` **2. PageLoadingState** ```typescript import { PageLoadingState } from '@/components/LoadingState'; // For full page loads if (isLoading) { return ; } ``` **3. InlineLoadingState** ```typescript import { InlineLoadingState } from '@/components/LoadingState'; // For inline loading within components {isLoading && } ``` **4. CardLoadingSkeleton** ```typescript import { CardLoadingSkeleton } from '@/components/LoadingState'; // For loading card content {isLoading && } ``` **5. TableLoadingSkeleton** ```typescript import { TableLoadingSkeleton } from '@/components/LoadingState'; // For loading table rows {isLoading && } ``` ### Usage Guidelines **Page-Level Loading:** ```typescript // βœ… Good - Use PageLoadingState for full page loads if (orderQ.isLoading || countriesQ.isLoading) { return ; } // ❌ Bad - Don't use plain text if (isLoading) { return
Loading...
; } ``` **Inline Loading:** ```typescript // βœ… Good - Use InlineLoadingState for partial loads {q.isLoading && } // ❌ Bad - Don't use custom spinners {q.isLoading &&
Loading...
} ``` **Table Loading:** ```typescript // βœ… Good - Use TableLoadingSkeleton for tables {q.isLoading && } // ❌ Bad - Don't show empty state while loading {q.isLoading &&
Loading data...
} ``` ### Best Practices 1. **Always use i18n** - All loading messages must be translatable ```typescript ``` 2. **Be specific** - Use descriptive messages ```typescript // βœ… Good // ❌ Bad ``` 3. **Choose appropriate size** - Match the context - `sm` - Inline, buttons, small components - `md` - Default, cards, sections - `lg` - Full page, important actions 4. **Use skeletons for lists** - Better UX than spinners ```typescript {isLoading ? : } ``` 5. **Responsive design** - Loading states work on all screen sizes - Mobile: Optimized spacing and sizing - Desktop: Full layout preserved ### Pattern Examples **Order Edit Page:** ```typescript export default function OrdersEdit() { const orderQ = useQuery({ ... }); if (orderQ.isLoading) { return ; } return ; } ``` **Order Detail Page:** ```typescript export default function OrderDetail() { const q = useQuery({ ... }); return (

{__('Order Details')}

{q.isLoading && } {q.data && }
); } ``` **Orders List:** ```typescript export default function OrdersList() { const q = useQuery({ ... }); return (
... {q.isLoading && } {q.data?.map(order => )}
); } ``` --- ## 6. πŸ”Œ Addon Development Standards ### 6.1 Addon Injection System WooNooW provides a **filter-based addon injection system** that allows third-party plugins to integrate seamlessly with the SPA without modifying core files. **Core Principle:** All modules that can accept external injection MUST provide filter hooks following the standard naming convention. ### 6.2 Hook Naming Convention All WooNooW hooks follow this structure: ``` woonoow/{category}/{action}[/{subcategory}] ``` **Examples:** - `woonoow/addon_registry` - Register addon metadata - `woonoow/spa_routes` - Register SPA routes - `woonoow/nav_tree` - Modify navigation tree - `woonoow/nav_tree/products/children` - Inject into Products submenu - `woonoow/dashboard/widgets` - Add dashboard widgets (future) - `woonoow/order/detail/panels` - Add order detail panels (future) **Rules:** 1. Always prefix with `woonoow/` 2. Use lowercase with underscores 3. Use singular nouns for registries (`addon_registry`, not `addons_registry`) 4. Use hierarchical structure for nested items 5. Use descriptive names that indicate purpose ### 6.3 Filter Template Pattern When creating a new module that accepts external injection, follow this template: #### **Backend (PHP)** ```php 'my-item', * 'label' => 'My Item', * 'value' => 'something', * ]; * return $data; * }); */ $data = apply_filters('woonoow/my_module/items', $data); // Validate and store $validated = self::validate_items($data); update_option(self::OPTION_KEY, [ 'version' => self::VERSION, 'items' => $validated, 'updated' => time(), ], false); } private static function validate_items(array $items): array { // Validation logic return $items; } public static function get_items(): array { $data = get_option(self::OPTION_KEY, []); return $data['items'] ?? []; } public static function flush() { delete_option(self::OPTION_KEY); } public static function get_frontend_data(): array { // Return sanitized data for frontend return self::get_items(); } } ``` #### **Expose to Frontend (Assets.php)** ```php // In localize_runtime() method wp_localize_script($handle, 'WNW_MY_MODULE', MyModuleRegistry::get_frontend_data()); wp_add_inline_script($handle, 'window.WNW_MY_MODULE = window.WNW_MY_MODULE || WNW_MY_MODULE;', 'after'); ``` #### **Frontend (TypeScript)** ```typescript // Read from window const moduleData = (window as any).WNW_MY_MODULE || []; // Use in component function MyComponent() { const items = (window as any).WNW_MY_MODULE || []; return (
{items.map(item => (
{item.label}
))}
); } ``` ### 6.4 Documentation Requirements When adding a new filter hook, you MUST: 1. **Add to Hook Registry** (see section 6.5) 2. **Document in code** with PHPDoc 3. **Add example** in ADDON_INJECTION_GUIDE.md 4. **Update** ADDONS_ADMIN_UI_REQUIREMENTS.md ### 6.5 Hook Registry See `HOOKS_REGISTRY.md` for complete list of available hooks and filters. ### 6.6 Non-React Addon Development **Question:** Can developers build addons without React? **Answer:** **YES!** WooNooW supports multiple addon approaches: #### **Approach 1: PHP + HTML/CSS/JS (No React)** Traditional WordPress addon development works perfectly: ```php 'my-addon', 'name' => 'My Addon', 'version' => '1.0.0', ]; return $addons; }); // Add navigation item that links to classic admin page add_filter('woonoow/nav_tree', function($tree) { $tree[] = [ 'key' => 'my-addon', 'label' => 'My Addon', 'path' => '/my-addon-classic', // Will redirect to admin page 'icon' => 'puzzle', 'children' => [], ]; return $tree; }); // Register classic admin page add_action('admin_menu', function() { add_menu_page( 'My Addon', 'My Addon', 'manage_options', 'my-addon-page', 'my_addon_render_page', 'dashicons-admin-generic', 30 ); }); function my_addon_render_page() { ?>

My Traditional Addon

Built with PHP, HTML, CSS, and vanilla JS!

My Addon

Built with vanilla JavaScript!

`; // Add event listeners setTimeout(() => { const button = container.querySelector('#my-button'); button.addEventListener('click', () => { alert('Vanilla JS works!'); }); }, 0); return container; } ``` **This approach:** - βœ… Integrates with SPA - βœ… No React required - βœ… Can use Tailwind classes - βœ… Can fetch from REST API - ⚠️ Must return DOM element - ⚠️ Manual state management #### **Approach 3: React Component (Full SPA)** For developers comfortable with React: ```typescript // dist/MyAddon.tsx - React component import React from 'react'; export default function MyAddonPage() { const [count, setCount] = React.useState(0); return (

My Addon

Built with React!

); } ``` **This approach:** - βœ… Full SPA integration - βœ… React state management - βœ… Can use React Query - βœ… Can use WooNooW components - βœ… Best UX - ⚠️ Requires React knowledge ### 6.7 Addon Development Checklist When creating a module that accepts addons: - [ ] Create Registry class (e.g., `MyModuleRegistry.php`) - [ ] Add filter hook with `woonoow/` prefix - [ ] Document filter in PHPDoc with example - [ ] Expose data to frontend via `Assets.php` - [ ] Add to `HOOKS_REGISTRY.md` - [ ] Add example to `ADDON_INJECTION_GUIDE.md` - [ ] Test with example addon - [ ] Update `ADDONS_ADMIN_UI_REQUIREMENTS.md` ### 6.8 Orders Module as Reference The **Orders module** is the reference implementation: - No external injection (by design) - Clean route structure - Type-safe components - Proper error handling - Mobile responsive - i18n complete Use Orders as the template for building new core modules. --- ## 7. 🎨 Admin Interface Modes WooNooW provides **three distinct admin interface modes** to accommodate different workflows and user preferences: ### **1. Normal Mode (wp-admin)** - **Access:** `/wp-admin/admin.php?page=woonoow` - **Layout:** Traditional WordPress admin with WooNooW SPA in content area - **Use Case:** Standard WordPress admin workflow - **Features:** - WordPress admin bar and sidebar visible - Full WordPress admin functionality - WooNooW SPA integrated seamlessly - Settings submenu hidden (use WooCommerce settings) - **When to use:** When you need access to other WordPress admin features alongside WooNooW ### **2. Fullscreen Mode** - **Access:** Toggle button in WooNooW header - **Layout:** WooNooW SPA only (no WordPress chrome) - **Use Case:** Focused work sessions, order processing - **Features:** - Maximized workspace - Distraction-free interface - All WooNooW features accessible - Settings submenu hidden - **When to use:** When you want to focus exclusively on WooNooW tasks ### **3. Standalone Mode** ✨ - **Access:** `https://yoursite.com/admin` - **Layout:** Complete standalone application with custom login - **Use Case:** Quick daily access, mobile-friendly, bookmark-able - **Features:** - Custom login page (`/admin#/login`) - WordPress authentication integration - Settings submenu visible (SPA settings pages) - "WordPress" button to access wp-admin - "Logout" button in header - Admin bar link in wp-admin to standalone - Session persistence across modes - **When to use:** As your primary WooNooW interface, especially on mobile or for quick access ### **Mode Switching** - **From wp-admin to Standalone:** Click "WooNooW" in admin bar - **From Standalone to wp-admin:** Click "WordPress" button in header - **To Fullscreen:** Click fullscreen toggle in any mode - **Session persistence:** Login state is shared across all modes ### **Settings Submenu Behavior** - **Normal Mode:** No settings submenu (use WooCommerce settings in wp-admin) - **Fullscreen Mode:** No settings submenu - **Standalone Mode:** Full settings submenu visible with SPA pages **Implementation:** Settings submenu uses dynamic getter in `admin-spa/src/nav/tree.ts`: ```typescript get children() { const isStandalone = (window as any).WNW_CONFIG?.standaloneMode; if (!isStandalone) return []; return [ /* settings items */ ]; } ``` --- ## 8. πŸ€– AI Agent Collaboration Rules When using an AI IDE agent (ChatGPT, Claude, etc.): ### Step 1: Context Injection Always load: - `README.md` - `PROJECT_SOP.md` - The specific file(s) being edited ### Step 2: Editing Rules 1. All AI edits must be **idempotent** β€” never break structure or naming conventions. 2. Always follow PSR‑12 PHP standard and React code conventions. 3. When unsure about a design decision, **refer back to this S.O.P.** before guessing. 4. New files must be registered in the correct namespace path. 5. When editing React components, ensure build compatibility with Vite. ### Step 3: Communication AI agents must: - Explain each patch clearly. - Never auto‑remove code without reason. - Maintain English for all code comments, Markdown for docs. --- ## 7. πŸ“¦ Release Steps 1. Run all builds: ```bash npm run build && npm run pack ``` 2. Test in LocalWP with a sample Woo store. 3. Validate HPOS compatibility and order creation flow. 4. Push final `woonoow.zip` to release channel (Sejoli, member.dwindi.com, or manual upload). 5. Tag version using semantic versioning (e.g. `v0.2.0-beta`). --- ## 8. 🧭 Decision Hierarchy | Category | Decision Reference | |-----------|--------------------| | Code Style | Follow PSR‑12 (PHP) & Airbnb/React rules | | Architecture | PSR‑4 + modular single responsibility | | UI/UX | Modern minimal style, standardized using Tailwind + Shadcn UI. Recharts for data visualization. | | Icons | Use lucide-react via npm i lucide-react. Icons should match Shadcn UI guidelines. Always import directly (e.g. import { Package } from 'lucide-react'). Maintain consistent size (16–20px) and stroke width (1.5px). Use Tailwind classes for color states. | | **Navigation Pattern** | **CRUD pages MUST follow consistent back button navigation: New Order: Index ← New. Edit Order: Index ← Detail ← Edit. Back button always goes to parent page, not index. Use ArrowLeft icon from lucide-react. Toolbar format: `

Page Title

`** | | Compatibility | Must preserve Woo hooks unless explicitly replaced | | Performance | Async-first, no blocking mail or sync jobs | | **Email Policy** | **ALL `wp_mail()` calls MUST be delayed by 15+ seconds using Action Scheduler or wp-cron. Never send emails synchronously during API requests (create, update, status change). Use `OrdersController::schedule_order_email()` pattern.** | | Deployment | LocalWP β†’ Coolify β†’ Production | --- ## 9. 🧩 Future Extensions - **Addon Manager** (JSON feed + licensing integration). - **Admin Insights** (charts, sales analytics with React). - **Storefront SPA Theme Override** (optional full React mode). - **Developer SDK** for 3rd-party addon compatibility. --- ## 10. πŸ“œ License & Ownership All rights reserved to **Dwindi (dewe.dev)**. The WooNooW project may include GPL-compatible code portions for WordPress compliance. Redistribution without written consent is prohibited outside official licensing channels. ---