From 74e084caa6e30462c31bee7b64011d1c9d4be464 Mon Sep 17 00:00:00 2001 From: dwindown Date: Thu, 13 Nov 2025 06:28:03 +0700 Subject: [PATCH] feat: Add button dialog with text, link, and style options MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## ✨ 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 View Order ``` **Outline:** ```html Learn More ``` ### Preview Support: - Both styles render correctly in preview - Solid: Purple background - Outline: Purple border, transparent bg Next: Email content builder? 🤔 --- .../Settings/Notifications/EditTemplate.tsx | 99 ++++++++++++++++++- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx b/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx index 8320211..953a4c2 100644 --- a/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx +++ b/admin-spa/src/routes/Settings/Notifications/EditTemplate.tsx @@ -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 || '

Card Title

\n

Card content goes here...

'} toast.success(__('Card inserted')); }; + const openButtonDialog = () => { + setButtonText('Click Here'); + setButtonLink('{order_url}'); + setButtonType('solid'); + setButtonDialogOpen(true); + }; + const insertButton = () => { - const buttonHtml = `

Button Text

`; + const buttonClass = buttonType === 'solid' ? 'button' : 'button-outline'; + const buttonHtml = `

${buttonText}

`; setBody(body + buttonHtml + '\n'); + setButtonDialogOpen(false); toast.success(__('Button inserted')); }; @@ -240,6 +257,7 @@ ${content || '

Card Title

\n

Card content goes here...

'} 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; } @@ -428,7 +446,7 @@ ${content || '

Card Title

\n

Card content goes here...

'} type="button" variant="outline" size="sm" - onClick={insertButton} + onClick={openButtonDialog} className="h-7 text-xs gap-1" > @@ -464,6 +482,83 @@ ${content || '

Card Title

\n

Card content goes here...

'} )} + {/* Button Insert Dialog */} + + + + {__('Insert Button')} + + {__('Configure your call-to-action button. Use variables like {order_url} for dynamic links.')} + + + +
+ {/* Button Text */} +
+ + setButtonText(e.target.value)} + placeholder={__('e.g., View Order, Track Shipment')} + /> +
+ + {/* Button Link */} +
+ + setButtonLink(e.target.value)} + placeholder={__('e.g., {order_url}, {product_url}')} + /> +

+ {__('Use variables like {order_url} or enter a full URL')} +

+
+ + {/* Button Type */} +
+ + setButtonType(value)}> +
+ + +
+
+ + +
+
+
+
+ + + + + +
+
+ {/* Preview Tab */} {activeTab === 'preview' && (