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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user