- CPNI: requires either clean compliance checkbox OR issues section opened - STIR/SHAKEN: requires selecting implementation status before advancing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
168 lines
6.8 KiB
Text
168 lines
6.8 KiB
Text
---
|
|
// CPNIStep — simplified CPNI certification confirmation.
|
|
// Most carriers have zero complaints/breaches/issues. Default to "all clean"
|
|
// with a single checkbox to confirm, and an expandable section for carriers
|
|
// that DID have issues during the reporting period.
|
|
---
|
|
|
|
<div class="pw-step">
|
|
<h2>CPNI Certification — <span id="pw-cpni-year"></span></h2>
|
|
<p class="pw-help">
|
|
Confirm your CPNI compliance status for the reporting period.
|
|
Most carriers can certify clean compliance — just confirm below.
|
|
</p>
|
|
|
|
<label class="pw-confirm-box">
|
|
<input type="checkbox" id="pw-cpni-clean" checked />
|
|
<div>
|
|
<strong>I confirm clean CPNI compliance for this reporting period:</strong>
|
|
<ul>
|
|
<li>No complaints regarding unauthorized release or use of CPNI</li>
|
|
<li>No data breaches involving CPNI</li>
|
|
<li>No employee disciplinary actions for CPNI violations</li>
|
|
<li>No unauthorized data broker access to CPNI</li>
|
|
<li>We do not use CPNI for marketing beyond subscribed services</li>
|
|
</ul>
|
|
</div>
|
|
</label>
|
|
|
|
<details id="pw-cpni-issues" class="pw-issues">
|
|
<summary>I had compliance issues during this period (complaints, breaches, etc.)</summary>
|
|
<div class="pw-issues-body">
|
|
|
|
<label class="pw-field">CPNI complaints received
|
|
<input type="number" id="pw-cpni-complaints-count" min="0" value="0" />
|
|
</label>
|
|
<label class="pw-field">Complaint details (if any)
|
|
<textarea id="pw-cpni-complaints-desc" rows="2" placeholder="Nature of complaints and how resolved"></textarea>
|
|
</label>
|
|
|
|
<label class="pw-field">Data breaches involving CPNI
|
|
<input type="number" id="pw-cpni-breaches-count" min="0" value="0" />
|
|
</label>
|
|
<label class="pw-field">Breach details (if any)
|
|
<textarea id="pw-cpni-breaches-desc" rows="2" placeholder="Description, notifications filed per 47 CFR § 64.2011"></textarea>
|
|
</label>
|
|
|
|
<label class="pw-field">Employees disciplined for CPNI violations
|
|
<input type="number" id="pw-cpni-disciplinary-count" min="0" value="0" />
|
|
</label>
|
|
|
|
<label class="pw-field">Data broker issues
|
|
<textarea id="pw-cpni-brokers-desc" rows="2" placeholder="Actions taken against data brokers, if any"></textarea>
|
|
</label>
|
|
|
|
<label class="pw-field">CPNI marketing usage
|
|
<select id="pw-cpni-marketing">
|
|
<option value="no">No — we do not use CPNI for marketing</option>
|
|
<option value="opt_in">Yes — with opt-in customer approval</option>
|
|
<option value="opt_out">Yes — with opt-out customer approval</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
</details>
|
|
|
|
<div id="pw-cpni-err" class="pw-err" hidden></div>
|
|
</div>
|
|
|
|
<style>
|
|
.pw-step h2 { margin: 0 0 0.5rem; color: #1a2744; }
|
|
.pw-help { color: #64748b; font-size: 0.9rem; margin-bottom: 1rem; }
|
|
.pw-confirm-box {
|
|
display: flex; gap: 0.75rem; padding: 1rem; background: #f0fdf4;
|
|
border: 1px solid #86efac; border-radius: 8px; cursor: pointer;
|
|
margin-bottom: 1rem; align-items: flex-start;
|
|
}
|
|
.pw-confirm-box input { margin-top: 0.3rem; width: 18px; height: 18px; accent-color: #059669; }
|
|
.pw-confirm-box strong { display: block; font-size: 0.9rem; color: #065f46; margin-bottom: 0.3rem; }
|
|
.pw-confirm-box ul { margin: 0; padding-left: 1.25rem; font-size: 0.82rem; color: #047857; }
|
|
.pw-confirm-box li { margin: 0.15rem 0; }
|
|
.pw-issues { margin-top: 0.5rem; }
|
|
.pw-issues summary {
|
|
cursor: pointer; font-size: 0.85rem; color: #b45309; font-weight: 600;
|
|
padding: 0.5rem 0;
|
|
}
|
|
.pw-issues-body {
|
|
padding: 0.75rem; background: #fffbeb; border: 1px solid #fde68a;
|
|
border-radius: 6px; margin-top: 0.5rem;
|
|
}
|
|
.pw-field { display: block; font-size: 0.82rem; color: #475569; margin-bottom: 0.6rem; }
|
|
.pw-field input, .pw-field textarea, .pw-field select {
|
|
display: block; width: 100%; padding: 0.4rem 0.5rem;
|
|
border: 1px solid #cbd5e1; border-radius: 4px; font-size: 0.85rem; margin-top: 0.2rem;
|
|
}
|
|
.pw-field input[type="number"] { width: 80px; }
|
|
.pw-err { color: #b91c1c; margin-top: 0.75rem; font-size: 0.9rem; }
|
|
</style>
|
|
|
|
<script>
|
|
const yearEl = document.getElementById("pw-cpni-year")!;
|
|
const reportingYear = new Date().getFullYear() - 1;
|
|
yearEl.textContent = String(reportingYear);
|
|
|
|
const cleanBox = document.getElementById("pw-cpni-clean") as HTMLInputElement;
|
|
const issuesEl = document.getElementById("pw-cpni-issues") as HTMLDetailsElement;
|
|
|
|
// When "clean" is unchecked, auto-open issues
|
|
cleanBox.addEventListener("change", () => {
|
|
if (!cleanBox.checked) issuesEl.open = true;
|
|
});
|
|
// When issues are opened, uncheck clean
|
|
issuesEl.addEventListener("toggle", () => {
|
|
if (issuesEl.open && cleanBox.checked) {
|
|
// Only uncheck if they actually entered something
|
|
}
|
|
});
|
|
|
|
window.addEventListener("pw:step-next", (evt: any) => {
|
|
const PW = (window as any).PWIntake;
|
|
if (PW.steps[PW.get().step_index] !== "cpni_questions") return;
|
|
|
|
const errEl = document.getElementById("pw-cpni-err") as HTMLDivElement;
|
|
|
|
// Validate: if not clean, must have opened issues section
|
|
if (!cleanBox.checked && !issuesEl.open) {
|
|
errEl.hidden = false;
|
|
errEl.textContent = "Please either confirm clean compliance or expand the issues section and provide details.";
|
|
evt.preventDefault();
|
|
return;
|
|
}
|
|
errEl.hidden = true;
|
|
|
|
const g = (id: string) => (document.getElementById(id) as HTMLInputElement)?.value || "";
|
|
|
|
if (cleanBox.checked) {
|
|
// Clean compliance — simple case
|
|
PW.patchIntakeData({
|
|
cpni: {
|
|
reporting_year: reportingYear,
|
|
clean_compliance: true,
|
|
complaints: "no", complaints_count: 0,
|
|
breaches: "no", breaches_count: 0,
|
|
disciplinary: "no", disciplinary_count: 0,
|
|
data_brokers: "no",
|
|
marketing_usage: "no",
|
|
},
|
|
});
|
|
} else {
|
|
// Has issues — collect details
|
|
PW.patchIntakeData({
|
|
cpni: {
|
|
reporting_year: reportingYear,
|
|
clean_compliance: false,
|
|
complaints: parseInt(g("pw-cpni-complaints-count")) > 0 ? "yes" : "no",
|
|
complaints_count: parseInt(g("pw-cpni-complaints-count")) || 0,
|
|
complaints_description: g("pw-cpni-complaints-desc"),
|
|
breaches: parseInt(g("pw-cpni-breaches-count")) > 0 ? "yes" : "no",
|
|
breaches_count: parseInt(g("pw-cpni-breaches-count")) || 0,
|
|
breaches_description: g("pw-cpni-breaches-desc"),
|
|
disciplinary: parseInt(g("pw-cpni-disciplinary-count")) > 0 ? "yes" : "no",
|
|
disciplinary_count: parseInt(g("pw-cpni-disciplinary-count")) || 0,
|
|
data_brokers: g("pw-cpni-brokers-desc").trim() ? "yes" : "no",
|
|
data_brokers_description: g("pw-cpni-brokers-desc"),
|
|
marketing_usage: g("pw-cpni-marketing"),
|
|
},
|
|
});
|
|
}
|
|
});
|
|
</script>
|