Added comprehensive documentation for Mobile Contextual Header Pattern. PROJECT_SOP.md Updates: - Added section 5.8: Mobile Contextual Header Pattern - Documented dual-header system concept - Provided implementation examples - Added CRUD page rules table - Included form submit pattern - Listed best practices and file references PROGRESS_NOTE.md Updates: - Added complete progress entry for Mobile Orders UI Enhancement - Documented all 6 major features implemented - Included technical implementation details - Listed all modified files - Added testing checklist - Documented git commits - Defined next steps Key Documentation: ✅ Dual header system (Contextual + Page Header) ✅ Implementation patterns with code examples ✅ CRUD page header rules ✅ Form ref pattern for header submit buttons ✅ Responsive action button patterns ✅ Industry standard references ✅ Complete feature list and benefits ✅ Zero eslint errors/warnings achievement Status: Production ready, fully documented
34 KiB
🧭 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
.mdfiles 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:
- ✅ Update
PROGRESS_NOTE.mdafter completing any major feature - ✅ Add test cases to
TESTING_CHECKLIST.mdbefore implementation - ✅ Use consistent formatting (emojis, headings, code blocks)
- ✅ Include "Last synced" timestamp in GMT+7
- ✅ Reference file paths and line numbers for code changes
2. 🧱 Core Principles
- Zero Data Migration — All data remains in WooCommerce’s database schema.
- Safe Activation/Deactivation — Deactivating WooNooW restores vanilla Woo instantly.
- HPOS-First Architecture — Mandatory use of WooCommerce HPOS.
- Hybrid by Default — SSR + React SPA islands for Cart, Checkout, and My‑Account.
- Full SPA Option — Optional React-only mode for performance-critical sites.
- Compat Layer — HookBridge & SlotRenderer preserve legacy addon behavior.
- 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
- Use LocalWP or Docker (PHP 8.2+, MySQL 8, Redis optional).
- Clone or mount
woonoowfolder into/wp-content/plugins/. - Ensure WooCommerce is installed and active.
- Activate WooNooW in wp-admin → “Plugins.”
5.2 Build & Test Commands
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:
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-readydev— development stagingfeature/*— 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 Barbecomes a collapsible sidebar while all others remain visible. - Sticky layout rules ensure
App BarandMenu Barremain 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-ctrlclass 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.cssdefines base CSS variables for control sizing. - File:
admin-spa/src/index.cssimports./ui/tokens.cssand applies the.ui-ctrlrules globally.
These rules ensure consistent UX across device classes while maintaining WooNooW's design hierarchy.
5.8 Mobile Contextual Header Pattern
WooNooW implements a dual-header system for mobile-first UX, ensuring actionable pages have consistent navigation and action buttons.
Concept: Two Headers on Mobile
-
Contextual Header (Mobile + Desktop)
- Common actions that work everywhere
- Format:
[Back Button] Page Title [Primary Action] - Always visible (sticky)
- Examples: Back, Edit, Save, Create
-
Page Header / Extra Actions (Desktop Only)
- Additional desktop-specific actions
- Hidden on mobile (
hidden md:flex) - Examples: Print, Invoice, Label, Export
Implementation Pattern
import { usePageHeader } from '@/contexts/PageHeaderContext';
import { Button } from '@/components/ui/button';
export default function MyPage() {
const { setPageHeader, clearPageHeader } = usePageHeader();
const nav = useNavigate();
// Set contextual header
useEffect(() => {
const actions = (
<div className="flex gap-2">
<Button size="sm" variant="ghost" onClick={() => nav('/parent')}>
{__('Back')}
</Button>
<Button size="sm" onClick={handlePrimaryAction}>
{__('Save')}
</Button>
</div>
);
setPageHeader(__('Page Title'), actions);
return () => clearPageHeader();
}, [dependencies]);
return (
<div>
{/* Desktop-only extra actions */}
<div className="hidden md:flex gap-2">
<button onClick={printAction}>{__('Print')}</button>
<button onClick={exportAction}>{__('Export')}</button>
</div>
{/* Page content */}
</div>
);
}
Rules for CRUD Pages
| Page Type | Contextual Header | Page Header |
|---|---|---|
| List | None (list page) | Filters, Search |
| Detail | [Back] Title [Edit] | Print, Invoice, Label |
| New | [Back] Title [Create] | None |
| Edit | [Back] Title [Save] | None |
Form Submit Pattern
For New/Edit pages, move submit button to contextual header:
// Use formRef to trigger submit from header
const formRef = useRef<HTMLFormElement>(null);
const actions = (
<Button onClick={() => formRef.current?.requestSubmit()}>
{__('Save')}
</Button>
);
<OrderForm formRef={formRef} hideSubmitButton={true} />
Best Practices
- No Duplication - If action is in contextual header, remove from page body
- Mobile First - Contextual header shows essential actions only
- Desktop Enhancement - Extra actions in page header (desktop only)
- Consistent Pattern - All CRUD pages follow same structure
- Loading States - Buttons show loading state during mutations
Files
admin-spa/src/contexts/PageHeaderContext.tsx- Context provideradmin-spa/src/hooks/usePageHeader.ts- Hook for setting headersadmin-spa/src/components/PageHeader.tsx- Header component
5.8 Error Handling & User Notifications
WooNooW implements a centralized, user-friendly error handling system that ensures consistent UX across all features.
Core Principles
- Never expose technical details to end users (no "API 500", stack traces, or raw error codes)
- Use appropriate notification types based on context
- Provide actionable feedback with clear next steps
- Maintain consistency across all pages and features
Notification Types
| Context | Component | Use Case | Example |
|---|---|---|---|
| Page Load Errors | <ErrorCard> |
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 | <ErrorMessage> |
Form field errors | "Email address is required" |
Implementation
// 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 <ErrorCard
title="Failed to load data"
message={getPageLoadErrorMessage(query.error)}
onRetry={() => query.refetch()}
/>;
}
Error Message Mapping
Backend errors are mapped to user-friendly messages in lib/errorHandling.ts:
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 utilitiesadmin-spa/src/components/ErrorCard.tsx— Page load error componentadmin-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:
{
"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.phpcreate() method - Frontend handling:
admin-spa/src/lib/errorHandling.tsgetErrorMessage()
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:
// 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:
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
<button>{__('Try again')}</button>
<h2>{sprintf(__('Order #%s'), order.number)}</h2>
// 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
- Never hardcode user-facing strings - Always use translation functions
- Use translators comments for context when using placeholders
- Keep strings simple - Avoid complex concatenation
- 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
import { LoadingState } from '@/components/LoadingState';
// Default loading
<LoadingState />
// Custom message
<LoadingState message={__('Loading order...')} />
// Different sizes
<LoadingState size="sm" message={__('Saving...')} />
<LoadingState size="md" message={__('Loading...')} /> // default
<LoadingState size="lg" message={__('Processing...')} />
// Full screen overlay
<LoadingState fullScreen message={__('Loading...')} />
2. PageLoadingState
import { PageLoadingState } from '@/components/LoadingState';
// For full page loads
if (isLoading) {
return <PageLoadingState message={__('Loading order...')} />;
}
3. InlineLoadingState
import { InlineLoadingState } from '@/components/LoadingState';
// For inline loading within components
{isLoading && <InlineLoadingState message={__('Loading...')} />}
4. CardLoadingSkeleton
import { CardLoadingSkeleton } from '@/components/LoadingState';
// For loading card content
{isLoading && <CardLoadingSkeleton />}
5. TableLoadingSkeleton
import { TableLoadingSkeleton } from '@/components/LoadingState';
// For loading table rows
{isLoading && <TableLoadingSkeleton rows={10} />}
Usage Guidelines
Page-Level Loading:
// ✅ Good - Use PageLoadingState for full page loads
if (orderQ.isLoading || countriesQ.isLoading) {
return <PageLoadingState message={sprintf(__('Loading order #%s...'), orderId)} />;
}
// ❌ Bad - Don't use plain text
if (isLoading) {
return <div>Loading...</div>;
}
Inline Loading:
// ✅ Good - Use InlineLoadingState for partial loads
{q.isLoading && <InlineLoadingState message={__('Loading order...')} />}
// ❌ Bad - Don't use custom spinners
{q.isLoading && <div><Loader2 className="animate-spin" /> Loading...</div>}
Table Loading:
// ✅ Good - Use TableLoadingSkeleton for tables
{q.isLoading && <TableLoadingSkeleton rows={10} />}
// ❌ Bad - Don't show empty state while loading
{q.isLoading && <div>Loading data...</div>}
Best Practices
-
Always use i18n - All loading messages must be translatable
<LoadingState message={__('Loading...')} /> -
Be specific - Use descriptive messages
// ✅ Good <LoadingState message={sprintf(__('Loading order #%s...'), orderId)} /> // ❌ Bad <LoadingState message="Loading..." /> -
Choose appropriate size - Match the context
sm- Inline, buttons, small componentsmd- Default, cards, sectionslg- Full page, important actions
-
Use skeletons for lists - Better UX than spinners
{isLoading ? <TableLoadingSkeleton rows={5} /> : <Table data={data} />} -
Responsive design - Loading states work on all screen sizes
- Mobile: Optimized spacing and sizing
- Desktop: Full layout preserved
Pattern Examples
Order Edit Page:
export default function OrdersEdit() {
const orderQ = useQuery({ ... });
if (orderQ.isLoading) {
return <LoadingState message={sprintf(__('Loading order #%s...'), orderId)} />;
}
return <OrderForm ... />;
}
Order Detail Page:
export default function OrderDetail() {
const q = useQuery({ ... });
return (
<div>
<h1>{__('Order Details')}</h1>
{q.isLoading && <InlineLoadingState message={__('Loading order...')} />}
{q.data && <OrderContent order={q.data} />}
</div>
);
}
Orders List:
export default function OrdersList() {
const q = useQuery({ ... });
return (
<table>
<thead>...</thead>
<tbody>
{q.isLoading && <TableLoadingSkeleton rows={10} />}
{q.data?.map(order => <OrderRow key={order.id} order={order} />)}
</tbody>
</table>
);
}
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 metadatawoonoow/spa_routes- Register SPA routeswoonoow/nav_tree- Modify navigation treewoonoow/nav_tree/products/children- Inject into Products submenuwoonoow/dashboard/widgets- Add dashboard widgets (future)woonoow/order/detail/panels- Add order detail panels (future)
Rules:
- Always prefix with
woonoow/ - Use lowercase with underscores
- Use singular nouns for registries (
addon_registry, notaddons_registry) - Use hierarchical structure for nested items
- 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
namespace WooNooW\Compat;
class MyModuleRegistry {
const OPTION_KEY = 'wnw_my_module_data';
const VERSION = '1.0.0';
public static function init() {
add_action('plugins_loaded', [__CLASS__, 'collect_data'], 30);
add_action('activated_plugin', [__CLASS__, 'flush']);
add_action('deactivated_plugin', [__CLASS__, 'flush']);
}
public static function collect_data() {
$data = [];
/**
* Filter: woonoow/my_module/items
*
* Allows addons to register items with this module.
*
* @param array $data Array of item configurations
*
* Example:
* add_filter('woonoow/my_module/items', function($data) {
* $data['my-item'] = [
* 'id' => '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)
// 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)
// 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 (
<div>
{items.map(item => (
<div key={item.id}>{item.label}</div>
))}
</div>
);
}
6.4 Documentation Requirements
When adding a new filter hook, you MUST:
- Add to Hook Registry (see section 6.5)
- Document in code with PHPDoc
- Add example in ADDON_INJECTION_GUIDE.md
- 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
/**
* Plugin Name: My Traditional Addon
*/
// Register addon
add_filter('woonoow/addon_registry', function($addons) {
$addons['my-addon'] = [
'id' => '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() {
?>
<div class="wrap">
<h1>My Traditional Addon</h1>
<p>Built with PHP, HTML, CSS, and vanilla JS!</p>
<script>
// Vanilla JavaScript works fine
document.addEventListener('DOMContentLoaded', function() {
console.log('My addon loaded!');
});
</script>
</div>
<?php
}
This approach:
- ✅ Works with WooNooW navigation
- ✅ No React knowledge required
- ✅ Uses standard WordPress admin pages
- ✅ Can use WordPress admin styles
- ✅ Can enqueue own CSS/JS
- ⚠️ Opens in separate page (not SPA)
Approach 2: Vanilla JS Component (No React)
For developers who want SPA integration without React:
// dist/MyAddon.js - Vanilla JS module
export default function MyAddonPage(props) {
const container = document.createElement('div');
container.className = 'p-6';
container.innerHTML = `
<div class="rounded-lg border border-border p-6 bg-card">
<h2 class="text-xl font-semibold mb-2">My Addon</h2>
<p class="text-sm opacity-70">Built with vanilla JavaScript!</p>
<button id="my-button" class="px-4 py-2 bg-blue-500 text-white rounded">
Click Me
</button>
</div>
`;
// 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:
// dist/MyAddon.tsx - React component
import React from 'react';
export default function MyAddonPage() {
const [count, setCount] = React.useState(0);
return (
<div className="p-6">
<div className="rounded-lg border border-border p-6 bg-card">
<h2 className="text-xl font-semibold mb-2">My Addon</h2>
<p className="text-sm opacity-70">Built with React!</p>
<button
onClick={() => setCount(count + 1)}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
Clicked {count} times
</button>
</div>
</div>
);
}
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
- Custom login page (
- 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:
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.mdPROJECT_SOP.md- The specific file(s) being edited
Step 2: Editing Rules
- All AI edits must be idempotent — never break structure or naming conventions.
- Always follow PSR‑12 PHP standard and React code conventions.
- When unsure about a design decision, refer back to this S.O.P. before guessing.
- New files must be registered in the correct namespace path.
- 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
- Run all builds:
npm run build && npm run pack - Test in LocalWP with a sample Woo store.
- Validate HPOS compatibility and order creation flow.
- Push final
woonoow.zipto release channel (Sejoli, member.dwindi.com, or manual upload). - 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: <button onClick={() => nav('/parent/path')}><ArrowLeft /> Back</button> <h2>Page Title</h2> |
| 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.