feat: Enhance Store Details with branding features
## 1. Architecture Decisions ✅ Created two comprehensive documents: ### A. ARCHITECTURE_DECISION_CUSTOMER_SPA.md **Decision: Hybrid Approach (Option C)** **WooNooW Plugin ($149/year):** - Admin-SPA (full featured) ✅ - Customer-SPA (basic cart/checkout/account) ✅ - Shortcode mode (works with any theme) ✅ - Full SPA mode (optional) ✅ **Premium Themes ($79/year each):** - Enhanced customer-spa components - Industry-specific designs - Optional upsell **Revenue Analysis:** - Option A (Core): $149K/year - Option B (Separate): $137K/year - **Option C (Hybrid): $164K/year** ✅ Winner! **Benefits:** - 60% users get complete solution - 30% agencies can customize - 10% enterprise have flexibility - Higher revenue potential - Better market positioning ### B. ADDON_REACT_INTEGRATION.md **Clarified addon development approach** **Level 1: Vanilla JS** (No build) - Simple addons use window.WooNooW API - No build process needed - Easy for PHP developers **Level 2: Exposed React** (Recommended) - WooNooW exposes React on window - Addons can use React without bundling it - Build with external React - Best of both worlds **Level 3: Slot-Based** (Advanced) - Full React component integration - Type safety - Modern DX **Implementation:** ```typescript window.WooNooW = { React: React, ReactDOM: ReactDOM, hooks: { addFilter, addAction }, components: { Button, Input, Select }, utils: { api, toast }, }; ``` --- ## 2. Enhanced Store Details Page ✅ ### New Components Created: **A. ImageUpload Component** - Drag & drop support - WordPress media library integration - File validation (type, size) - Preview with remove button - Loading states **B. ColorPicker Component** - Native color picker - Hex input with validation - Preset colors - Live preview - Popover UI ### Store Details Enhancements: **Added to Store Identity Card:** - ✅ Store tagline input - ✅ Store logo upload (2MB max) - ✅ Store icon upload (1MB max) **New Brand Colors Card:** - ✅ Primary color picker - ✅ Accent color picker - ✅ Error color picker - ✅ Reset to default button - ✅ Live preview **Features:** - All branding in one place - No separate Brand & Appearance tab needed - Clean, professional UI - Easy to use - Industry standard --- ## Summary **Architecture:** - ✅ Customer-SPA in core (hybrid approach) - ✅ Addon React integration clarified - ✅ Revenue model optimized **Implementation:** - ✅ ImageUpload component - ✅ ColorPicker component - ✅ Enhanced Store Details page - ✅ Branding features integrated **Result:** - Clean, focused settings - Professional branding tools - Better revenue potential - Clear development path
This commit is contained in:
@@ -7,6 +7,9 @@ import { SettingsSection } from './components/SettingsSection';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { SearchableSelect } from '@/components/ui/searchable-select';
|
||||
import { ImageUpload } from '@/components/ui/image-upload';
|
||||
import { ColorPicker } from '@/components/ui/color-picker';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { toast } from 'sonner';
|
||||
import flagsData from '@/data/flags.json';
|
||||
|
||||
@@ -38,6 +41,13 @@ interface StoreSettings {
|
||||
timezone: string;
|
||||
weightUnit: string;
|
||||
dimensionUnit: string;
|
||||
// Branding
|
||||
storeLogo: string;
|
||||
storeIcon: string;
|
||||
storeTagline: string;
|
||||
primaryColor: string;
|
||||
accentColor: string;
|
||||
errorColor: string;
|
||||
}
|
||||
|
||||
export default function StoreDetailsPage() {
|
||||
@@ -60,6 +70,12 @@ export default function StoreDetailsPage() {
|
||||
timezone: 'Asia/Jakarta',
|
||||
weightUnit: 'kg',
|
||||
dimensionUnit: 'cm',
|
||||
storeLogo: '',
|
||||
storeIcon: '',
|
||||
storeTagline: '',
|
||||
primaryColor: '#3b82f6',
|
||||
accentColor: '#10b981',
|
||||
errorColor: '#ef4444',
|
||||
});
|
||||
|
||||
// Fetch store settings
|
||||
@@ -110,6 +126,12 @@ export default function StoreDetailsPage() {
|
||||
timezone: storeData.timezone || 'Asia/Jakarta',
|
||||
weightUnit: storeData.weight_unit || 'kg',
|
||||
dimensionUnit: storeData.dimension_unit || 'cm',
|
||||
storeLogo: storeData.store_logo || '',
|
||||
storeIcon: storeData.store_icon || '',
|
||||
storeTagline: storeData.store_tagline || '',
|
||||
primaryColor: storeData.primary_color || '#3b82f6',
|
||||
accentColor: storeData.accent_color || '#10b981',
|
||||
errorColor: storeData.error_color || '#ef4444',
|
||||
};
|
||||
}, [storeData]);
|
||||
|
||||
@@ -140,6 +162,12 @@ export default function StoreDetailsPage() {
|
||||
timezone: data.timezone,
|
||||
weight_unit: data.weightUnit,
|
||||
dimension_unit: data.dimensionUnit,
|
||||
store_logo: data.storeLogo,
|
||||
store_icon: data.storeIcon,
|
||||
store_tagline: data.storeTagline,
|
||||
primary_color: data.primaryColor,
|
||||
accent_color: data.accentColor,
|
||||
error_color: data.errorColor,
|
||||
}),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['store-settings'] });
|
||||
@@ -250,6 +278,85 @@ export default function StoreDetailsPage() {
|
||||
placeholder="+62 812 3456 7890"
|
||||
/>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection
|
||||
label="Store tagline"
|
||||
description="A short tagline or slogan for your store"
|
||||
htmlFor="storeTagline"
|
||||
>
|
||||
<Input
|
||||
id="storeTagline"
|
||||
value={settings.storeTagline}
|
||||
onChange={(e) => updateSetting('storeTagline', e.target.value)}
|
||||
placeholder="Quality products, delivered fast"
|
||||
/>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection label="Store logo" description="Recommended: 200x60px PNG with transparent background">
|
||||
<ImageUpload
|
||||
value={settings.storeLogo}
|
||||
onChange={(url) => updateSetting('storeLogo', url)}
|
||||
onRemove={() => updateSetting('storeLogo', '')}
|
||||
maxSize={2}
|
||||
/>
|
||||
</SettingsSection>
|
||||
|
||||
<SettingsSection label="Store icon" description="Favicon for browser tabs (32x32px)">
|
||||
<ImageUpload
|
||||
value={settings.storeIcon}
|
||||
onChange={(url) => updateSetting('storeIcon', url)}
|
||||
onRemove={() => updateSetting('storeIcon', '')}
|
||||
maxSize={1}
|
||||
/>
|
||||
</SettingsSection>
|
||||
</SettingsCard>
|
||||
|
||||
{/* Brand Colors */}
|
||||
<SettingsCard
|
||||
title="Brand Colors"
|
||||
description="Customize your admin interface colors"
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<ColorPicker
|
||||
label="Primary Color"
|
||||
description="Main brand color"
|
||||
value={settings.primaryColor}
|
||||
onChange={(color) => updateSetting('primaryColor', color)}
|
||||
/>
|
||||
|
||||
<ColorPicker
|
||||
label="Accent Color"
|
||||
description="Success and highlights"
|
||||
value={settings.accentColor}
|
||||
onChange={(color) => updateSetting('accentColor', color)}
|
||||
/>
|
||||
|
||||
<ColorPicker
|
||||
label="Error Color"
|
||||
description="Errors and warnings"
|
||||
value={settings.errorColor}
|
||||
onChange={(color) => updateSetting('errorColor', color)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 mt-4 pt-4 border-t">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
updateSetting('primaryColor', '#3b82f6');
|
||||
updateSetting('accentColor', '#10b981');
|
||||
updateSetting('errorColor', '#ef4444');
|
||||
toast.success('Colors reset to default');
|
||||
}}
|
||||
>
|
||||
Reset to Default
|
||||
</Button>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Changes apply after saving
|
||||
</p>
|
||||
</div>
|
||||
</SettingsCard>
|
||||
|
||||
{/* Store Address */}
|
||||
|
||||
Reference in New Issue
Block a user