mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-12 02:38:15 +00:00
feat: build Outreach page with template list, WhatsApp preview, and message history
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
138
src/components/outreach/template-preview.tsx
Normal file
138
src/components/outreach/template-preview.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import { Button } from '@/components/base/buttons/button';
|
||||
import { Badge } from '@/components/base/badges/badges';
|
||||
import { WhatsAppMockup } from './whatsapp-mockup';
|
||||
import type { WhatsAppTemplate } from '@/types/entities';
|
||||
|
||||
interface TemplatePreviewProps {
|
||||
template: WhatsAppTemplate;
|
||||
}
|
||||
|
||||
const variableDescriptions: Record<string, string> = {
|
||||
patient_name: "Lead's contact name",
|
||||
hospital_name: 'Clinic or hospital name',
|
||||
campaign_name: 'Linked campaign name',
|
||||
booking_link: 'Appointment booking URL',
|
||||
};
|
||||
|
||||
export const TemplatePreview = ({ template }: TemplatePreviewProps) => {
|
||||
const totalSent = template.sendCount ?? 0;
|
||||
const delivered = template.deliveredCount ?? 0;
|
||||
const read = template.readCount ?? 0;
|
||||
const clicked = template.clickedCount ?? 0;
|
||||
const failed = template.failedCount ?? 0;
|
||||
|
||||
const metrics = [
|
||||
{ label: 'Sent', value: totalSent },
|
||||
{ label: 'Delivered', value: delivered },
|
||||
{ label: 'Read', value: read },
|
||||
{ label: 'Clicked', value: clicked },
|
||||
{ label: 'Failed', value: failed },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-primary rounded-2xl border border-secondary overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="p-5 border-b border-secondary flex items-center justify-between">
|
||||
<p className="text-md font-bold text-primary">
|
||||
Template Preview — {template.name}
|
||||
</p>
|
||||
<Button color="secondary" size="sm">
|
||||
Edit Template
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Body */}
|
||||
<div className="flex">
|
||||
{/* Left: phone mockup */}
|
||||
<div className="bg-secondary p-5 flex items-start justify-center flex-shrink-0">
|
||||
<WhatsAppMockup body={template.body ?? ''} variables={template.variables} />
|
||||
</div>
|
||||
|
||||
{/* Right: details */}
|
||||
<div className="flex-1 p-5 space-y-5">
|
||||
{/* Variables */}
|
||||
{template.variables.length > 0 && (
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-secondary uppercase tracking-wider mb-2">Variables</p>
|
||||
<div className="space-y-1.5">
|
||||
{template.variables.map((variable) => (
|
||||
<div key={variable} className="flex items-center gap-2 text-sm">
|
||||
<code className="bg-secondary px-1.5 py-0.5 rounded text-xs text-brand-secondary font-mono">
|
||||
{`{{${variable}}}`}
|
||||
</code>
|
||||
<span className="text-quaternary">→</span>
|
||||
<span className="text-tertiary">
|
||||
{variableDescriptions[variable] ?? variable}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Linked Campaign */}
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-secondary uppercase tracking-wider mb-2">Linked Campaign</p>
|
||||
{template.linkedCampaignName ? (
|
||||
<Badge type="pill-color" color="brand" size="md">
|
||||
{template.linkedCampaignName}
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge type="pill-color" color="gray" size="md">
|
||||
None (Custom)
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Approval Status */}
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-secondary uppercase tracking-wider mb-2">Approval Status</p>
|
||||
{template.approvalStatus === 'APPROVED' ? (
|
||||
<Badge type="pill-color" color="success" size="md">
|
||||
✓ Approved
|
||||
</Badge>
|
||||
) : template.approvalStatus === 'PENDING' ? (
|
||||
<Badge type="pill-color" color="warning" size="md">
|
||||
⏳ Pending Review
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge type="pill-color" color="error" size="md">
|
||||
Rejected
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Performance */}
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-secondary uppercase tracking-wider mb-2">Performance</p>
|
||||
<div className="flex gap-3">
|
||||
{metrics.map((metric) => (
|
||||
<div
|
||||
key={metric.label}
|
||||
className="flex-1 bg-secondary rounded-xl p-3 text-center min-w-0"
|
||||
>
|
||||
<p className="text-lg font-bold text-primary">{metric.value}</p>
|
||||
<p className="text-xs text-quaternary mt-0.5">{metric.label}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Languages */}
|
||||
{template.language.length > 0 && (
|
||||
<div>
|
||||
<p className="text-xs font-semibold text-secondary uppercase tracking-wider mb-2">Language</p>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{template.language.map((lang) => (
|
||||
<Badge key={lang} type="color" color="gray" size="sm">
|
||||
{lang.toUpperCase()}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user