257 lines
8.5 KiB
TypeScript
257 lines
8.5 KiB
TypeScript
import React from 'react';
|
|
import { FormSection } from '../../../components/forms/FormSection';
|
|
import { FormField } from '../../../components/forms/FormField';
|
|
import { SelectField } from '../../../components/forms/SelectField';
|
|
import { Button } from '../../../components/shared/Button';
|
|
|
|
const useCases = [
|
|
'MARKETING', 'CUSTOMER_CARE', 'MIXED', 'ACCOUNT_NOTIFICATION', 'DELIVERY_NOTIFICATION',
|
|
'FRAUD_ALERT', 'HIGHER_EDUCATION', 'LOW_VOLUME', 'POLITICAL', 'POLLING_VOTING',
|
|
'PUBLIC_SERVICE_ANNOUNCEMENT', 'SECURITY_ALERT',
|
|
];
|
|
|
|
const optInTypes = ['WEB_FORM', 'VERBAL', 'PAPER_FORM', 'VIA_TEXT', 'MOBILE_QR_CODE', 'OTHER'];
|
|
|
|
interface CampaignDetailsStepProps {
|
|
data: any;
|
|
errors: Record<string, string>;
|
|
onChange: (data: any) => void;
|
|
}
|
|
|
|
export function CampaignDetailsStep({ data, errors, onChange }: CampaignDetailsStepProps) {
|
|
const addSampleMessage = () => {
|
|
if (data.sampleMessages.length < 5) {
|
|
onChange({ sampleMessages: [...data.sampleMessages, ''] });
|
|
}
|
|
};
|
|
|
|
const removeSampleMessage = (index: number) => {
|
|
onChange({
|
|
sampleMessages: data.sampleMessages.filter((_: any, i: number) => i !== index),
|
|
});
|
|
};
|
|
|
|
const updateSampleMessage = (index: number, value: string) => {
|
|
const updated = [...data.sampleMessages];
|
|
updated[index] = value;
|
|
onChange({ sampleMessages: updated });
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<FormSection
|
|
title="Campaign Details"
|
|
description="Describe your messaging campaign"
|
|
>
|
|
<SelectField
|
|
label="Use Case"
|
|
name="useCase"
|
|
value={data.useCase}
|
|
onChange={(v) => onChange({ useCase: v })}
|
|
options={useCases}
|
|
error={errors.useCase}
|
|
helpText="Primary purpose of your messages"
|
|
required
|
|
/>
|
|
|
|
<div>
|
|
<label
|
|
style={{
|
|
display: 'block',
|
|
fontSize: 'var(--font-size-sm)',
|
|
fontWeight: 600,
|
|
color: 'var(--color-text-primary)',
|
|
marginBottom: 'var(--spacing-2)',
|
|
}}
|
|
>
|
|
Campaign Description *
|
|
</label>
|
|
<textarea
|
|
value={data.description}
|
|
onChange={(e) => onChange({ description: e.target.value })}
|
|
placeholder="Describe what messages you'll send and why users would want them..."
|
|
rows={4}
|
|
style={{
|
|
width: '100%',
|
|
padding: 'var(--spacing-3)',
|
|
fontSize: 'var(--font-size-base)',
|
|
border: `1px solid ${errors.description ? 'var(--color-error)' : 'var(--color-border-primary)'}`,
|
|
borderRadius: 'var(--border-radius-md)',
|
|
}}
|
|
/>
|
|
{errors.description && (
|
|
<div style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-error)', marginTop: 'var(--spacing-1)' }}>
|
|
{errors.description}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
<label
|
|
style={{
|
|
display: 'block',
|
|
fontSize: 'var(--font-size-sm)',
|
|
fontWeight: 600,
|
|
color: 'var(--color-text-primary)',
|
|
marginBottom: 'var(--spacing-2)',
|
|
}}
|
|
>
|
|
Sample Messages * (1-5 examples)
|
|
</label>
|
|
{data.sampleMessages.map((msg: string, index: number) => (
|
|
<div key={index} style={{ marginBottom: 'var(--spacing-3)' }}>
|
|
<div style={{ display: 'flex', gap: 'var(--spacing-2)', marginBottom: 'var(--spacing-1)' }}>
|
|
<textarea
|
|
value={msg}
|
|
onChange={(e) => updateSampleMessage(index, e.target.value)}
|
|
placeholder={`Sample message ${index + 1}...`}
|
|
rows={2}
|
|
style={{ flex: 1 }}
|
|
/>
|
|
{data.sampleMessages.length > 1 && (
|
|
<Button variant="danger" size="sm" onClick={() => removeSampleMessage(index)}>
|
|
Remove
|
|
</Button>
|
|
)}
|
|
</div>
|
|
<div style={{ fontSize: 'var(--font-size-xs)', color: 'var(--color-text-tertiary)' }}>
|
|
{msg.length} characters
|
|
</div>
|
|
</div>
|
|
))}
|
|
{data.sampleMessages.length < 5 && (
|
|
<Button variant="secondary" size="sm" onClick={addSampleMessage}>
|
|
+ Add Sample Message
|
|
</Button>
|
|
)}
|
|
{errors.sampleMessages && (
|
|
<div style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-error)', marginTop: 'var(--spacing-1)' }}>
|
|
{errors.sampleMessages}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div>
|
|
<label
|
|
style={{
|
|
display: 'block',
|
|
fontSize: 'var(--font-size-sm)',
|
|
fontWeight: 600,
|
|
color: 'var(--color-text-primary)',
|
|
marginBottom: 'var(--spacing-2)',
|
|
}}
|
|
>
|
|
Message Flow *
|
|
</label>
|
|
<textarea
|
|
value={data.messageFlow}
|
|
onChange={(e) => onChange({ messageFlow: e.target.value })}
|
|
placeholder="Describe how users opt in to receive your messages..."
|
|
rows={3}
|
|
style={{
|
|
width: '100%',
|
|
padding: 'var(--spacing-3)',
|
|
fontSize: 'var(--font-size-base)',
|
|
border: `1px solid ${errors.messageFlow ? 'var(--color-error)' : 'var(--color-border-primary)'}`,
|
|
borderRadius: 'var(--border-radius-md)',
|
|
}}
|
|
/>
|
|
{errors.messageFlow && (
|
|
<div style={{ fontSize: 'var(--font-size-sm)', color: 'var(--color-error)', marginTop: 'var(--spacing-1)' }}>
|
|
{errors.messageFlow}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<SelectField
|
|
label="Opt-In Type"
|
|
name="optInType"
|
|
value={data.optInType}
|
|
onChange={(v) => onChange({ optInType: v })}
|
|
options={optInTypes}
|
|
error={errors.optInType}
|
|
required
|
|
/>
|
|
|
|
<FormField
|
|
label="Opt-In Confirmation Message"
|
|
name="optInMessage"
|
|
value={data.optInMessage}
|
|
onChange={(v) => onChange({ optInMessage: v })}
|
|
placeholder="Thanks for subscribing! Reply STOP to opt out."
|
|
error={errors.optInMessage}
|
|
helpText="Sent after user opts in"
|
|
required
|
|
/>
|
|
|
|
<FormField
|
|
label="Opt-Out Message"
|
|
name="optOutMessage"
|
|
value={data.optOutMessage}
|
|
onChange={(v) => onChange({ optOutMessage: v })}
|
|
placeholder="You've been unsubscribed. Reply START to rejoin."
|
|
error={errors.optOutMessage}
|
|
helpText="Response to STOP"
|
|
required
|
|
/>
|
|
|
|
<FormField
|
|
label="Help Message"
|
|
name="helpMessage"
|
|
value={data.helpMessage}
|
|
onChange={(v) => onChange({ helpMessage: v })}
|
|
placeholder="For support, contact us at help@example.com or reply STOP to unsubscribe."
|
|
error={errors.helpMessage}
|
|
helpText="Response to HELP"
|
|
required
|
|
/>
|
|
|
|
<div>
|
|
<label
|
|
style={{
|
|
display: 'block',
|
|
fontSize: 'var(--font-size-sm)',
|
|
fontWeight: 600,
|
|
color: 'var(--color-text-primary)',
|
|
marginBottom: 'var(--spacing-3)',
|
|
}}
|
|
>
|
|
Message Content
|
|
</label>
|
|
<label
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: 'var(--spacing-2)',
|
|
marginBottom: 'var(--spacing-2)',
|
|
cursor: 'pointer',
|
|
}}
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
checked={data.hasEmbeddedLinks}
|
|
onChange={(e) => onChange({ hasEmbeddedLinks: e.target.checked })}
|
|
/>
|
|
<span style={{ fontSize: 'var(--font-size-sm)' }}>Messages contain links/URLs</span>
|
|
</label>
|
|
<label
|
|
style={{
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: 'var(--spacing-2)',
|
|
cursor: 'pointer',
|
|
}}
|
|
>
|
|
<input
|
|
type="checkbox"
|
|
checked={data.hasEmbeddedPhone}
|
|
onChange={(e) => onChange({ hasEmbeddedPhone: e.target.checked })}
|
|
/>
|
|
<span style={{ fontSize: 'var(--font-size-sm)' }}>Messages contain phone numbers</span>
|
|
</label>
|
|
</div>
|
|
</FormSection>
|
|
</div>
|
|
);
|
|
}
|