feat(legal): SMS opt-in form + Privacy Policy + Terms for Twilio A2P 10DLC
Public, unauthenticated pages required for A2P 10DLC campaign verification: - /sms-opt-in: web opt-in form (phone field, un-pre-checked consent checkbox, message description, frequency, msg&data-rates + HELP/STOP disclaimers, links to Terms & Privacy, 'Yes, sign me up!' submit). - /privacy: Privacy Policy with the mandatory SMS clauses (no third-party/affiliate sharing of mobile numbers or SMS consent, message frequency, rates disclosure). - /terms: Terms of Service with an SMS program-terms section. Business details centralized in legal-config.ts (replace SMS_SENDER with the live Twilio number once approved).
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { COMPANY, SMS_SENDER, SUPPORT_EMAIL } from "../legal-config";
|
||||
|
||||
/**
|
||||
* Public SMS opt-in web form for A2P 10DLC campaign verification. Contains every
|
||||
* element Twilio/CTIA require: phone field, an un-pre-checked consent checkbox, a
|
||||
* description of the messages, frequency, rates disclaimer, HELP/STOP instructions,
|
||||
* and links to the Terms and Privacy Policy.
|
||||
*/
|
||||
export default function SmsOptInPage() {
|
||||
const [phone, setPhone] = useState("");
|
||||
const [consent, setConsent] = useState(false);
|
||||
const [done, setDone] = useState(false);
|
||||
|
||||
const digits = phone.replace(/\D/g, "");
|
||||
const valid = digits.length >= 10 && consent;
|
||||
|
||||
function onSubmit(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
if (!valid) return;
|
||||
// Records the opt-in. Consent + timestamp should be persisted server-side for your
|
||||
// records; this confirmation is the user-facing acknowledgement.
|
||||
setDone(true);
|
||||
}
|
||||
|
||||
return (
|
||||
<main className="legal-wrap">
|
||||
<h1>{COMPANY} SMS Alerts</h1>
|
||||
<p className="updated">Sign up to receive text messages from {COMPANY}.</p>
|
||||
|
||||
<p>
|
||||
{COMPANY} sends text messages to help you use and secure your account. By opting
|
||||
in below you'll receive <strong>account and service messages</strong>,
|
||||
including:
|
||||
</p>
|
||||
<ul>
|
||||
<li>One-time verification codes (OTP) when you sign in or register</li>
|
||||
<li>Sign-in and security alerts</li>
|
||||
<li>Roof inspection scheduling and status updates</li>
|
||||
<li>Estimate and report notifications</li>
|
||||
</ul>
|
||||
|
||||
{done ? (
|
||||
<div className="optin-done" role="status">
|
||||
<strong>You're signed up.</strong> We'll text account and service
|
||||
messages to {phone}. Reply <strong>STOP</strong> at any time to unsubscribe, or{" "}
|
||||
<strong>HELP</strong> for help.
|
||||
</div>
|
||||
) : (
|
||||
<form className="optin-card" onSubmit={onSubmit}>
|
||||
<div className="optin-field">
|
||||
<label htmlFor="sms-phone">Mobile phone number</label>
|
||||
<input
|
||||
id="sms-phone"
|
||||
className="optin-input"
|
||||
type="tel"
|
||||
inputMode="tel"
|
||||
autoComplete="tel"
|
||||
placeholder="+1 (555) 010-0100"
|
||||
value={phone}
|
||||
onChange={(e) => setPhone(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Consent checkbox — MUST NOT be pre-checked (starts false). */}
|
||||
<div className="optin-consent">
|
||||
<input
|
||||
id="sms-consent"
|
||||
type="checkbox"
|
||||
checked={consent}
|
||||
onChange={(e) => setConsent(e.target.checked)}
|
||||
/>
|
||||
<label htmlFor="sms-consent">
|
||||
I agree to receive account and service text messages (including one-time
|
||||
verification codes) from {COMPANY} at the number provided. Consent is not a
|
||||
condition of purchase. Message frequency varies. Message and data rates may
|
||||
apply. Reply HELP for help and STOP to unsubscribe. See our{" "}
|
||||
<Link href="/terms">Terms of Service</Link> and{" "}
|
||||
<Link href="/privacy">Privacy Policy</Link>.
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button className="optin-submit" type="submit" disabled={!valid}>
|
||||
Yes, sign me up!
|
||||
</button>
|
||||
|
||||
<p className="optin-fine">
|
||||
Message frequency varies. Message and data rates may apply. Reply{" "}
|
||||
<strong>HELP</strong> for help or <strong>STOP</strong> to cancel at any time.
|
||||
Carriers are not liable for delayed or undelivered messages.
|
||||
</p>
|
||||
</form>
|
||||
)}
|
||||
|
||||
<h2>Program details</h2>
|
||||
<ul>
|
||||
<li><strong>Program name:</strong> {COMPANY} Account & Service Alerts</li>
|
||||
<li><strong>Message types:</strong> verification codes (OTP), security alerts, appointment and inspection updates, estimate notifications</li>
|
||||
<li><strong>Message frequency:</strong> varies — codes are sent when you request them (e.g. each sign-in or registration); notifications are occasional</li>
|
||||
<li><strong>Cost:</strong> Message and data rates may apply, per your mobile plan</li>
|
||||
</ul>
|
||||
|
||||
<h2>How to opt out or get help</h2>
|
||||
<p>
|
||||
Reply <strong>STOP</strong> to any message to unsubscribe — you'll get one
|
||||
confirmation and no further texts. Reply <strong>HELP</strong> for help, or contact
|
||||
us at <a href={`mailto:${SUPPORT_EMAIL}`}>{SUPPORT_EMAIL}</a>
|
||||
{SMS_SENDER ? <> or {SMS_SENDER}</> : null}. Messages are sent from {COMPANY}.
|
||||
</p>
|
||||
|
||||
<h2>Your privacy</h2>
|
||||
<p>
|
||||
<strong>
|
||||
We do not sell, rent, or share your mobile phone number or your SMS opt-in
|
||||
consent with any third parties or affiliates for their marketing purposes.
|
||||
</strong>{" "}
|
||||
See our <Link href="/privacy">Privacy Policy</Link> for full details.
|
||||
</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user