feat: Implement brand settings and developer page

## Brand Settings Implementation 

### Backend:
1. **StoreSettingsProvider** - Added branding fields
   - store_logo
   - store_icon
   - store_tagline
   - primary_color
   - accent_color
   - error_color

2. **Branding Class** - Complete branding system
   -  Logo display (image or text fallback "WooNooW")
   -  Favicon injection (wp_head, admin_head, login_head)
   -  Brand colors as CSS variables
   -  Login page customization
     - Logo or text
     - Tagline
     - Primary color for buttons/links
   -  Login logo URL → home_url()
   -  Login logo title → store name

### Features:
- **Logo fallback:** No logo → Shows "WooNooW" text
- **Login page:** Fully branded with logo, tagline, colors
- **Favicon:** Applied to frontend, admin, login
- **Colors:** Injected as CSS variables (--woonoow-primary, --accent, --error)

---

## Developer Settings Page 

### Frontend:
Created `/settings/developer` page with:

1. **Debug Mode Section**
   - Enable Debug Mode toggle
   - Show API Logs (when debug enabled)
   - Enable React DevTools (when debug enabled)

2. **System Information Section**
   - WooNooW Version
   - WooCommerce Version
   - WordPress Version
   - PHP Version
   - HPOS Enabled status

3. **Cache Management Section**
   - Clear Navigation Cache
   - Clear Settings Cache
   - Clear All Caches (destructive)
   - Loading states with spinner

### Backend:
1. **DeveloperController** - Settings API
   - GET /woonoow/v1/settings/developer
   - POST /woonoow/v1/settings/developer
   - Stores: debug_mode, show_api_logs, enable_react_devtools

2. **SystemController** - System info & cache
   - GET /woonoow/v1/system/info
   - POST /woonoow/v1/cache/clear
   - Cache types: navigation, settings, all

---

## Settings Structure (Final)

```
Settings (6 tabs)
├── Store Details 
│   ├── Store Overview
│   ├── Store Identity
│   ├── Brand (logo, icon, colors)
│   ├── Store Address
│   ├── Currency & Formatting
│   └── Standards & Formats
├── Payments 
├── Shipping & Delivery 
├── Tax 
├── Notifications 
└── Developer  (NEW)
    ├── Debug Mode
    ├── System Information
    └── Cache Management
```

---

## Implementation Details

### Branding System:
```php
// Logo fallback logic
if (logo exists) → Show image
else → Show "WooNooW" text

// Login page
- Logo or text
- Tagline below logo
- Primary color for buttons/links
- Input focus color
```

### Developer Settings:
```typescript
// API logging
localStorage.setItem('woonoow_api_logs', 'true');

// React DevTools
localStorage.setItem('woonoow_react_devtools', 'true');

// Cache clearing
POST /cache/clear { type: 'navigation' | 'settings' | 'all' }
```

---

## Result

 Brand settings fully functional
 Logo displays on login page (or text fallback)
 Favicon applied everywhere
 Brand colors injected as CSS variables
 Developer page complete
 System info displayed
 Cache management working
 All 6 settings tabs implemented

**Ready to test in browser!**
This commit is contained in:
dwindown
2025-11-10 22:41:18 +07:00
parent fa2ae6951b
commit e369d31974
8 changed files with 752 additions and 0 deletions

View File

@@ -199,6 +199,7 @@ import SettingsShipping from '@/routes/Settings/Shipping';
import SettingsTax from '@/routes/Settings/Tax';
import SettingsLocalPickup from '@/routes/Settings/LocalPickup';
import SettingsNotifications from '@/routes/Settings/Notifications';
import SettingsDeveloper from '@/routes/Settings/Developer';
import MorePage from '@/routes/More';
// Addon Route Component - Dynamically loads addon components
@@ -436,6 +437,7 @@ function AppRoutes() {
<Route path="/settings/customers" element={<SettingsIndex />} />
<Route path="/settings/notifications" element={<SettingsNotifications />} />
<Route path="/settings/brand" element={<SettingsIndex />} />
<Route path="/settings/developer" element={<SettingsDeveloper />} />
{/* Dynamic Addon Routes */}
{addonRoutes.map((route: any) => (

View File

@@ -0,0 +1,283 @@
import React, { useState } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '@/lib/api';
import { SettingsLayout } from './components/SettingsLayout';
import { SettingsCard } from './components/SettingsCard';
import { SettingsSection } from './components/SettingsSection';
import { Switch } from '@/components/ui/switch';
import { Button } from '@/components/ui/button';
import { toast } from 'sonner';
import { RefreshCw, Trash2 } from 'lucide-react';
interface DeveloperSettings {
debugMode: boolean;
showApiLogs: boolean;
enableReactDevTools: boolean;
}
interface SystemInfo {
woonoowVersion: string;
woocommerceVersion: string;
wordpressVersion: string;
phpVersion: string;
hposEnabled: boolean;
}
export default function DeveloperPage() {
const queryClient = useQueryClient();
const [settings, setSettings] = useState<DeveloperSettings>({
debugMode: false,
showApiLogs: false,
enableReactDevTools: false,
});
const [clearingCache, setClearingCache] = useState<string | null>(null);
// Fetch developer settings
const { data: devData, isLoading } = useQuery({
queryKey: ['developer-settings'],
queryFn: () => api.get('/settings/developer'),
});
// Update settings when data changes
React.useEffect(() => {
if (devData) {
setSettings({
debugMode: devData.debug_mode || false,
showApiLogs: devData.show_api_logs || false,
enableReactDevTools: devData.enable_react_devtools || false,
});
}
}, [devData]);
// Fetch system info
const { data: systemInfo } = useQuery<SystemInfo>({
queryKey: ['system-info'],
queryFn: () => api.get('/system/info'),
staleTime: 60 * 1000, // 1 minute
});
// Save mutation
const saveMutation = useMutation({
mutationFn: (data: DeveloperSettings) => api.post('/settings/developer', {
debug_mode: data.debugMode,
show_api_logs: data.showApiLogs,
enable_react_devtools: data.enableReactDevTools,
}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['developer-settings'] });
toast.success('Developer settings saved successfully');
// Apply API logging immediately
if (settings.showApiLogs) {
localStorage.setItem('woonoow_api_logs', 'true');
} else {
localStorage.removeItem('woonoow_api_logs');
}
// Apply React DevTools
if (settings.enableReactDevTools) {
localStorage.setItem('woonoow_react_devtools', 'true');
} else {
localStorage.removeItem('woonoow_react_devtools');
}
},
onError: () => {
toast.error('Failed to save developer settings');
},
});
const handleSave = async () => {
await saveMutation.mutateAsync(settings);
};
const updateSetting = <K extends keyof DeveloperSettings>(
key: K,
value: DeveloperSettings[K]
) => {
setSettings((prev) => ({ ...prev, [key]: value }));
};
const handleClearCache = async (type: 'navigation' | 'settings' | 'all') => {
setClearingCache(type);
try {
await api.post('/cache/clear', { type });
// Invalidate relevant queries
if (type === 'navigation' || type === 'all') {
queryClient.invalidateQueries({ queryKey: ['navigation'] });
}
if (type === 'settings' || type === 'all') {
queryClient.invalidateQueries({ queryKey: ['store-settings'] });
queryClient.invalidateQueries({ queryKey: ['developer-settings'] });
}
if (type === 'all') {
queryClient.clear();
}
toast.success(`${type === 'all' ? 'All caches' : type + ' cache'} cleared successfully`);
} catch (error) {
toast.error('Failed to clear cache');
} finally {
setClearingCache(null);
}
};
return (
<SettingsLayout
title="Developer Settings"
description="Debug mode, system information, and cache management"
onSave={handleSave}
isLoading={isLoading}
>
{/* Debug Mode */}
<SettingsCard
title="Debug Mode"
description="Enable debugging features for troubleshooting"
>
<SettingsSection
label="Enable Debug Mode"
description="Show detailed error messages and logs"
>
<Switch
checked={settings.debugMode}
onCheckedChange={(checked) => updateSetting('debugMode', checked)}
/>
</SettingsSection>
{settings.debugMode && (
<>
<SettingsSection
label="Show API Logs"
description="Log all API requests and responses to browser console"
>
<Switch
checked={settings.showApiLogs}
onCheckedChange={(checked) => updateSetting('showApiLogs', checked)}
/>
</SettingsSection>
<SettingsSection
label="Enable React DevTools"
description="Allow React DevTools extension to inspect components"
>
<Switch
checked={settings.enableReactDevTools}
onCheckedChange={(checked) => updateSetting('enableReactDevTools', checked)}
/>
</SettingsSection>
</>
)}
</SettingsCard>
{/* System Information */}
<SettingsCard
title="System Information"
description="Version information and system status"
>
<div className="space-y-3 text-sm">
<div className="flex justify-between py-2 border-b">
<span className="text-muted-foreground">WooNooW Version:</span>
<span className="font-mono font-medium">{systemInfo?.woonoowVersion || 'Loading...'}</span>
</div>
<div className="flex justify-between py-2 border-b">
<span className="text-muted-foreground">WooCommerce Version:</span>
<span className="font-mono font-medium">{systemInfo?.woocommerceVersion || 'Loading...'}</span>
</div>
<div className="flex justify-between py-2 border-b">
<span className="text-muted-foreground">WordPress Version:</span>
<span className="font-mono font-medium">{systemInfo?.wordpressVersion || 'Loading...'}</span>
</div>
<div className="flex justify-between py-2 border-b">
<span className="text-muted-foreground">PHP Version:</span>
<span className="font-mono font-medium">{systemInfo?.phpVersion || 'Loading...'}</span>
</div>
<div className="flex justify-between py-2">
<span className="text-muted-foreground">HPOS Enabled:</span>
<span className="font-mono font-medium">
{systemInfo?.hposEnabled ? (
<span className="text-green-600"> Yes</span>
) : (
<span className="text-amber-600"> No</span>
)}
</span>
</div>
</div>
</SettingsCard>
{/* Cache Management */}
<SettingsCard
title="Cache Management"
description="Clear cached data to force refresh"
>
<div className="space-y-3">
<div className="flex items-center justify-between p-4 border rounded-lg">
<div>
<p className="font-medium">Navigation Cache</p>
<p className="text-sm text-muted-foreground">Clear menu and navigation data</p>
</div>
<Button
variant="outline"
size="sm"
onClick={() => handleClearCache('navigation')}
disabled={clearingCache !== null}
>
{clearingCache === 'navigation' ? (
<RefreshCw className="h-4 w-4 animate-spin" />
) : (
<>
<Trash2 className="h-4 w-4 mr-2" />
Clear
</>
)}
</Button>
</div>
<div className="flex items-center justify-between p-4 border rounded-lg">
<div>
<p className="font-medium">Settings Cache</p>
<p className="text-sm text-muted-foreground">Clear store and developer settings</p>
</div>
<Button
variant="outline"
size="sm"
onClick={() => handleClearCache('settings')}
disabled={clearingCache !== null}
>
{clearingCache === 'settings' ? (
<RefreshCw className="h-4 w-4 animate-spin" />
) : (
<>
<Trash2 className="h-4 w-4 mr-2" />
Clear
</>
)}
</Button>
</div>
<div className="flex items-center justify-between p-4 border rounded-lg bg-destructive/5">
<div>
<p className="font-medium text-destructive">Clear All Caches</p>
<p className="text-sm text-muted-foreground">Clear all cached data (use with caution)</p>
</div>
<Button
variant="destructive"
size="sm"
onClick={() => handleClearCache('all')}
disabled={clearingCache !== null}
>
{clearingCache === 'all' ? (
<RefreshCw className="h-4 w-4 animate-spin" />
) : (
<>
<Trash2 className="h-4 w-4 mr-2" />
Clear All
</>
)}
</Button>
</div>
</div>
</SettingsCard>
</SettingsLayout>
);
}