/** * Placeholders Section * Reference for available email placeholders/merge tags. * * Accepts optional `placeholders` and `categoryLabels` props so each * feature can supply its own set. When omitted the subscription-specific * defaults are used (backward-compatible). */ import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip'; import { ChevronDown, Variable, Copy, Check } from 'lucide-react'; import { toast } from 'sonner'; /** A single placeholder / merge-tag definition. */ export interface PlaceholderConfig { tag: string; description: string; example: string; category: string; } /** Label + colour for a placeholder category badge. */ export interface PlaceholderCategoryLabel { label: string; color: string; } // --------------------------------------------------------------------------- // Default subscription placeholders (used when no props are supplied) // --------------------------------------------------------------------------- const defaultPlaceholders: PlaceholderConfig[] = [ // Customer { tag: '{customer_name}', description: 'Customer full name', example: 'John Doe', category: 'customer' }, { tag: '{customer_first_name}', description: 'Customer first name', example: 'John', category: 'customer' }, { tag: '{customer_last_name}', description: 'Customer last name', example: 'Doe', category: 'customer' }, { tag: '{customer_email}', description: 'Customer email address', example: 'john@example.com', category: 'customer' }, // Order { tag: '{order_id}', description: 'Order number', example: '#1234', category: 'order' }, { tag: '{order_date}', description: 'Order date', example: 'January 15, 2025', category: 'order' }, { tag: '{order_total}', description: 'Order total amount', example: '$99.99', category: 'order' }, { tag: '{payment_method}', description: 'Payment method used', example: 'Credit Card', category: 'order' }, // Subscription { tag: '{subscription_id}', description: 'Subscription ID', example: '#SUB-5678', category: 'subscription' }, { tag: '{subscription_status}', description: 'Current subscription status', example: 'Active', category: 'subscription' }, { tag: '{product_name}', description: 'Subscription product name', example: 'Premium Membership', category: 'subscription' }, { tag: '{subscription_price}', description: 'Subscription price', example: '$29.99/month', category: 'subscription' }, { tag: '{next_billing_date}', description: 'Next billing/renewal date', example: 'February 15, 2025', category: 'subscription' }, { tag: '{expiry_date}', description: 'Subscription expiry date', example: 'March 15, 2025', category: 'subscription' }, { tag: '{billing_period}', description: 'Billing period', example: 'Monthly', category: 'subscription' }, // Site { tag: '{site_name}', description: 'Your website name', example: 'My Store', category: 'site' }, { tag: '{site_url}', description: 'Your website URL', example: 'https://mystore.com', category: 'site' }, { tag: '{admin_email}', description: 'Admin email address', example: 'admin@mystore.com', category: 'site' }, { tag: '{my_account_url}', description: 'My Account page URL', example: 'https://mystore.com/my-account', category: 'site' }, // Date { tag: '{current_date}', description: 'Current date', example: 'January 24, 2026', category: 'date' }, { tag: '{year}', description: 'Current year', example: '2026', category: 'date' }, ]; const defaultCategoryLabels: Record = { customer: { label: 'Customer', color: 'bg-blue-100 text-blue-700' }, order: { label: 'Order', color: 'bg-green-100 text-green-700' }, subscription: { label: 'Subscription', color: 'bg-purple-100 text-purple-700' }, site: { label: 'Site', color: 'bg-orange-100 text-orange-700' }, date: { label: 'Date', color: 'bg-gray-100 text-gray-700' }, }; // --------------------------------------------------------------------------- // Placeholder item component // --------------------------------------------------------------------------- interface PlaceholderItemProps { placeholder: PlaceholderConfig; catLabels: Record; } function PlaceholderItem({ placeholder, catLabels }: PlaceholderItemProps) { const [copied, setCopied] = useState(false); const handleCopy = async () => { try { await navigator.clipboard.writeText(placeholder.tag); setCopied(true); toast.success(`Copied ${placeholder.tag}`); setTimeout(() => setCopied(false), 2000); } catch { toast.error('Failed to copy'); } }; const cat = catLabels[placeholder.category]; return (
{placeholder.tag} {cat && ( {cat.label} )}

{placeholder.description}

Click to copy

); } // --------------------------------------------------------------------------- // Main component // --------------------------------------------------------------------------- interface PlaceholdersSectionProps { /** Custom placeholder list. Falls back to subscription defaults. */ placeholders?: PlaceholderConfig[]; /** Custom category label map. Falls back to subscription defaults. */ categoryLabels?: Record; } export function PlaceholdersSection({ placeholders, categoryLabels, }: PlaceholdersSectionProps = {}) { const [isOpen, setIsOpen] = useState(false); const [activeCategory, setActiveCategory] = useState(null); const items = placeholders ?? defaultPlaceholders; const catLabels = categoryLabels ?? defaultCategoryLabels; const filteredPlaceholders = activeCategory ? items.filter((p) => p.category === activeCategory) : items; // Derive unique category keys from the items. const categories = Array.from(new Set(items.map((p) => p.category))); return (
Placeholders {items.length}
{/* Category Filter */}
{categories.map((cat) => ( ))}
{/* Placeholder List */}
{filteredPlaceholders.map((placeholder) => ( ))}
{/* Helper Text */}

Click any placeholder to copy it. Use these in your email content fields.

); }