feat: Add button dialog with text, link, and style options

##  Better Button Insert!

### What Changed:

**Before:**
- Click [+ Button] → Inserts generic button immediately
- No customization
- Always same text/link

**After:**
- Click [+ Button] → Opens dialog
- Configure before inserting
- Professional UX

### Button Dialog Features:

**3 Configuration Options:**

1. **Button Text**
   - Input field for custom text
   - Placeholder: "e.g., View Order, Track Shipment"
   - Default: "Click Here"

2. **Button Link**
   - Input field for URL or variable
   - Placeholder: "e.g., {order_url}, {product_url}"
   - Default: "{order_url}"
   - Hint: "Use variables like {order_url} or enter a full URL"

3. **Button Style** (NEW!)
   - **Solid** - High priority, urgent action
     - Purple background, white text
     - For primary CTAs (View Order, Complete Payment)
   - **Outline** - Secondary action, less urgent
     - Purple border, purple text, transparent bg
     - For secondary actions (Learn More, Contact Support)

### Visual Style Selector:

```
○ [Solid]     High priority, urgent action
○ [Outline]   Secondary action, less urgent
```

Shows actual button preview in dialog!

### Why 2 Button Types?

**Solid (Primary):**
- Urgent actions: "Complete Order", "Pay Now", "Track Shipment"
- High conversion priority
- Stands out in email

**Outline (Secondary):**
- Optional actions: "View Details", "Learn More", "Contact Us"
- Lower priority
- Doesn't compete with primary CTA

**Email Best Practice:**
- 1 solid button per email (primary action)
- 0-2 outline buttons (secondary actions)
- Clear visual hierarchy = better conversions

### Output:

**Solid:**
```html
<a href="{order_url}" class="button">View Order</a>
```

**Outline:**
```html
<a href="{order_url}" class="button-outline">Learn More</a>
```

### Preview Support:
- Both styles render correctly in preview
- Solid: Purple background
- Outline: Purple border, transparent bg

Next: Email content builder? 🤔
This commit is contained in:
dwindown
2025-11-13 06:28:03 +07:00
parent f8538c4cf7
commit 74e084caa6

View File

@@ -9,6 +9,8 @@ import { Input } from '@/components/ui/input';
import { RichTextEditor } from '@/components/ui/rich-text-editor';
import { Label } from '@/components/ui/label';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { ArrowLeft, Eye, Edit, RotateCcw, Plus, CheckCircle, Info, AlertCircle, Image } from 'lucide-react';
import { toast } from 'sonner';
import { __ } from '@/lib/i18n';
@@ -35,6 +37,12 @@ export default function EditTemplate() {
const [variables, setVariables] = useState<{ [key: string]: string }>({});
const [activeTab, setActiveTab] = useState('editor');
const [codeMode, setCodeMode] = useState(false);
// Button dialog state
const [buttonDialogOpen, setButtonDialogOpen] = useState(false);
const [buttonText, setButtonText] = useState('Click Here');
const [buttonLink, setButtonLink] = useState('{order_url}');
const [buttonType, setButtonType] = useState<'solid' | 'outline'>('solid');
// Fetch template
const { data: template, isLoading, error } = useQuery({
@@ -143,9 +151,18 @@ ${content || '<h2>Card Title</h2>\n<p>Card content goes here...</p>'}
toast.success(__('Card inserted'));
};
const openButtonDialog = () => {
setButtonText('Click Here');
setButtonLink('{order_url}');
setButtonType('solid');
setButtonDialogOpen(true);
};
const insertButton = () => {
const buttonHtml = `<p style="text-align: center;"><a href="{order_url}" class="button">Button Text</a></p>`;
const buttonClass = buttonType === 'solid' ? 'button' : 'button-outline';
const buttonHtml = `<p style="text-align: center;"><a href="${buttonLink}" class="${buttonClass}">${buttonText}</a></p>`;
setBody(body + buttonHtml + '\n');
setButtonDialogOpen(false);
toast.success(__('Button inserted'));
};
@@ -240,6 +257,7 @@ ${content || '<h2>Card Title</h2>\n<p>Card content goes here...</p>'}
h3 { font-size: 16px; margin-top: 0; margin-bottom: 8px; color: #333; }
p { font-size: 16px; line-height: 1.6; color: #555; margin-bottom: 16px; }
.button { display: inline-block; background: #7f54b3; color: #fff !important; padding: 14px 28px; border-radius: 6px; text-decoration: none; font-weight: 600; }
.button-outline { display: inline-block; background: transparent; color: #7f54b3 !important; padding: 12px 26px; border: 2px solid #7f54b3; border-radius: 6px; text-decoration: none; font-weight: 600; }
.info-box { background: #f6f6f6; border-radius: 6px; padding: 20px; margin: 16px 0; }
.footer { padding: 32px; text-align: center; color: #888; font-size: 13px; }
</style>
@@ -428,7 +446,7 @@ ${content || '<h2>Card Title</h2>\n<p>Card content goes here...</p>'}
type="button"
variant="outline"
size="sm"
onClick={insertButton}
onClick={openButtonDialog}
className="h-7 text-xs gap-1"
>
<Plus className="h-3 w-3" />
@@ -464,6 +482,83 @@ ${content || '<h2>Card Title</h2>\n<p>Card content goes here...</p>'}
</Card>
)}
{/* Button Insert Dialog */}
<Dialog open={buttonDialogOpen} onOpenChange={setButtonDialogOpen}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>{__('Insert Button')}</DialogTitle>
<DialogDescription>
{__('Configure your call-to-action button. Use variables like {order_url} for dynamic links.')}
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
{/* Button Text */}
<div className="space-y-2">
<Label htmlFor="button-text">{__('Button Text')}</Label>
<Input
id="button-text"
value={buttonText}
onChange={(e) => setButtonText(e.target.value)}
placeholder={__('e.g., View Order, Track Shipment')}
/>
</div>
{/* Button Link */}
<div className="space-y-2">
<Label htmlFor="button-link">{__('Button Link')}</Label>
<Input
id="button-link"
value={buttonLink}
onChange={(e) => setButtonLink(e.target.value)}
placeholder={__('e.g., {order_url}, {product_url}')}
/>
<p className="text-xs text-muted-foreground">
{__('Use variables like {order_url} or enter a full URL')}
</p>
</div>
{/* Button Type */}
<div className="space-y-2">
<Label>{__('Button Style')}</Label>
<RadioGroup value={buttonType} onValueChange={(value: 'solid' | 'outline') => setButtonType(value)}>
<div className="flex items-center space-x-2">
<RadioGroupItem value="solid" id="solid" />
<Label htmlFor="solid" className="font-normal cursor-pointer">
<div className="flex items-center gap-2">
<div className="px-4 py-2 bg-primary text-primary-foreground rounded text-sm font-medium">
{__('Solid')}
</div>
<span className="text-xs text-muted-foreground">{__('High priority, urgent action')}</span>
</div>
</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="outline" id="outline" />
<Label htmlFor="outline" className="font-normal cursor-pointer">
<div className="flex items-center gap-2">
<div className="px-4 py-2 border-2 border-primary text-primary rounded text-sm font-medium">
{__('Outline')}
</div>
<span className="text-xs text-muted-foreground">{__('Secondary action, less urgent')}</span>
</div>
</Label>
</div>
</RadioGroup>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setButtonDialogOpen(false)}>
{__('Cancel')}
</Button>
<Button onClick={insertButton}>
{__('Insert Button')}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* Preview Tab */}
{activeTab === 'preview' && (
<Card>