File size: 5,496 Bytes
b1324bf
32f4dc0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b1324bf
32f4dc0
 
 
 
 
 
 
b1324bf
32f4dc0
 
 
 
 
 
b1324bf
7fe6a68
32f4dc0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import React, { useEffect, useState } from 'react';
import { Loader2 } from 'lucide-react';

/**
 * Public embed page (iframe). No sign-in required.
 * Route: /embed/cta/:publicId
 */
export default function CtaFormEmbed() {
    const publicId = window.location.pathname.split('/').filter(Boolean).pop() || '';
    const [phase, setPhase] = useState('loading');
    const [form, setForm] = useState(null);
    const [error, setError] = useState('');
    const [values, setValues] = useState({});
    const [submitting, setSubmitting] = useState(false);
    const [done, setDone] = useState(false);
    const [doneMsg, setDoneMsg] = useState('');

    useEffect(() => {
        if (!publicId) {
            setError('Invalid form link');
            setPhase('error');
            return;
        }
        fetch(`/api/public/cta-forms/${encodeURIComponent(publicId)}`)
            .then((r) => r.json().then((j) => ({ ok: r.ok, j })))
            .then(({ ok, j }) => {
                if (!ok) throw new Error(j.detail || 'Form not found');
                setForm(j);
                const init = {};
                (j.fields || []).forEach((f) => {
                    init[f.key] = '';
                });
                setValues(init);
                setPhase('ready');
            })
            .catch((e) => {
                setError(e.message || 'Could not load form');
                setPhase('error');
            });
    }, [publicId]);

    const onChange = (key, val) => {
        setValues((prev) => ({ ...prev, [key]: val }));
    };

    const onSubmit = async (e) => {
        e.preventDefault();
        setSubmitting(true);
        setError('');
        try {
            const res = await fetch(`/api/public/cta-forms/${encodeURIComponent(publicId)}/submit`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    fields: values,
                    page_url: window.location.href,
                }),
            });
            const data = await res.json().catch(() => ({}));
            if (!res.ok) {
                throw new Error(
                    typeof data.detail === 'string' ? data.detail : 'Submission failed'
                );
            }
            setDone(true);
            setDoneMsg(data.message || 'Thank you — we will be in touch soon.');
        } catch (err) {
            setError(err.message || 'Submission failed');
        } finally {
            setSubmitting(false);
        }
    };

    if (phase === 'loading') {
        return (
            <div className="flex min-h-[12rem] items-center justify-center bg-transparent p-4">
                <Loader2 className="h-8 w-8 animate-spin text-violet-600" />
            </div>
        );
    }

    if (phase === 'error') {
        return (
            <div className="flex min-h-[12rem] items-center justify-center bg-transparent p-4">
                <p className="text-sm text-red-600">{error}</p>
            </div>
        );
    }

    return (
        <div className="bg-transparent p-4">
            <div className="mx-auto w-full max-w-lg rounded-xl border border-slate-200 bg-white p-4">
                {done ? (
                    <p className="text-sm text-green-700 text-center py-6">{doneMsg}</p>
                ) : (
                    <form onSubmit={onSubmit} className="space-y-3">
                        {(form.fields || []).map((f) => (
                            <div key={f.key}>
                                <label className="block text-xs font-medium text-slate-600 mb-1">
                                    {f.label}
                                    {f.required ? ' *' : ''}
                                </label>
                                {f.type === 'textarea' ? (
                                    <textarea
                                        className="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm"
                                        rows={4}
                                        required={!!f.required}
                                        value={values[f.key] || ''}
                                        onChange={(e) => onChange(f.key, e.target.value)}
                                    />
                                ) : (
                                    <input
                                        type={f.type || 'text'}
                                        className="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm"
                                        required={!!f.required}
                                        value={values[f.key] || ''}
                                        onChange={(e) => onChange(f.key, e.target.value)}
                                    />
                                )}
                            </div>
                        ))}
                        {error ? <p className="text-xs text-red-600">{error}</p> : null}
                        <button
                            type="submit"
                            disabled={submitting}
                            className="w-full rounded-lg bg-violet-600 py-2.5 text-sm font-semibold text-white hover:bg-violet-700 disabled:opacity-60"
                        >
                            {submitting ? 'Sending…' : 'Submit'}
                        </button>
                    </form>
                )}
            </div>
        </div>
    );
}