From be671b66ecd64d294597cfa817db83f2b708d81e Mon Sep 17 00:00:00 2001 From: dwindown Date: Thu, 20 Nov 2025 17:27:39 +0700 Subject: [PATCH] feat: Convert Products form to vertical tab layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applied VerticalTabForm to ProductFormTabbed Changes: 1. Replaced horizontal Tabs with VerticalTabForm 2. Converted TabsContent to FormSection components 3. Removed activeTab state (scroll spy handles this) 4. Dynamic tabs based on product type - Simple: General, Inventory, Organization - Variable: General, Inventory, Variations, Organization Benefits: ✅ Consistent layout with Coupons form ✅ Better space utilization ✅ Narrower content area (more readable) ✅ Scroll spy navigation ✅ Click to scroll to section ✅ Professional UI Layout: - Desktop: 250px sidebar + content area - Sidebar: Sticky with icons - Content: Scrollable with smooth navigation - Mobile: Keeps original horizontal tabs (future) Removed: - TabsList, TabsTrigger components - activeTab state and setActiveTab calls - Manual tab switching on validation errors Result: Both Products and Coupons now use same vertical tab pattern Forms are more professional and easier to navigate --- .../Products/partials/ProductFormTabbed.tsx | 92 +++++++++---------- 1 file changed, 41 insertions(+), 51 deletions(-) diff --git a/admin-spa/src/routes/Products/partials/ProductFormTabbed.tsx b/admin-spa/src/routes/Products/partials/ProductFormTabbed.tsx index ca7f21a..fa48bf7 100644 --- a/admin-spa/src/routes/Products/partials/ProductFormTabbed.tsx +++ b/admin-spa/src/routes/Products/partials/ProductFormTabbed.tsx @@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query'; import { api } from '@/lib/api'; import { __ } from '@/lib/i18n'; import { Button } from '@/components/ui/button'; -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { VerticalTabForm, FormSection } from '@/components/VerticalTabForm'; import { Package, DollarSign, Layers, Tag } from 'lucide-react'; import { toast } from 'sonner'; import { GeneralTab } from './tabs/GeneralTab'; @@ -74,7 +74,6 @@ export function ProductFormTabbed({ const [downloadable, setDownloadable] = useState(initial?.downloadable || false); const [featured, setFeatured] = useState(initial?.featured || false); const [submitting, setSubmitting] = useState(false); - const [activeTab, setActiveTab] = useState('general'); // Update form state when initial data changes (for edit mode) useEffect(() => { @@ -124,13 +123,11 @@ export function ProductFormTabbed({ // Validation if (!name.trim()) { toast.error(__('Product name is required')); - setActiveTab('general'); return; } if (type === 'simple' && !regularPrice) { toast.error(__('Regular price is required for simple products')); - setActiveTab('general'); return; } @@ -166,32 +163,19 @@ export function ProductFormTabbed({ } }; + // Define tabs based on product type + const tabs = [ + { id: 'general', label: __('General'), icon: }, + { id: 'inventory', label: __('Inventory'), icon: }, + ...(type === 'variable' ? [{ id: 'variations', label: __('Variations'), icon: }] : []), + { id: 'organization', label: __('Organization'), icon: }, + ]; + return (
- - - - - {__('General')} - - - - {__('Inventory')} - - {type === 'variable' && ( - - - {__('Variations')} - - )} - - - {__('Organization')} - - - + {/* General Tab */} - + - + {/* Inventory Tab */} - + - + - {/* Variations Tab */} - - - + {/* Variations Tab (only for variable products) */} + {type === 'variable' && ( + + + + )} {/* Organization Tab */} - + - - + - {/* Submit Button */} - {!hideSubmitButton && ( -
- -
- )} + {/* Submit Button */} + {!hideSubmitButton && ( +
+ +
+ )} +
); }