feat: Add vertical tab layout to Coupon form

Implemented VerticalTabForm component for better UX

Created Components:
1. VerticalTabForm.tsx - Reusable vertical tab layout
   - Left sidebar with navigation (250px on desktop)
   - Right content area (scrollable)
   - Scroll spy - auto-highlights active section
   - Click to scroll to section
   - Smooth scrolling behavior
   - Icons support for tabs

2. FormSection component
   - Wrapper for form sections
   - Proper ref forwarding
   - Section ID tracking

Updated CouponForm:
- Added vertical tab navigation
- 3 sections: General, Usage restrictions, Usage limits
- Icons: Settings, ShieldCheck, BarChart3
- Narrower content area (better readability)
- Desktop-only (lg:block) - mobile keeps original layout

Features:
 Scroll spy - active tab follows scroll
 Click navigation - smooth scroll to section
 Visual hierarchy - clear section separation
 Better space utilization
 Reduced form width for readability
 Professional UI like Shopify/Stripe

Layout:
- Desktop: 250px sidebar + remaining content
- Content: max-h-[calc(100vh-200px)] scrollable
- Sticky sidebar (top-4)
- Active state: bg-primary text-primary-foreground
- Hover state: bg-muted hover:text-foreground

Next: Apply same pattern to Products form
This commit is contained in:
dwindown
2025-11-20 16:00:03 +07:00
parent 0f47c08b7a
commit 7455d99ab8
3 changed files with 169 additions and 18 deletions

View File

@@ -9,7 +9,9 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
import { Checkbox } from '@/components/ui/checkbox';
import { Button } from '@/components/ui/button';
import { MultiSelect } from '@/components/ui/multi-select';
import { VerticalTabForm, FormSection } from '@/components/VerticalTabForm';
import { ProductsApi } from '@/lib/api';
import { Settings, ShieldCheck, BarChart3 } from 'lucide-react';
import type { Coupon, CouponFormData } from '@/lib/api/coupons';
interface CouponFormProps {
@@ -76,10 +78,18 @@ export default function CouponForm({
setFormData(prev => ({ ...prev, [field]: value }));
};
const tabs = [
{ id: 'general', label: __('General'), icon: <Settings className="w-4 h-4" /> },
{ id: 'restrictions', label: __('Usage restrictions'), icon: <ShieldCheck className="w-4 h-4" /> },
{ id: 'limits', label: __('Usage limits'), icon: <BarChart3 className="w-4 h-4" /> },
];
return (
<form ref={formRef} onSubmit={handleSubmit} className="space-y-6">
{/* General Settings */}
<Card>
<form ref={formRef} onSubmit={handleSubmit}>
<VerticalTabForm tabs={tabs}>
{/* General Settings */}
<FormSection id="general">
<Card>
<CardHeader>
<CardTitle>{__('General')}</CardTitle>
</CardHeader>
@@ -170,8 +180,10 @@ export default function CouponForm({
</div>
</CardContent>
</Card>
</FormSection>
{/* Usage Restrictions */}
{/* Usage Restrictions */}
<FormSection id="restrictions">
<Card>
<CardHeader>
<CardTitle>{__('Usage restrictions')}</CardTitle>
@@ -320,8 +332,10 @@ export default function CouponForm({
</p>
</CardContent>
</Card>
</FormSection>
{/* Usage Limits */}
{/* Usage Limits */}
<FormSection id="limits">
<Card>
<CardHeader>
<CardTitle>{__('Usage limits')}</CardTitle>
@@ -378,19 +392,21 @@ export default function CouponForm({
</p>
</CardContent>
</Card>
</FormSection>
{/* Submit Button (if not hidden) */}
{!hideSubmitButton && (
<div className="flex gap-3">
<Button type="submit" disabled={submitting}>
{submitting
? __('Saving...')
: mode === 'create'
? __('Create Coupon')
: __('Update Coupon')}
</Button>
</div>
)}
{/* Submit Button (if not hidden) */}
{!hideSubmitButton && (
<div className="flex gap-3 mt-6">
<Button type="submit" disabled={submitting}>
{submitting
? __('Saving...')
: mode === 'create'
? __('Create Coupon')
: __('Update Coupon')}
</Button>
</div>
)}
</VerticalTabForm>
</form>
);
}