Added getAttrs functions to parseHTML in tiptap-button-extension.ts.
Now properly extracts text/href/style from DOM elements:
- data-button: extracts from data-text, data-href, data-style
- a.button: extracts text/href, defaults to solid style
- a.button-outline: extracts text/href, defaults to outline style
This fixes the issue where buttons appeared unstyled (outline
instead of solid) when editing a card that contained buttons.
Added data-button attribute selector to TipTap button parseHTML.
This ensures buttons are properly detected when text alignment is
applied, as alignment may affect CSS class detection.
Priority order:
1. a[data-button] - most reliable
2. a.button
3. a.button-outline
Button modals in both RichTextEditor and EmailBuilder filtered
for _url variables only, excluding reset_link. Updated filter to
include both _url and _link patterns.
Files changed:
- rich-text-editor.tsx line 415
- EmailBuilder.tsx line 359
ROOT CAUSE: Complete flow trace revealed syntax mismatch:
- blocksToMarkdown outputs NEW syntax: [card:type], [button:style](url)Text[/button]
- markdownToBlocks ONLY parsed OLD syntax: [card type="..."], [button url="..."]
This caused buttons/cards to be lost when:
1. User adds button in Visual mode
2. blocksToMarkdown converts to [button:solid]({url})Text[/button]
3. handleBlocksChange stores this in markdownContent
4. When switching tabs/previewing, markdownToBlocks runs
5. It FAILED to parse new syntax, buttons disappear!
FIX: Added handlers for NEW syntax in markdownToBlocks (converter.ts):
- [card:type]...[/card] pattern (before old syntax)
- [button:style](url)Text[/button] pattern (before old syntax)
Now both syntaxes work correctly in round-trip conversion.
1. Added missing base variables in get_variables():
- site_name, site_title, store_name
- shop_url, my_account_url
- support_email, current_year
2. Fixed social icon URL path calculation:
- Was using 3x dirname which pointed to 'includes/' not plugin root
- Now uses WOONOOW_URL constant or correct 4x dirname
3. Added px-6 padding to EmailBuilder dialog body
4. Added portal container to Select component for CSS scoping
1. Dialog Portal: Render inside #woonoow-admin-app container instead
of document.body to fix Tailwind CSS scoping in WordPress admin
2. Variables Panel: Redesigned from flat list to collapsible accordion
- Collapsed by default (less visual noise)
- Categorized: Order (blue), Customer (green), Shipping (orange), Store (purple)
- Color-coded pills for quick recognition
- Shows count of available variables
3. StarterKit: Disable built-in Link to prevent duplicate extension warning
The RichTextEditor useEffect was comparing raw content with editor HTML,
but they differed due to whitespace normalization (e.g., '\n\n' vs '').
This caused continuous setContent calls, freezing the edit dialog.
Fixed by normalizing whitespace in both strings before comparison.
StarterKit 3.10+ now includes Link by default. Our code was adding
Link.configure() separately, causing duplicate extension warning and
breaking the email builder visual editor modal.
Fixed by configuring StarterKit with { link: false } so our custom
Link.configure() with specific options is the only Link extension.
1. EmailBuilder: Fixed dialog handlers to not block all interactions
- Previously dialog prevented all outside clicks
- Now only blocks when WP media modal is open
- Dialog can be properly closed via escape or outside click
2. DefaultTemplates: Updated new_customer email
- Added note about using 'Forgot Password?' if link expires
- Clear instructions for users
Issue 1 - Dashboard > Overview Never Active:
Added debug logging to investigate why Overview submenu never shows active
- Console logs path, pathname, and isActive state
- Will help identify the root cause
Issue 2 - Guest Wishlist Public Page:
Problem: Guests couldn't access wishlist (redirected to login)
Solution: Created public /wishlist route accessible to all users
Implementation:
1. New Public Wishlist Page:
- Route: /wishlist (not /my-account/wishlist)
- Accessible to guests and logged-in users
- Guest mode: Shows product IDs from localStorage
- Logged-in mode: Shows full product details from API
- Guests can view and remove items
2. Updated All Header Links:
- ClassicLayout: /wishlist
- ModernLayout: /wishlist
- BoutiqueLayout: /wishlist
- No more wp-login redirect for guests
3. Guest Experience:
- See list of wishlisted product IDs
- Click to view product details
- Remove items from wishlist
- Prompt to login for full details
Issue 3 - Wishlist Page Selector Setting:
Status: Deprecated/unused for SPA architecture
- SPA uses React Router, not WordPress pages
- Setting saved but has no effect
- Shareable wishlist would also be SPA route
- No need for page CPT selection
Files Modified:
- customer-spa/src/pages/Wishlist.tsx (new public page)
- customer-spa/src/App.tsx (added /wishlist route)
- customer-spa/src/hooks/useWishlist.ts (export productIds)
- customer-spa/src/layouts/BaseLayout.tsx (all themes use /wishlist)
- customer-spa/dist/app.js (rebuilt)
- admin-spa/src/components/nav/SubmenuBar.tsx (debug logging)
- admin-spa/dist/app.js (rebuilt)
Result:
✅ Guests can access wishlist page
✅ Guests can view and manage localStorage wishlist
✅ No login redirect for guest wishlist
✅ Debug logging added for Overview issue
Problem: ALL submenu items showed active at once (screenshot evidence)
Root Cause: Complex logic with exact flag and startsWith() was broken
- startsWith logic caused multiple matches
- exact flag handling was inconsistent
Solution: Simplified to exact pathname match ONLY
- isActive = it.path === pathname
- No more startsWith, no more exact flag complexity
- One pathname = one active submenu item
Result: Only the current submenu item shows active ✅
Files Modified:
- admin-spa/src/components/nav/SubmenuBar.tsx (simplified logic)
- admin-spa/dist/app.js (rebuilt)
Submenu Active State Fix:
Problem: Dashboard Overview never active, All Orders/Products/Customers always active
Root Cause: Submenu path matching didn't respect 'exact' flag from backend
Solution: Added proper exact flag handling in SubmenuBar component
- If item.exact = true: only match exact path (for Overview at /dashboard)
- If item.exact = false/undefined: match path + sub-paths (for All Orders at /orders)
Result: Submenu items now show correct active state ✅
Guest Wishlist Frontend Fix:
Problem: Guest wishlist enabled in backend but frontend blocked with login prompt
Root Cause: useWishlist hook had frontend login checks before API calls
Solution: Removed frontend login checks from addToWishlist and removeFromWishlist
- Backend already enforces permission via check_permission() based on enable_guest_wishlist
- Frontend now lets backend handle authorization
- API returns proper error if guest wishlist disabled
Result: Guests can add/remove wishlist items when setting enabled ✅
Files Modified (2):
- admin-spa/src/components/nav/SubmenuBar.tsx (exact flag handling)
- customer-spa/src/hooks/useWishlist.ts (removed login checks)
- admin-spa/dist/app.js + customer-spa/dist/app.js (rebuilt)
Both submenu and guest wishlist issues resolved!
- Created LayoutWrapper component to conditionally render header/footer based on route
- Created MinimalHeader component (logo only)
- Created MinimalFooter component (trust badges + policy links)
- Created usePageVisibility hook to get visibility settings per page
- Wrapped ClassicLayout with LayoutWrapper for conditional rendering
- Header/footer visibility now controlled directly in React SPA
- Settings: show/minimal/hide for both header and footer
- Background color support for checkout and thankyou pages
Issue: Blank form when tabs change dynamically
Problem:
- When product type changes (simple → variable)
- Tabs array changes (adds/removes variations tab)
- activeTab state still points to old tab ID
- If old tab ID doesn't exist, no section shows
- Result: Blank form
Fix:
- Added useEffect to watch tabs array
- Check if current activeTab exists in new tabs
- If not, reset to first tab (tabs[0].id)
- Ensures valid activeTab always
Example:
- Initial: tabs = [general, inventory, organization]
- activeTab = 'general' ✅
- Type changes to variable
- New tabs = [general, inventory, variations, organization]
- activeTab still 'general' ✅ (exists, no change)
- But if activeTab was 'variations' and type changed to simple
- Old activeTab 'variations' doesn't exist
- Reset to 'general' ✅
Result:
✅ Form always shows active section
✅ Handles dynamic tab changes
✅ No blank forms
Critical bug: Tab buttons were submitting the form
Problem:
- Buttons inside <form> default to type="submit"
- Clicking any tab triggered form submission
- Form would submit instead of switching tabs
- Very disruptive UX
Fix:
- Added type="button" to all tab buttons
- Mobile horizontal tabs
- Desktop vertical tabs
- Now tabs only switch sections, no submit
Changes:
1. Mobile tab buttons: type="button"
2. Desktop tab buttons: type="button"
Result:
✅ Tabs switch sections without submitting
✅ Form only submits via submit button
✅ Proper form behavior
Root cause: Wrong prop check
- Was checking: child.props['data-section-id']
- Should check: child.props.id
Why this matters:
- FormSection receives 'id' as a React prop
- 'data-section-id' is only a DOM attribute
- React.Children.map sees React props, not DOM attributes
- So child.props['data-section-id'] was always undefined
- Condition never matched, no hidden class applied
- All sections stayed visible
Fix:
- Check child.props.id instead
- Cast to string for type safety
- Now condition matches correctly
- Hidden class applied to inactive sections
Result:
✅ Only active section visible
✅ Works on desktop and mobile
✅ Simple one-line fix per location
Fixed two critical issues with VerticalTabForm:
Issue #1: All sections showing at once
- Problem: className override was removing original classes
- Fix: Preserve originalClassName and append 'hidden' when inactive
- Now only active section is visible
- Inactive sections get 'hidden' class added
Issue #2: No horizontal tabs on mobile
- Added mobile horizontal tabs (lg:hidden)
- Scrollable tab bar with overflow-x-auto
- Active tab highlighted with bg-primary
- Icons + labels for each tab
- Separate mobile content area
Changes to VerticalTabForm.tsx:
1. Fixed className merging logic
- Get originalClassName from child.props
- Active: use originalClassName as-is
- Inactive: append ' hidden' to originalClassName
- Prevents className override issue
2. Added mobile layout
- Horizontal tabs at top (lg:hidden)
- Flex with gap-2, overflow-x-auto
- flex-shrink-0 prevents tab squishing
- Active state: bg-primary text-primary-foreground
- Inactive state: bg-muted text-muted-foreground
3. Desktop layout (hidden lg:flex)
- Vertical sidebar (w-56)
- Content area (flex-1)
- Scroll spy for desktop only
4. Mobile content area (lg:hidden)
- No scroll spy (simpler)
- Direct tab switching
- Same visibility logic (hidden class)
Result:
✅ Only active section visible (desktop + mobile)
✅ Mobile has horizontal tabs
✅ Desktop has vertical sidebar
✅ Proper responsive behavior
✅ Tab switching works correctly
Fixed 3 critical issues:
1. Fixed Vertical Tabs - Cards All Showing
- Updated VerticalTabForm to hide inactive sections
- Only active section visible (className: hidden for others)
- Proper tab switching now works
2. Added Mobile Search/Filter to Coupons
- Created CouponFilterSheet component
- Added mobile search bar with icon
- Filter button with active count badge
- Matches Products pattern exactly
- Sheet with Apply/Reset buttons
3. Removed max-height from VerticalTabForm
- User removed max-h-[calc(100vh-200px)]
- Content now flows naturally
- Better for forms with varying heights
Components Created:
- CouponFilterSheet.tsx - Mobile filter bottom sheet
- Discount type filter
- Apply/Reset actions
- Active filter count
Changes to Coupons/index.tsx:
- Added mobile search bar (md:hidden)
- Added filter sheet state
- Added activeFiltersCount
- Search icon + SlidersHorizontal icon
- Filter badge indicator
Changes to VerticalTabForm:
- Hide inactive sections (className: hidden)
- Only show section matching activeTab
- Proper visibility control
Result:
✅ Vertical tabs work correctly (only one section visible)
✅ Mobile search/filter on Coupons (like Products)
✅ Filter count badge
✅ Professional mobile UX
Next: Move customer site member checkbox to settings
Implemented: Frontend Components for Level 1 Compatibility
Created Components:
- MetaFields.tsx - Generic meta field renderer
- useMetaFields.ts - Hook for field registry
Integrated Into:
- Orders/Edit.tsx - Meta fields after OrderForm
- Products/Edit.tsx - Meta fields after ProductForm
Features:
- Supports: text, textarea, number, date, select, checkbox
- Groups fields by section
- Zero coupling with specific plugins
- Renders any registered fields dynamically
- Read-only mode support
How It Works:
1. Backend exposes meta via API (Phase 1)
2. PHP registers fields via MetaFieldsRegistry (Phase 3 - next)
3. Fields localized to window.WooNooWMetaFields
4. useMetaFields hook reads registry
5. MetaFields component renders fields
6. User edits fields
7. Form submission includes meta
8. Backend saves via update_order_meta_data()
Result:
- Generic, reusable components
- Zero plugin-specific code
- Works with any registered fields
- Clean separation of concerns
Next: Phase 3 - PHP MetaFieldsRegistry system
**Issue:**
- PageHeader had max-w-5xl hardcoded
- This made all pages boxed (Orders, Products, etc.)
- Only settings pages should be boxed
**Solution:**
- Use useLocation to detect current route
- Apply max-w-5xl only when pathname starts with '/settings'
- All other pages get full width (w-full)
**Result:**
✅ Settings pages: Boxed layout (max-w-5xl)
✅ Other pages: Full width layout
✅ Consistent with design system
Fixed 4 major UX issues:
1. Stock Column - Show Infinity Symbol
Problem: Stock shows badge even when not managed
Solution:
- Check manage_stock flag
- If true: Show StockBadge with quantity
- If false: Show ∞ (infinity symbol) for unlimited
Result: Clear visual for unlimited stock
2. Type Column & Price Display
Problem: Type column empty, price ignores sale price
Solution:
- Type: Show badge with product.type (simple, variable, etc.)
- Price: Respect sale price hierarchy:
1. price_html (WooCommerce formatted)
2. sale_price (show strikethrough regular + green sale)
3. regular_price (normal display)
4. — (dash for no price)
Result:
- Type visible with badge styling
- Sale prices show with strikethrough
- Clear visual hierarchy
3. Rich Text Editor for Description
Problem: Description shows raw HTML in textarea
Solution:
- Created RichTextEditor component with Tiptap
- Toolbar: Bold, Italic, H2, Lists, Quote, Undo/Redo
- Integrated into GeneralTab
Features:
- WYSIWYG editing
- Keyboard shortcuts
- Clean toolbar UI
- Saves as HTML
Result: Professional rich text editing experience
4. Inline Create Categories & Tags
Problem: Cannot create new categories/tags in product form
Solution:
- Added input + "Add" button above each list
- Press Enter or click Add to create
- Auto-selects newly created item
- Shows loading state
- Toast notifications
Result:
- No need to leave product form
- Seamless workflow
- Better UX
Files Changed:
- index.tsx: Stock ∞, sale price display, type badge
- GeneralTab.tsx: RichTextEditor integration
- OrganizationTab.tsx: Inline create UI
- RichTextEditor.tsx: New reusable component
Note: Variation attribute value issue (screenshot 1) needs API data format investigation
## Issue #1: Back Button Navigation Fixed
**Problem:** Back button navigated too far to Notifications.tsx
**Root Cause:** Wrong route - should go to /staff or /customer page with templates tab
**Solution:**
- Detect if staff or customer event
- Navigate to `/settings/notifications/{staff|customer}?tab=templates`
- Staff.tsx and Customer.tsx read tab query param
- Auto-open templates tab on return
**Files:**
- `routes/Settings/Notifications/EditTemplate.tsx`
- `routes/Settings/Notifications/Staff.tsx`
- `routes/Settings/Notifications/Customer.tsx`
## Issue #2: Dialog Pattern - Use Existing, Dont Reinvent!
**Problem:** Created new DialogBody component, over-engineered
**Root Cause:** Didnt check existing dialog usage in project
**Solution:**
- Reverted dialog.tsx to original
- Use existing pattern from Shipping.tsx:
```tsx
<DialogContent className="max-h-[90vh] overflow-y-auto">
```
- Simple, proven, works!
**Files:**
- `components/ui/dialog.tsx` - Reverted to original
- `components/ui/rich-text-editor.tsx` - Use existing pattern
**Lesson Learned:**
Always scan project for existing patterns before creating new ones!
Both issues fixed! ✅
## ✅ Issue 1: WordPress Media Not Loading
**Problem:** WP media library not loaded error
**Solution:**
- Added fallback to URL prompt
- Better error handling
- User can still insert images if WP media fails
## ✅ Issue 2: Button Variables Filter
**Problem:** All variables shown in button link field
**Solution:**
- Filter to only show URL variables
- Applied to both RichTextEditor and EmailBuilder
- Only `*_url` variables displayed
**Before:** {order_number} {customer_name} {order_total} ...
**After:** {order_url} {store_url} only
## ✅ Issue 3: Color Customization Note
**Noted for future:**
- Hero card gradient colors
- Button primary color
- Button secondary border color
- Will be added to email customization form later
## ✅ Issue 4 & 5: Heading Display in Editor & Builder
**Problem:** Headings looked like paragraphs
**Solution:**
- Added Tailwind heading styles to RichTextEditor
- Added heading styles to BlockRenderer
- Now headings are visually distinct:
- H1: 3xl, bold
- H2: 2xl, bold
- H3: xl, bold
- H4: lg, bold
**Files Modified:**
- `components/ui/rich-text-editor.tsx`
- `components/EmailBuilder/BlockRenderer.tsx`
## ✅ Issue 6: Order Items Variable
**Problem:** No variable for product list/table
**Solution:**
- Added `order_items` variable
- Description: "Order Items (formatted table)"
- Will render formatted product list in emails
**File Modified:**
- `includes/Core/Notifications/TemplateProvider.php`
## ✅ Issue 7: Remove Edit Icon from Spacer/Divider
**Problem:** Edit button shown but no options to edit
**Solution:**
- Conditional rendering of edit button
- Only show for `card` and `button` blocks
- Spacer and divider only show: ↑ ↓ ×
**File Modified:**
- `components/EmailBuilder/BlockRenderer.tsx`
---
## 📋 Summary
All user feedback addressed:
1. ✅ WP Media fallback
2. ✅ Button variables filtered
3. ✅ Color customization noted
4. ✅ Headings visible in editor
5. ✅ Headings visible in builder
6. ✅ Order items variable added
7. ✅ Edit icon removed from spacer/divider
Ready for testing! ��
## ✅ Improvements 4-5 Complete - Respecting WordPress!
### 4. WordPress Media Modal for TipTap Images
**Before:**
- Prompt dialog for image URL
- Manual URL entry
- No media library access
**After:**
- Native WordPress Media Modal
- Browse existing uploads
- Upload new images
- Full media library features
- Alt text, dimensions included
**Implementation:**
- `wp-media.ts` helper library
- `openWPMediaImage()` function
- Integrates with TipTap Image extension
- Sets src, alt, title automatically
### 5. WordPress Media Modal for Store Logos/Favicon
**Before:**
- Only drag-and-drop or file picker
- No access to existing media
**After:**
- "Choose from Media Library" button
- Filtered by media type:
- Logo: PNG, JPEG, SVG, WebP
- Favicon: PNG, ICO
- Browse and reuse existing assets
- Professional WordPress experience
**Implementation:**
- Updated `ImageUpload` component
- Added `mediaType` prop
- Three specialized functions:
- `openWPMediaLogo()`
- `openWPMediaFavicon()`
- `openWPMediaImage()`
## 📦 New Files:
**lib/wp-media.ts:**
```typescript
- openWPMedia() - Core function
- openWPMediaImage() - For general images
- openWPMediaLogo() - For logos (filtered)
- openWPMediaFavicon() - For favicons (filtered)
- WPMediaFile interface
- Full TypeScript support
```
## 🎨 User Experience:
**Email Builder:**
- Click image icon in RichTextEditor
- WordPress Media Modal opens
- Select from library or upload
- Image inserted with proper attributes
**Store Settings:**
- Drag-and-drop still works
- OR click "Choose from Media Library"
- Filtered by appropriate file types
- Reuse existing brand assets
## 🙏 Respect to WordPress:
**Why This Matters:**
1. **Familiar Interface** - Users know WordPress Media
2. **Existing Assets** - Access uploaded media
3. **Better UX** - No manual URL entry
4. **Professional** - Native WordPress integration
5. **Consistent** - Same as Posts/Pages
**WordPress Integration:**
- Uses `window.wp.media` API
- Respects user permissions
- Works with media library
- Proper nonce handling
- Full compatibility
## 📋 All 5 Improvements Complete:
✅ 1. Heading Selector (H1-H4, Paragraph)
✅ 2. Styled Buttons in Cards (matching standalone)
✅ 3. Variable Pills for Button Links
✅ 4. WordPress Media for TipTap Images
✅ 5. WordPress Media for Store Logos/Favicon
## 🚀 Ready for Production!
All user feedback implemented perfectly! 🎉
## Issue:
- API returns data ✅
- Console shows template data ✅
- But form inputs remain empty ❌
## Root Cause:
RichTextEditor not re-rendering when template data loads
## Fixes:
### 1. Add Key Prop to Force Re-render ✅
```tsx
<RichTextEditor
key={`editor-${eventId}-${channelId}`} // Force new instance
content={body}
onChange={setBody}
variables={variableKeys}
/>
```
- Key changes when route params change
- Forces React to create new editor instance
- Ensures fresh state with new template data
### 2. Improve RichTextEditor Sync Logic ✅
```tsx
useEffect(() => {
if (editor && content) {
const currentContent = editor.getHTML();
if (content !== currentContent) {
console.log("RichTextEditor: Updating content");
editor.commands.setContent(content);
}
}
}, [content, editor]);
```
- Check if content actually changed
- Add logging for debugging
- Prevent unnecessary updates
## Expected Result:
1. Template data loads from API ✅
2. Subject input fills with default ✅
3. Body editor fills with default ✅
4. Variables populate dropdown ✅
Test by refreshing the page!
## ✅ Issue 1: Cookie Authentication in Standalone Mode
**Problem:**
- `rest_cookie_invalid_nonce` errors on customer-settings
- `Cookie check failed` errors on media uploads
- Both endpoints returning 403 in standalone mode
**Root Cause:**
WordPress REST API requires `credentials: "include"` for cookie-based authentication in cross-origin contexts (standalone mode uses different URL).
**Fixed:**
1. **Customer Settings (Customers.tsx)**
- Added `credentials: "include"` to both GET and POST requests
- Use `WNW_CONFIG.nonce` as primary nonce source
- Fallback to `wpApiSettings.nonce`
2. **Media Upload (image-upload.tsx)**
- Added `credentials: "include"` to media upload
- Prioritize `WNW_CONFIG.nonce` for standalone mode
- Changed from `same-origin` to `include` for cross-origin support
**Result:**
- ✅ Customer settings load and save in standalone mode
- ✅ Image/logo uploads work in standalone mode
- ✅ SVG uploads work with proper authentication
## ✅ Issue 2: Dynamic VIP Customer Calculation
**Problem:** VIP calculation was hardcoded (TODO comment)
**Requirement:** Use dynamic settings from Customer Settings page
**Fixed (AnalyticsController.php):**
1. **Individual Customer VIP Status**
- Call `CustomerSettingsProvider::is_vip_customer()` for each customer
- Add `is_vip` field to customer data
- Set `segment` to "vip" for VIP customers
- Count VIP customers dynamically
2. **Segments Overview**
- Replace hardcoded `vip: 0` with actual `$vip_count`
- VIP count updates automatically based on settings
**How It Works:**
- CustomerSettingsProvider reads settings from database
- Checks: min_spent, min_orders, timeframe, require_both, exclude_refunded
- Calculates VIP status in real-time based on current criteria
- Updates immediately when settings change
**Result:**
- ✅ VIP badge shows correctly on customer list
- ✅ VIP count in segments reflects actual qualified customers
- ✅ Changes to VIP criteria instantly affect dashboard
- ✅ No cache issues - recalculates on each request
---
## Files Modified:
- `Customers.tsx` - Add credentials for cookie auth
- `image-upload.tsx` - Add credentials for media upload
- `AnalyticsController.php` - Dynamic VIP calculation
## Testing:
1. ✅ Customer settings save in standalone mode
2. ✅ Logo upload works in standalone mode
3. ✅ VIP customers show correct badge
4. ✅ Change VIP criteria → dashboard updates
5. ✅ Segments show correct VIP count
## ✅ Issue 1: Customers Submenu Missing in WP-Admin
**Problem:** Tax and Customer submenus only visible in standalone mode
**Root Cause:** PHP navigation registry did not include Customers
**Fixed:** Added Customers to NavigationRegistry.php settings children
**Result:** Customers submenu now shows in all modes
## ✅ Issue 2: App Logo/Title in Topbar
**Problem:** Should show logo → store name → "WooNooW" fallback
**Fixed:** Header component now:
- Fetches branding from /store/branding endpoint
- Shows logo image if available
- Falls back to store name text
- Updates on store settings change event
**Result:** Proper branding hierarchy in app header
## ✅ Issue 3: Zone Card Header Density on Mobile
**Problem:** "Indonesia Addons" row with 3 icons too cramped on mobile
**Fixed:** Shipping.tsx zone card header:
- Reduced gap from gap-3 to gap-2/gap-1 on mobile
- Smaller font size on mobile (text-sm md:text-lg)
- Added min-w-0 for proper text truncation
- flex-shrink-0 on icon buttons
**Result:** Better mobile spacing and readability
## ✅ Issue 4: Go to WP Admin Button
**Problem:** Should show in standalone mode, not wp-admin
**Fixed:** More page now shows "Go to WP Admin" button:
- Only in standalone mode
- Before Logout button
- Links to /wp-admin
**Result:** Easy access to WP Admin from standalone mode
## ✅ Issue 5: Customer Settings 403 Error
**Problem:** Permission check failing for customer-settings endpoint
**Fixed:** StoreController.php check_permission():
- Added fallback: manage_woocommerce OR manage_options
- Ensures administrators always have access
**Result:** Customer Settings page loads successfully
## ✅ Issue 6: Dark Mode Logo Upload Field
**Problem:** No UI to upload dark mode logo
**Fixed:** Store settings page now has:
- "Store logo (Light mode)" field
- "Store logo (Dark mode)" field (optional)
- Backend support in StoreSettingsProvider
- Full save/load functionality
**Result:** Users can upload separate logos for light/dark modes
## ✅ Issue 7: Login Card Background Too Dark
**Problem:** Login card same color as background in dark mode
**Fixed:** Login.tsx card styling:
- Changed from dark:bg-gray-800 (solid)
- To dark:bg-gray-900/50 (semi-transparent)
- Added backdrop-blur-xl for glass effect
- Added border for definition
**Result:** Login card visually distinct with modern glass effect
---
## Summary
**All 7 Issues Resolved:**
1. ✅ Customers submenu in all modes
2. ✅ Logo/title hierarchy in topbar
3. ✅ Mobile zone card spacing
4. ✅ Go to WP Admin in standalone
5. ✅ Customer Settings permission fix
6. ✅ Dark mode logo upload field
7. ✅ Lighter login card background
**Files Modified:**
- NavigationRegistry.php - Added Customers to nav
- App.tsx - Logo/branding in header
- Shipping.tsx - Mobile spacing
- More/index.tsx - WP Admin button
- StoreController.php - Permission fallback
- Store.tsx - Dark logo field
- StoreSettingsProvider.php - Dark logo backend
- Login.tsx - Card background
**Ready for production!** 🎉
## Issue 1: Submenu Hidden in WP-Admin Modes ✅
**Problem:** Tax and Customer submenus visible in standalone but hidden in wp-admin modes
**Root Cause:** Incorrect `top` positioning calculation
- Was: `top-[calc(7rem+32px)]` (112px + 32px = 144px)
- Should be: `top-16` (64px - header height)
**Fixed:**
- `DashboardSubmenuBar.tsx` - Updated positioning
- `SubmenuBar.tsx` - Updated positioning
**Result:** Submenus now visible in all modes ✅
---
## Issue 2: Inconsistent Title in Standalone ✅
**Problem:** Title should prioritize: Logo → Store Name → WooNooW
**Fixed:**
- `StandaloneAdmin.php` - Use `woonoow_store_name` option first
- Falls back to `blogname`, then "WooNooW"
---
## Issue 3: Dense Card Header on Mobile ✅
**Problem:** Card header with title, description, and button too cramped on mobile
**Solution:** Stack vertically on mobile, horizontal on desktop
**Fixed:**
- `SettingsCard.tsx` - Changed from `flex-row` to `flex-col sm:flex-row`
- Button now full width on mobile, auto on desktop
**Result:** Better mobile UX, readable spacing ✅
---
## Issue 4: Missing "Go to WP Admin" Link ✅
**Added:**
- New button in More page (wp-admin modes only)
- Positioned before Exit Fullscreen/Logout
- Uses `ExternalLink` icon
- Links to `/wp-admin/`
---
## Issue 5: Customer Settings 403 Error ⚠️
**Status:** Backend ready, needs testing
- `CustomerSettingsProvider.php` exists and is autoloaded
- REST endpoints registered in `StoreController.php`
- Permission callback uses `manage_woocommerce`
**Next:** Test endpoint directly to verify
---
## Issue 6: Dark Mode Logo Support ✅
**Added:**
- New field: `store_logo_dark`
- Stored in: `woonoow_store_logo_dark` option
- Added to `StoreSettingsProvider.php`:
- `get_settings()` - Returns dark logo
- `save_settings()` - Saves dark logo
**Frontend:** Ready for implementation (use `useTheme()` to switch)
---
## Issue 7: Consistent Dark Background ✅
**Problem:** Login and Dashboard use different dark backgrounds
- Login: `dark:from-gray-900 dark:to-gray-800` (pure gray)
- Dashboard: `--background: 222.2 84% 4.9%` (dark blue-gray)
**Solution:** Use design system variables consistently
**Fixed:**
- `Login.tsx` - Changed to `dark:from-background dark:to-background`
- Card background: `dark:bg-card` instead of `dark:bg-gray-800`
**Result:** Consistent dark mode across all screens ✅
---
## Summary
✅ **Fixed 6 issues:**
1. Submenu visibility in all modes
2. Standalone title logic
3. Mobile card header density
4. WP Admin link in More page
5. Dark mode logo backend support
6. Consistent dark background colors
⚠️ **Needs Testing:**
- Customer Settings 403 error (backend ready, verify endpoint)
**Files Modified:**
- `DashboardSubmenuBar.tsx`
- `SubmenuBar.tsx`
- `StandaloneAdmin.php`
- `SettingsCard.tsx`
- `More/index.tsx`
- `StoreSettingsProvider.php`
- `Login.tsx`
**All UI/UX polish complete!** 🎨