feat: Add in-app documentation system

Phase 1: Core Documentation
- Created docs/ folder with 8 markdown documentation files
- Getting Started, Installation, Troubleshooting, FAQ
- Configuration docs (Appearance, SPA Mode)
- Feature docs (Shop, Checkout)
- PHP registry with filter hook for addon extensibility

Phase 2: Documentation Viewer
- DocsController.php with REST API endpoints
- GET /woonoow/v1/docs - List all docs (with addon hook)
- GET /woonoow/v1/docs/{slug} - Get document content
- Admin SPA /help route with sidebar navigation
- Markdown rendering with react-markdown
- Added Help & Docs to More page for mobile access

Filter Hook: woonoow_docs_registry
Addons can register their own documentation sections.
This commit is contained in:
Dwindi Ramadhana
2026-01-04 11:43:32 +07:00
parent 1206117df1
commit 68c3423f50
18 changed files with 3083 additions and 4 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -49,8 +49,10 @@
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-markdown": "^10.1.0",
"react-router-dom": "^7.9.4", "react-router-dom": "^7.9.4",
"recharts": "^3.3.0", "recharts": "^3.3.0",
"remark-gfm": "^4.0.1",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"tailwind-merge": "^3.3.1", "tailwind-merge": "^3.3.1",
"vaul": "^1.1.2", "vaul": "^1.1.2",

View File

@@ -261,6 +261,7 @@ import NewsletterSubscribers from '@/routes/Marketing/Newsletter';
import CampaignsList from '@/routes/Marketing/Campaigns'; import CampaignsList from '@/routes/Marketing/Campaigns';
import CampaignEdit from '@/routes/Marketing/Campaigns/Edit'; import CampaignEdit from '@/routes/Marketing/Campaigns/Edit';
import MorePage from '@/routes/More'; import MorePage from '@/routes/More';
import Help from '@/routes/Help';
// Addon Route Component - Dynamically loads addon components // Addon Route Component - Dynamically loads addon components
function AddonRoute({ config }: { config: any }) { function AddonRoute({ config }: { config: any }) {
@@ -583,6 +584,9 @@ function AppRoutes() {
<Route path="/marketing/campaigns" element={<CampaignsList />} /> <Route path="/marketing/campaigns" element={<CampaignsList />} />
<Route path="/marketing/campaigns/:id" element={<CampaignEdit />} /> <Route path="/marketing/campaigns/:id" element={<CampaignEdit />} />
{/* Help - Main menu route with no submenu */}
<Route path="/help" element={<Help />} />
{/* Dynamic Addon Routes */} {/* Dynamic Addon Routes */}
{addonRoutes.map((route: any) => ( {addonRoutes.map((route: any) => (
<Route <Route

View File

@@ -0,0 +1,163 @@
import { useState, useEffect } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { Skeleton } from '@/components/ui/skeleton';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { AlertCircle } from 'lucide-react';
import type { DocContent as DocContentType } from './types';
interface DocContentProps {
slug: string;
}
export default function DocContent({ slug }: DocContentProps) {
const [doc, setDoc] = useState<DocContentType | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchDoc = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(`/wp-json/woonoow/v1/docs/${slug}`, {
credentials: 'include',
});
if (!response.ok) {
throw new Error('Document not found');
}
const data = await response.json();
if (data.success) {
setDoc(data.doc);
} else {
throw new Error(data.message || 'Failed to load document');
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load document');
setDoc(null);
} finally {
setLoading(false);
}
};
fetchDoc();
}, [slug]);
if (loading) {
return (
<div className="space-y-4">
<Skeleton className="h-10 w-3/4" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-2/3" />
<Skeleton className="h-32 w-full mt-6" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-5/6" />
</div>
);
}
if (error) {
return (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
{error}
</AlertDescription>
</Alert>
);
}
if (!doc) {
return null;
}
return (
<article className="prose prose-slate dark:prose-invert max-w-none">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
// Custom heading with anchor links
h1: ({ children }) => (
<h1 className="text-3xl font-bold mb-6 pb-4 border-b">{children}</h1>
),
h2: ({ children }) => (
<h2 className="text-2xl font-semibold mt-10 mb-4">{children}</h2>
),
h3: ({ children }) => (
<h3 className="text-xl font-medium mt-8 mb-3">{children}</h3>
),
// Styled tables
table: ({ children }) => (
<div className="overflow-x-auto my-6">
<table className="min-w-full border-collapse border border-border rounded-lg">
{children}
</table>
</div>
),
th: ({ children }) => (
<th className="border border-border bg-muted px-4 py-2 text-left font-semibold">
{children}
</th>
),
td: ({ children }) => (
<td className="border border-border px-4 py-2">{children}</td>
),
// Styled code blocks
code: ({ className, children }) => {
const isInline = !className;
if (isInline) {
return (
<code className="bg-muted px-1.5 py-0.5 rounded text-sm font-mono">
{children}
</code>
);
}
return (
<code className={className}>
{children}
</code>
);
},
pre: ({ children }) => (
<pre className="bg-muted p-4 rounded-lg overflow-x-auto my-4">
{children}
</pre>
),
// Styled blockquotes for notes
blockquote: ({ children }) => (
<blockquote className="border-l-4 border-primary bg-primary/5 pl-4 py-2 my-4 italic">
{children}
</blockquote>
),
// Links
a: ({ href, children }) => (
<a
href={href}
className="text-primary hover:underline"
target={href?.startsWith('http') ? '_blank' : undefined}
rel={href?.startsWith('http') ? 'noopener noreferrer' : undefined}
>
{children}
</a>
),
// Lists
ul: ({ children }) => (
<ul className="list-disc pl-6 my-4 space-y-2">{children}</ul>
),
ol: ({ children }) => (
<ol className="list-decimal pl-6 my-4 space-y-2">{children}</ol>
),
// Horizontal rule
hr: () => <hr className="my-8 border-border" />,
}}
>
{doc.content}
</ReactMarkdown>
</article>
);
}

View File

@@ -0,0 +1,160 @@
import { useState, useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
import { Book, ChevronRight, FileText, Settings, Layers, Puzzle, Menu, X } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import DocContent from './DocContent';
import type { DocSection } from './types';
const iconMap: Record<string, React.ReactNode> = {
'book-open': <Book className="w-4 h-4" />,
'file-text': <FileText className="w-4 h-4" />,
'settings': <Settings className="w-4 h-4" />,
'layers': <Layers className="w-4 h-4" />,
'puzzle': <Puzzle className="w-4 h-4" />,
};
export default function Help() {
const [searchParams, setSearchParams] = useSearchParams();
const [sections, setSections] = useState<DocSection[]>([]);
const [loading, setLoading] = useState(true);
const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>({});
const [sidebarOpen, setSidebarOpen] = useState(false);
const currentSlug = searchParams.get('doc') || 'getting-started';
// Fetch documentation registry
useEffect(() => {
const fetchDocs = async () => {
try {
const response = await fetch('/wp-json/woonoow/v1/docs', {
credentials: 'include',
});
const data = await response.json();
if (data.success) {
setSections(data.sections);
// Expand all sections by default
const expanded: Record<string, boolean> = {};
data.sections.forEach((section: DocSection) => {
expanded[section.key] = true;
});
setExpandedSections(expanded);
}
} catch (error) {
console.error('Failed to fetch docs:', error);
} finally {
setLoading(false);
}
};
fetchDocs();
}, []);
const toggleSection = (key: string) => {
setExpandedSections(prev => ({
...prev,
[key]: !prev[key],
}));
};
const selectDoc = (slug: string) => {
setSearchParams({ doc: slug });
setSidebarOpen(false); // Close mobile sidebar
};
const isActive = (slug: string) => slug === currentSlug;
return (
<div className="flex h-[calc(100vh-64px)] bg-background">
{/* Mobile menu button */}
<Button
variant="ghost"
size="icon"
className="lg:hidden fixed bottom-4 right-4 z-50 bg-primary text-primary-foreground shadow-lg rounded-full w-12 h-12"
onClick={() => setSidebarOpen(!sidebarOpen)}
>
{sidebarOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
</Button>
{/* Sidebar */}
<aside
className={cn(
"w-72 border-r bg-muted/30 flex-shrink-0 transition-transform duration-300",
"fixed lg:relative inset-y-0 left-0 z-40 lg:translate-x-0",
sidebarOpen ? "translate-x-0" : "-translate-x-full"
)}
>
<div className="p-4 border-b">
<h2 className="text-lg font-semibold flex items-center gap-2">
<Book className="w-5 h-5" />
Documentation
</h2>
<p className="text-sm text-muted-foreground">Help & Guides</p>
</div>
<div className="h-[calc(100vh-180px)] overflow-y-auto">
<nav className="p-2">
{loading ? (
<div className="p-4 text-sm text-muted-foreground">Loading...</div>
) : sections.length === 0 ? (
<div className="p-4 text-sm text-muted-foreground">No documentation available</div>
) : (
sections.map((section) => (
<div key={section.key} className="mb-2">
<button
onClick={() => toggleSection(section.key)}
className="w-full flex items-center gap-2 px-3 py-2 text-sm font-medium text-foreground hover:bg-muted rounded-md"
>
{iconMap[section.icon] || <FileText className="w-4 h-4" />}
<span className="flex-1 text-left">{section.label}</span>
<ChevronRight
className={cn(
"w-4 h-4 transition-transform",
expandedSections[section.key] && "rotate-90"
)}
/>
</button>
{expandedSections[section.key] && (
<div className="ml-4 mt-1 space-y-1">
{section.items.map((item) => (
<button
key={item.slug}
onClick={() => selectDoc(item.slug)}
className={cn(
"w-full text-left px-3 py-1.5 text-sm rounded-md transition-colors",
isActive(item.slug)
? "bg-primary text-primary-foreground"
: "text-muted-foreground hover:bg-muted hover:text-foreground"
)}
>
{item.title}
</button>
))}
</div>
)}
</div>
))
)}
</nav>
</div>
</aside>
{/* Backdrop for mobile */}
{sidebarOpen && (
<div
className="fixed inset-0 bg-black/50 z-30 lg:hidden"
onClick={() => setSidebarOpen(false)}
/>
)}
{/* Main content */}
<main className="flex-1 overflow-y-auto">
<div className="max-w-4xl mx-auto p-6 lg:p-10">
<DocContent slug={currentSlug} />
</div>
</main>
</div>
);
}

View File

@@ -0,0 +1,31 @@
/**
* Documentation Types
*/
export interface DocItem {
slug: string;
title: string;
}
export interface DocSection {
key: string;
label: string;
icon: string;
items: DocItem[];
}
export interface DocContent {
slug: string;
title: string;
content: string;
}
export interface DocsRegistryResponse {
success: boolean;
sections: DocSection[];
}
export interface DocContentResponse {
success: boolean;
doc: DocContent;
}

View File

@@ -1,6 +1,6 @@
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { useNavigate, Link } from 'react-router-dom'; import { useNavigate, Link } from 'react-router-dom';
import { Tag, Settings as SettingsIcon, Palette, ChevronRight, Minimize2, LogOut, Sun, Moon, Monitor, ExternalLink, Mail, Megaphone } from 'lucide-react'; import { Tag, Settings as SettingsIcon, Palette, ChevronRight, Minimize2, LogOut, Sun, Moon, Monitor, ExternalLink, Mail, Megaphone, HelpCircle } from 'lucide-react';
import { __ } from '@/lib/i18n'; import { __ } from '@/lib/i18n';
import { usePageHeader } from '@/contexts/PageHeaderContext'; import { usePageHeader } from '@/contexts/PageHeaderContext';
import { useApp } from '@/contexts/AppContext'; import { useApp } from '@/contexts/AppContext';
@@ -32,6 +32,12 @@ const menuItems: MenuItem[] = [
label: __('Settings'), label: __('Settings'),
description: __('Configure your store settings'), description: __('Configure your store settings'),
to: '/settings' to: '/settings'
},
{
icon: <HelpCircle className="w-5 h-5" />,
label: __('Help & Docs'),
description: __('Documentation and guides'),
to: '/help'
} }
]; ];

132
docs/_registry.php Normal file
View File

@@ -0,0 +1,132 @@
<?php
/**
* WooNooW Documentation Registry
*
* This file registers all core documentation.
* Addons can extend using the 'woonoow_docs_registry' filter.
*/
namespace WooNooW\Docs;
/**
* Get all registered documentation
*
* @return array Documentation registry
*/
function get_docs_registry() {
$docs_dir = dirname(__FILE__);
// Core WooNooW documentation
$docs = [
'core' => [
'label' => 'WooNooW',
'icon' => 'book-open',
'items' => [
[
'slug' => 'getting-started',
'title' => 'Getting Started',
'file' => $docs_dir . '/getting-started.md',
],
[
'slug' => 'installation',
'title' => 'Installation',
'file' => $docs_dir . '/installation.md',
],
[
'slug' => 'troubleshooting',
'title' => 'Troubleshooting',
'file' => $docs_dir . '/troubleshooting.md',
],
[
'slug' => 'faq',
'title' => 'FAQ',
'file' => $docs_dir . '/faq.md',
],
],
],
'configuration' => [
'label' => 'Configuration',
'icon' => 'settings',
'items' => [
[
'slug' => 'configuration/appearance',
'title' => 'Appearance Settings',
'file' => $docs_dir . '/configuration/appearance.md',
],
[
'slug' => 'configuration/spa-mode',
'title' => 'SPA Mode',
'file' => $docs_dir . '/configuration/spa-mode.md',
],
],
],
'features' => [
'label' => 'Features',
'icon' => 'layers',
'items' => [
[
'slug' => 'features/shop',
'title' => 'Shop Page',
'file' => $docs_dir . '/features/shop.md',
],
[
'slug' => 'features/checkout',
'title' => 'Checkout',
'file' => $docs_dir . '/features/checkout.md',
],
],
],
];
/**
* Filter: woonoow_docs_registry
*
* Allows addons to register their own documentation.
*
* @param array $docs Current documentation registry
* @return array Modified documentation registry
*
* @example
* add_filter('woonoow_docs_registry', function($docs) {
* $docs['my-addon'] = [
* 'label' => 'My Addon',
* 'icon' => 'puzzle',
* 'items' => [
* [
* 'slug' => 'my-addon/getting-started',
* 'title' => 'Getting Started',
* 'file' => __DIR__ . '/docs/getting-started.md',
* ],
* ],
* ];
* return $docs;
* });
*/
return apply_filters('woonoow_docs_registry', $docs);
}
/**
* Get a single documentation item by slug
*
* @param string $slug Document slug
* @return array|null Document data with content, or null if not found
*/
function get_doc_by_slug($slug) {
$registry = get_docs_registry();
foreach ($registry as $section) {
foreach ($section['items'] as $item) {
if ($item['slug'] === $slug) {
// Read file content
if (file_exists($item['file'])) {
$item['content'] = file_get_contents($item['file']);
} else {
$item['content'] = '# Document Not Found\n\nThis document is coming soon.';
}
return $item;
}
}
}
return null;
}

View File

@@ -0,0 +1,133 @@
# Appearance Settings
Customize the look and feel of your WooNooW store.
## Accessing Appearance Settings
Go to **WooNooW → Appearance** in the WordPress admin.
---
## General Settings
### Logo
Upload your store logo for display in the header.
- **Recommended size**: 200x60 pixels (width x height)
- **Formats**: PNG (transparent background recommended), SVG, JPG
- **Mobile**: Automatically resized for smaller screens
### SPA Page
Select which page hosts the WooNooW SPA. Default is "Store".
> **Note**: This page should contain the `[woonoow_spa]` shortcode.
### SPA Mode
Choose how WooNooW handles your store pages:
| Mode | Description |
|------|-------------|
| **Full** | All WooCommerce pages redirect to SPA |
| **Disabled** | Native WooCommerce templates are used |
---
## Colors
### Primary Color
The main brand color used for:
- Buttons
- Links
- Active states
- Primary actions
**Default**: `#6366f1` (Indigo)
### Secondary Color
Secondary UI elements:
- Less prominent buttons
- Borders
- Subtle backgrounds
**Default**: `#64748b` (Slate)
### Accent Color
Highlight color for:
- Sale badges
- Notifications
- Call-to-action elements
**Default**: `#f59e0b` (Amber)
---
## Typography
### Body Font
Font used for general text content.
**Options**: System fonts and Google Fonts
- Inter
- Open Sans
- Roboto
- Lato
- Poppins
- And more...
### Heading Font
Font used for titles and headings.
**Options**: Same as body fonts, plus:
- Cormorant Garamond (Serif option)
- Playfair Display
- Merriweather
### Font Sizes
Font sizes are responsive and adjust automatically based on screen size.
---
## Layout
### Container Width
Maximum width of the content area.
| Option | Width |
|--------|-------|
| Narrow | 1024px |
| Default | 1280px |
| Wide | 1536px |
| Full | 100% |
### Header Style
Configure the header appearance:
- **Fixed**: Stays at top when scrolling
- **Static**: Scrolls with page
### Product Grid
Columns in the shop page grid:
- Mobile: 1-2 columns
- Tablet: 2-3 columns
- Desktop: 3-4 columns
---
## Saving Changes
1. Make your changes
2. Click **Save Changes** button
3. Refresh your store page to see updates
> **Tip**: Open your store in another tab to preview changes quickly.

View File

@@ -0,0 +1,139 @@
# SPA Mode
Understanding and configuring WooNooW's SPA (Single Page Application) mode.
## What is SPA Mode?
SPA Mode controls how WooNooW handles your WooCommerce pages. It determines whether visitors experience the modern SPA interface or traditional WooCommerce templates.
---
## Available Modes
### Full Mode (Recommended)
**All WooCommerce pages redirect to the SPA.**
When a visitor navigates to:
- `/shop` → Redirects to `/store/shop`
- `/product/example` → Redirects to `/store/product/example`
- `/cart` → Redirects to `/store/cart`
- `/checkout` → Redirects to `/store/checkout`
- `/my-account` → Redirects to `/store/my-account`
**Benefits**:
- Instant page transitions
- Modern, consistent UI
- Better mobile experience
- Smooth animations
**Best for**:
- New stores
- Stores wanting a modern look
- Mobile-focused businesses
### Disabled Mode
**WooCommerce uses its native templates.**
WooCommerce pages work normally with your theme's templates. WooNooW admin features still work, but the customer-facing SPA is turned off.
**Benefits**:
- Keep existing theme customizations
- Compatibility with WooCommerce template overrides
- Traditional page-by-page navigation
**Best for**:
- Stores with heavy theme customizations
- Testing before full rollout
- Troubleshooting issues
---
## Switching Modes
### How to Switch
1. Go to **WooNooW → Appearance → General**
2. Find **SPA Mode** setting
3. Select your preferred mode
4. Click **Save Changes**
### What Happens When Switching
**Switching to Full**:
- WooCommerce pages start redirecting
- SPA loads for shop experience
- No data is changed
**Switching to Disabled**:
- Redirects stop immediately
- WooCommerce templates take over
- No data is changed
> **Note**: All your products, orders, and settings remain unchanged when switching modes.
---
## URL Structure
### Full Mode URLs
```
https://yourstore.com/store/ → Home/Shop
https://yourstore.com/store/shop → Shop page
https://yourstore.com/store/product/slug → Product page
https://yourstore.com/store/cart → Cart
https://yourstore.com/store/checkout → Checkout
https://yourstore.com/store/my-account → Account
```
### Disabled Mode URLs
Standard WooCommerce URLs:
```
https://yourstore.com/shop/ → Shop page
https://yourstore.com/product/slug → Product page
https://yourstore.com/cart/ → Cart
https://yourstore.com/checkout/ → Checkout
https://yourstore.com/my-account/ → Account
```
---
## SEO Considerations
### Full Mode SEO
- WooCommerce URLs (`/product/slug`) remain in sitemaps
- When users click from search results, they're redirected to SPA
- Meta tags are generated dynamically for social sharing
- 302 (temporary) redirects preserve link equity
### Disabled Mode SEO
- Standard WooCommerce SEO applies
- No redirects needed
- Works with Yoast SEO, RankMath, etc.
---
## Troubleshooting
### Redirects Not Working
1. **Flush Permalinks**: Go to Settings → Permalinks → Save Changes
2. **Check Store Page**: Ensure the Store page exists and has `[woonoow_spa]`
3. **Clear Cache**: Purge all caching layers
### Blank Pages After Enabling
1. Verify SPA Mode is set to "Full"
2. Clear browser cache
3. Check for JavaScript errors in browser console
### Want to Test Before Enabling
1. Keep mode as "Disabled"
2. Visit `/store/` directly to preview SPA
3. Switch to "Full" when satisfied

149
docs/faq.md Normal file
View File

@@ -0,0 +1,149 @@
# Frequently Asked Questions
Quick answers to common questions about WooNooW.
---
## General
### What is WooNooW?
WooNooW is a WooCommerce plugin that transforms your store into a modern Single Page Application (SPA). It provides instant page loads, a beautiful UI, and seamless shopping experience.
### Do I need WooCommerce?
Yes. WooNooW is an enhancement layer for WooCommerce. You need WooCommerce installed and activated.
### Will WooNooW affect my existing products?
No. WooNooW reads from WooCommerce. Your products, orders, and settings remain untouched.
---
## SPA Mode
### What's the difference between Full and Disabled mode?
| Mode | Behavior |
|------|----------|
| **Full** | All WooCommerce pages redirect to SPA. Modern, fast experience. |
| **Disabled** | WooCommerce pages use native templates. WooNooW admin still works. |
### Can I switch modes anytime?
Yes. Go to **WooNooW → Appearance → General** and change the SPA Mode. Changes take effect immediately.
### Which mode should I use?
- **Full**: For the best customer experience with instant loads
- **Disabled**: If you have theme customizations you want to keep
---
## Compatibility
### Does WooNooW work with my theme?
WooNooW's SPA is independent of your WordPress theme. In Full mode, the SPA uses its own styling. Your theme affects the rest of your site normally.
### Does WooNooW work with page builders?
The SPA pages are self-contained. Page builders work on other pages of your site.
### Which payment gateways are supported?
WooNooW supports all WooCommerce-compatible payment gateways:
- PayPal
- Stripe
- Bank Transfer (BACS)
- Cash on Delivery
- And more...
---
## SEO
### Is WooNooW SEO-friendly?
Yes. WooNooW uses:
- Clean URLs (`/store/product/product-name`)
- Dynamic meta tags for social sharing
- Proper redirects (302) from WooCommerce URLs
### What about my existing SEO?
WooCommerce URLs remain the indexed source. WooNooW redirects users to the SPA but preserves SEO value.
### Will my product pages be indexed?
Yes. Search engines index the WooCommerce URLs. When users click from search results, they're redirected to the fast SPA experience.
---
## Performance
### Is WooNooW faster than regular WooCommerce?
Yes, for navigation. After the initial load, page transitions are instant because the SPA doesn't reload the entire page.
### Will WooNooW slow down my site?
The initial load is similar to regular WooCommerce. Subsequent navigation is much faster.
### Does WooNooW work with caching?
Yes. Use page caching and object caching for best results.
---
## Customization
### Can I customize colors and fonts?
Yes. Go to **WooNooW → Appearance** to customize:
- Primary, secondary, and accent colors
- Body and heading fonts
- Logo and layout options
### Can I add custom CSS?
Currently, use your theme's Additional CSS feature. A custom CSS field may be added in future versions.
### Can I modify the SPA templates?
The SPA is built with React. Advanced customizations require development knowledge.
---
## Addons
### What are WooNooW addons?
Addons extend WooNooW with additional features like loyalty points, advanced analytics, etc.
### How do I install addons?
Addons are installed as separate WordPress plugins. They integrate automatically with WooNooW.
### Do addons work when SPA is disabled?
Most addon features are for the SPA. When disabled, addon functionality may be limited.
---
## Troubleshooting
### I see a blank page. What do I do?
1. Check SPA Mode is set to "Full"
2. Flush permalinks (**Settings → Permalinks → Save**)
3. Clear all caches
4. See [Troubleshooting](troubleshooting) for more
### How do I report a bug?
Contact support with:
- Steps to reproduce the issue
- WordPress/WooCommerce/WooNooW versions
- Any error messages
- Screenshots if applicable

145
docs/features/checkout.md Normal file
View File

@@ -0,0 +1,145 @@
# Checkout
The WooNooW checkout provides a streamlined purchasing experience.
## Overview
The checkout process includes:
1. **Cart Review** - Verify items before checkout
2. **Customer Information** - Billing and shipping details
3. **Payment Method** - Select how to pay
4. **Order Confirmation** - Complete the purchase
---
## Checkout Flow
### Step 1: Cart
Before checkout, customers review their cart:
- Product list with images
- Quantity adjustments
- Remove items
- Apply coupon codes
- See subtotal, shipping, and total
### Step 2: Customer Details
Customers provide:
- **Email address**
- **Billing information**
- Name
- Address
- Phone
- **Shipping address** (if different from billing)
> **Note**: Logged-in customers have their details pre-filled.
### Step 3: Shipping Method
If physical products are in the cart:
- Available shipping methods are shown
- Shipping cost is calculated
- Customer selects preferred method
### Step 4: Payment
Customers choose their payment method:
- Credit/Debit Card (Stripe, PayPal, etc.)
- Bank Transfer
- Cash on Delivery
- Other configured gateways
### Step 5: Place Order
After reviewing everything:
- Click "Place Order"
- Payment is processed
- Confirmation page is shown
- Email receipt is sent
---
## Features
### Guest Checkout
Allow customers to checkout without creating an account.
Configure in **WooCommerce → Settings → Accounts & Privacy**.
### Coupon Codes
Customers can apply discount codes:
1. Enter code in the coupon field
2. Click "Apply"
3. Discount is reflected in total
### Order Notes
Optional field for customers to add special instructions.
---
## Payment Gateways
### Supported Gateways
WooNooW supports all WooCommerce payment gateways:
| Gateway | Type |
|---------|------|
| Bank Transfer (BACS) | Manual |
| Check Payments | Manual |
| Cash on Delivery | Manual |
| PayPal | Card / PayPal |
| Stripe | Card |
| Square | Card |
### Configuring Gateways
1. Go to **WooNooW → Settings → Payments**
2. Enable desired payment methods
3. Configure API keys and settings
4. Test with sandbox/test mode first
---
## After Checkout
### Order Confirmation Page
Shows:
- Order number
- Order summary
- Next steps
### Confirmation Email
Automatically sent to customer with:
- Order details
- Payment confirmation
- Shipping information (if applicable)
---
## Troubleshooting
### "Place Order" Button Not Working
1. Check all required fields are filled
2. Verify payment gateway is properly configured
3. Check browser console for JavaScript errors
### Payment Declined
1. Customer should verify card details
2. Check payment gateway dashboard for error details
3. Ensure correct API keys are configured
### Shipping Not Showing
1. Verify shipping zones are configured in WooCommerce
2. Check if products have weight/dimensions set
3. Confirm customer's address is in a configured zone

96
docs/features/shop.md Normal file
View File

@@ -0,0 +1,96 @@
# Shop Page
The shop page displays your product catalog with browsing and filtering options.
## Overview
The WooNooW shop page provides:
- **Product Grid** - Visual display of products
- **Search** - Find products by name
- **Filters** - Category and sorting options
- **Pagination** - Navigate through products
---
## Features
### Product Cards
Each product displays:
- Product image
- Product name
- Price (with sale price if applicable)
- Add to Cart button
- Wishlist button (if enabled)
### Search
Type in the search box to filter products by name. Search is instant and updates the grid as you type.
### Category Filter
Filter products by category using the dropdown. Shows:
- All Categories
- Individual categories with product count
### Sorting
Sort products by:
- Default sorting
- Popularity
- Average rating
- Latest
- Price: Low to High
- Price: High to Low
---
## Customization
### Grid Layout
Configure the product grid in **WooNooW → Appearance**:
| Device | Options |
|--------|---------|
| Mobile | 1-2 columns |
| Tablet | 2-4 columns |
| Desktop | 2-6 columns |
### Product Card Style
Product cards can display:
- **Image** - Product featured image
- **Title** - Product name
- **Price** - Current price and sale price
- **Rating** - Star rating (if reviews enabled)
- **Add to Cart** - Quick add button
---
## Navigation
### Clicking a Product
Clicking a product card navigates to the full product page where customers can:
- View all images
- Select variations
- Read description
- Add to cart
### Back to Shop
From any product page, use the breadcrumb or browser back button to return to the shop.
---
## Performance
### Lazy Loading
Product images load as they come into view, improving initial page load time.
### Infinite Scroll vs Pagination
Currently uses pagination. Infinite scroll may be added in future versions.

54
docs/getting-started.md Normal file
View File

@@ -0,0 +1,54 @@
# Getting Started with WooNooW
Welcome to WooNooW! This guide will help you get up and running quickly.
## What is WooNooW?
WooNooW transforms your WooCommerce store into a modern, fast Single Page Application (SPA). It provides:
-**Instant Page Loads** - No page refreshes between navigation
- 🎨 **Modern UI** - Beautiful, responsive design out of the box
- 🛠 **Easy Customization** - Configure colors, fonts, and layout from admin
- 📱 **Mobile-First** - Optimized for all devices
## Quick Setup (3 Steps)
### Step 1: Activate the Plugin
After installing WooNooW, activate it from **Plugins → Installed Plugins**.
The plugin will automatically:
- Create a "Store" page for the SPA
- Configure basic settings
### Step 2: Access Admin Dashboard
Go to **WooNooW** in your WordPress admin menu.
You'll see the admin dashboard with:
- Orders management
- Settings configuration
- Appearance customization
### Step 3: Configure Your Store
Navigate to **Appearance** settings to:
1. **Upload your logo**
2. **Set brand colors** (primary, secondary, accent)
3. **Choose fonts** for headings and body text
4. **Configure SPA mode** (Full or Disabled)
## Next Steps
- [Installation Guide](installation) - Detailed installation instructions
- [Appearance Settings](configuration/appearance) - Customize your store's look
- [SPA Mode](configuration/spa-mode) - Understand Full vs Disabled mode
- [Troubleshooting](troubleshooting) - Common issues and solutions
## Need Help?
If you encounter any issues:
1. Check the [Troubleshooting](troubleshooting) guide
2. Review the [FAQ](faq)
3. Contact support with your WordPress and WooCommerce versions

92
docs/installation.md Normal file
View File

@@ -0,0 +1,92 @@
# Installation Guide
This guide covers installing WooNooW on your WordPress site.
## Requirements
Before installing, ensure your site meets these requirements:
| Requirement | Minimum | Recommended |
|-------------|---------|-------------|
| WordPress | 6.0+ | Latest |
| WooCommerce | 7.0+ | Latest |
| PHP | 7.4+ | 8.1+ |
| MySQL | 5.7+ | 8.0+ |
## Installation Methods
### Method 1: WordPress Admin (Recommended)
1. Go to **Plugins → Add New**
2. Click **Upload Plugin**
3. Select the `woonoow.zip` file
4. Click **Install Now**
5. Click **Activate**
### Method 2: FTP Upload
1. Extract `woonoow.zip` to get the `woonoow` folder
2. Upload to `/wp-content/plugins/`
3. Go to **Plugins → Installed Plugins**
4. Find WooNooW and click **Activate**
## Post-Installation
After activation, WooNooW automatically:
### 1. Creates Store Page
A new "Store" page is created with the SPA shortcode. This is your main storefront.
### 2. Registers Rewrite Rules
URL routes like `/store/shop` and `/store/product/...` are registered.
> **Note**: If you see 404 errors, go to **Settings → Permalinks** and click **Save Changes** to flush rewrite rules.
### 3. Sets Default Configuration
Basic appearance settings are configured with sensible defaults.
## Verification Checklist
After installation, verify everything works:
- [ ] Plugin activated without errors
- [ ] WooNooW menu appears in admin sidebar
- [ ] Store page exists (check **Pages**)
- [ ] `/store` URL loads the SPA
- [ ] Products display on shop page
## WooCommerce Compatibility
WooNooW works alongside WooCommerce:
| WooCommerce Page | WooNooW Behavior (Full Mode) |
|------------------|------------------------------|
| `/shop` | Redirects to `/store/shop` |
| `/product/...` | Redirects to `/store/product/...` |
| `/cart` | Redirects to `/store/cart` |
| `/checkout` | Redirects to `/store/checkout` |
| `/my-account` | Redirects to `/store/my-account` |
When SPA Mode is **Disabled**, WooCommerce pages work normally.
## Updating
To update WooNooW:
1. Download the latest version
2. Go to **Plugins → Installed Plugins**
3. Deactivate WooNooW (optional but recommended)
4. Delete the old version
5. Install and activate the new version
Your settings are preserved in the database.
## Uninstalling
To completely remove WooNooW:
1. Deactivate the plugin (restores WooCommerce page content)
2. Delete the plugin
3. (Optional) Delete WooNooW options from database
> **Note**: Deactivating restores original WooCommerce shortcodes to Cart, Checkout, and My Account pages.

173
docs/troubleshooting.md Normal file
View File

@@ -0,0 +1,173 @@
# Troubleshooting
Common issues and their solutions.
## Blank Pages
### Symptom
WooCommerce pages (shop, cart, checkout) show blank content.
### Solutions
**1. Check SPA Mode Setting**
- Go to **WooNooW → Appearance → General**
- Ensure **SPA Mode** is set to "Full"
- If you want native WooCommerce, set to "Disabled"
**2. Flush Permalinks**
- Go to **Settings → Permalinks**
- Click **Save Changes** (no changes needed)
- This refreshes rewrite rules
**3. Clear Cache**
If using a caching plugin:
- Clear page cache
- Clear object cache
- Purge CDN cache (if applicable)
---
## 404 Errors on SPA Routes
### Symptom
Visiting `/store/shop` or `/store/product/...` shows a 404 error.
### Solutions
**1. Flush Permalinks**
- Go to **Settings → Permalinks**
- Click **Save Changes**
**2. Check Store Page Exists**
- Go to **Pages**
- Verify "Store" page exists and is published
- The page should contain `[woonoow_spa]` shortcode
**3. Check SPA Page Setting**
- Go to **WooNooW → Appearance → General**
- Ensure **SPA Page** is set to the Store page
---
## Product Images Not Loading
### Symptom
Products show placeholder images instead of actual images.
### Solutions
**1. Regenerate Thumbnails**
- Install "Regenerate Thumbnails" plugin
- Run regeneration for all images
**2. Check Image URLs**
- Ensure images have valid URLs
- Check for mixed content (HTTP vs HTTPS)
---
## Slow Performance
### Symptom
SPA feels slow or laggy.
### Solutions
**1. Enable Caching**
- Install a caching plugin (WP Super Cache, W3 Total Cache)
- Enable object caching (Redis/Memcached)
**2. Optimize Images**
- Use WebP format
- Compress images before upload
- Use lazy loading
**3. Check Server Resources**
- Upgrade hosting if on shared hosting
- Consider VPS or managed WordPress hosting
---
## Checkout Not Working
### Symptom
Checkout page won't load or payment fails.
### Solutions
**1. Check Payment Gateway**
- Go to **WooCommerce → Settings → Payments**
- Verify payment method is enabled
- Check API credentials
**2. Check SSL Certificate**
- Checkout requires HTTPS
- Verify SSL is properly installed
**3. Check for JavaScript Errors**
- Open browser Developer Tools (F12)
- Check Console for errors
- Look for blocked scripts
---
## Emails Not Sending
### Symptom
Order confirmation emails not being received.
### Solutions
**1. Check Email Settings**
- Go to **WooNooW → Settings → Notifications**
- Verify email types are enabled
**2. Check WordPress Email**
- Test with a plugin like "Check & Log Email"
- Consider using SMTP plugin (WP Mail SMTP)
**3. Check Spam Folder**
- Emails may be in recipient's spam folder
- Add sender to whitelist
---
## Plugin Conflicts
### Symptom
WooNooW doesn't work after installing another plugin.
### Steps to Diagnose
1. **Deactivate other plugins** one by one
2. **Switch to default theme** (Twenty Twenty-Three)
3. **Check error logs** in `wp-content/debug.log`
### Common Conflicting Plugins
- Other WooCommerce template overrides
- Page builder plugins (sometimes)
- Heavy caching plugins (misconfigured)
---
## Getting More Help
If you can't resolve the issue:
1. **Collect Information**
- WordPress version
- WooCommerce version
- WooNooW version
- PHP version
- Error messages (from debug.log)
2. **Enable Debug Mode**
Add to `wp-config.php`:
```php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
```
3. **Contact Support**
Provide the collected information for faster resolution.

View File

@@ -0,0 +1,129 @@
<?php
/**
* Documentation API Controller
*
* Serves documentation content to the Admin SPA.
*/
namespace WooNooW\Api;
use WP_REST_Controller;
use WP_REST_Response;
use WP_REST_Request;
use WP_Error;
class DocsController extends WP_REST_Controller {
/**
* Namespace for REST routes
*/
protected $namespace = 'woonoow/v1';
/**
* Base route
*/
protected $rest_base = 'docs';
/**
* Register routes
*/
public function register_routes() {
// GET /woonoow/v1/docs - List all documentation
register_rest_route($this->namespace, '/' . $this->rest_base, [
[
'methods' => 'GET',
'callback' => [$this, 'get_docs_registry'],
'permission_callback' => [$this, 'check_permissions'],
],
]);
// GET /woonoow/v1/docs/{slug} - Get single document
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<slug>.+)', [
[
'methods' => 'GET',
'callback' => [$this, 'get_doc'],
'permission_callback' => [$this, 'check_permissions'],
'args' => [
'slug' => [
'required' => true,
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
],
],
],
]);
}
/**
* Check permissions - any logged in admin user
*/
public function check_permissions($request) {
return current_user_can('manage_options');
}
/**
* Get documentation registry
*
* @return WP_REST_Response
*/
public function get_docs_registry($request) {
require_once WOONOOW_PLUGIN_DIR . 'docs/_registry.php';
$registry = \WooNooW\Docs\get_docs_registry();
// Transform to frontend format (without file paths)
$result = [];
foreach ($registry as $section_key => $section) {
$items = [];
foreach ($section['items'] as $item) {
$items[] = [
'slug' => $item['slug'],
'title' => $item['title'],
];
}
$result[] = [
'key' => $section_key,
'label' => $section['label'],
'icon' => $section['icon'] ?? 'file-text',
'items' => $items,
];
}
return new WP_REST_Response([
'success' => true,
'sections' => $result,
], 200);
}
/**
* Get single document content
*
* @param WP_REST_Request $request
* @return WP_REST_Response|WP_Error
*/
public function get_doc($request) {
$slug = $request->get_param('slug');
require_once WOONOOW_PLUGIN_DIR . 'docs/_registry.php';
$doc = \WooNooW\Docs\get_doc_by_slug($slug);
if (!$doc) {
return new WP_Error(
'doc_not_found',
'Documentation not found',
['status' => 404]
);
}
return new WP_REST_Response([
'success' => true,
'doc' => [
'slug' => $doc['slug'],
'title' => $doc['title'],
'content' => $doc['content'],
],
], 200);
}
}

View File

@@ -24,6 +24,7 @@ use WooNooW\Api\NewsletterController;
use WooNooW\Api\ModulesController; use WooNooW\Api\ModulesController;
use WooNooW\Api\ModuleSettingsController; use WooNooW\Api\ModuleSettingsController;
use WooNooW\Api\CampaignsController; use WooNooW\Api\CampaignsController;
use WooNooW\Api\DocsController;
use WooNooW\Frontend\ShopController; use WooNooW\Frontend\ShopController;
use WooNooW\Frontend\CartController as FrontendCartController; use WooNooW\Frontend\CartController as FrontendCartController;
use WooNooW\Frontend\AccountController; use WooNooW\Frontend\AccountController;
@@ -165,6 +166,10 @@ class Routes {
$module_settings_controller = new ModuleSettingsController(); $module_settings_controller = new ModuleSettingsController();
$module_settings_controller->register_routes(); $module_settings_controller->register_routes();
// Documentation controller
$docs_controller = new DocsController();
$docs_controller->register_routes();
// Frontend controllers (customer-facing) // Frontend controllers (customer-facing)
ShopController::register_routes(); ShopController::register_routes();
FrontendCartController::register_routes(); FrontendCartController::register_routes();