new-site/site/src/components/intake/steps/EarthStationStep.astro
justin f8cd37ac8c Initial commit — Performance West telecom compliance platform
Includes: API (Express/TypeScript), Astro site, Python workers,
document generators, FCC compliance tools, Canada CRTC formation,
Ansible infrastructure, and deployment scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 06:54:22 -05:00

140 lines
7 KiB
Text

---
// EarthStationStep — satellite license info + private-line circuit inventory.
// Shown when line_105_categories contains 'satellite' or 'private_line'.
---
<div class="pw-step">
<h2>Earth station / Private line details</h2>
<p class="pw-help">
Satellite and private-line services have specialized 499-A revenue
line treatment. We'll collect your earth-station licensing and/or
private-line circuit inventory here.
</p>
<section class="pw-block" id="pw-sat-section">
<h3>Satellite licensing</h3>
<label class="pw-field">Earth station FCC License IDs (one per line, IB-format)</label>
<textarea id="pw-sat-licenses" class="pw-input" rows="3" placeholder="e.g., E123456&#10;E123457"></textarea>
<div class="pw-row">
<div><label class="pw-field">Orbital slot</label>
<input id="pw-sat-slot" class="pw-input" placeholder="e.g., W 73.0" /></div>
<div><label class="pw-field">Satellite operator</label>
<input id="pw-sat-op" class="pw-input" placeholder="e.g., Intelsat" /></div>
<div><label class="pw-field">Service type</label>
<select id="pw-sat-type" class="pw-input">
<option value="">—</option>
<option value="FSS">FSS (fixed satellite)</option>
<option value="MSS">MSS (mobile satellite)</option>
<option value="BSS">BSS (broadcast satellite)</option>
</select></div>
</div>
<label class="pw-field">US MSS subscribers (if MSS)</label>
<input type="number" id="pw-sat-mss-subs" class="pw-input" min="0" />
</section>
<section class="pw-block">
<h3>Private line circuit inventory</h3>
<p class="pw-help">
One row per DS-1 / DS-3 / Ethernet circuit. Required for Lines
305.1, 305.2 (resold) and 416 (retail). You can paste a CSV or add
rows manually.
</p>
<div id="pw-pl-circuits"></div>
<button type="button" class="pw-btn-plain pw-btn" id="pw-add-circuit">+ Add circuit</button>
</section>
</div>
<style>
.pw-step h2 { margin: 0 0 0.5rem; color: #1a2744; }
.pw-step h3 { margin: 0 0 0.4rem; color: #1a2744; font-size: 1rem; }
.pw-help { color: #64748b; font-size: 0.9rem; margin-bottom: 1rem; }
.pw-block { padding: 1rem; border: 1px solid #e2e8f0; border-radius: 8px; margin-bottom: 0.75rem; }
.pw-field { display: block; font-weight: 600; color: #1f2937; margin: 0.4rem 0 0.15rem; font-size: 0.82rem; }
.pw-input { width: 100%; padding: 0.5rem 0.7rem; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 0.9rem; font-family: inherit; }
.pw-row { display: flex; gap: 1rem; flex-wrap: wrap; }
.pw-row > * { flex: 1 1 150px; }
.pw-btn { padding: 0.4rem 0.9rem; border: 0; border-radius: 6px; font-size: 0.85rem; cursor: pointer; margin-top: 0.3rem; }
.pw-btn-plain { background: #e2e8f0; color: #1f2937; }
.pw-btn-danger { background: #fee2e2; color: #991b1b; padding: 0.25rem 0.7rem; font-size: 0.78rem; }
.pw-circuit {
padding: 0.5rem; background: #f8fafc; border: 1px solid #e2e8f0;
border-radius: 6px; margin-bottom: 0.4rem;
display: grid; grid-template-columns: 1fr 1fr 1fr 140px 90px 90px; gap: 0.5rem;
}
</style>
<script>
const circuitsDiv = document.getElementById("pw-pl-circuits") as HTMLElement;
function newCircuitRow(init: any = {}) {
const row = document.createElement("div");
row.className = "pw-circuit";
row.innerHTML = `
<input data-f="endpoint_a_city" class="pw-input" placeholder="A: city, state" value="${init.endpoint_a_city || ""}" />
<input data-f="endpoint_b_city" class="pw-input" placeholder="B: city, state" value="${init.endpoint_b_city || ""}" />
<input data-f="bandwidth" class="pw-input" placeholder="e.g., DS-3, 10 GigE" value="${init.bandwidth || ""}" />
<input data-f="monthly_rev_usd" class="pw-input" placeholder="Monthly $ USD" type="number" step="0.01" value="${init.monthly_rev_usd || ""}" />
<label style="font-size:.75rem;">
<input type="checkbox" data-f="is_international" ${init.is_international ? "checked" : ""}/> Intl
</label>
<button type="button" class="pw-btn pw-btn-danger" data-remove="1">✕</button>
`;
row.querySelector<HTMLButtonElement>("[data-remove]")!.addEventListener("click", () => row.remove());
return row;
}
document.getElementById("pw-add-circuit")!.addEventListener("click", () => {
circuitsDiv.appendChild(newCircuitRow());
});
window.addEventListener("pw:step-shown", (evt: any) => {
if (evt.detail.step !== "earth_station") return;
const s = (window as any).PWIntake.get();
const sat = s.intake_data?.satellite_meta || {};
(document.getElementById("pw-sat-licenses") as HTMLTextAreaElement).value =
(sat.earth_station_license_ids || []).join("\n");
(document.getElementById("pw-sat-slot") as HTMLInputElement).value = sat.orbital_slot || "";
(document.getElementById("pw-sat-op") as HTMLInputElement).value = sat.satellite_operator || "";
(document.getElementById("pw-sat-type") as HTMLSelectElement).value = sat.service_type || "";
(document.getElementById("pw-sat-mss-subs") as HTMLInputElement).value = sat.mss_us_subscribers ?? "";
circuitsDiv.innerHTML = "";
for (const c of s.intake_data?.private_line_circuits || []) {
circuitsDiv.appendChild(newCircuitRow(c));
}
// Hide satellite section if this filer is only private_line, not satellite
const cats = s.intake_data?.line_105_categories || [];
const hasSat = cats.some((c: any) => c.id === "satellite" || c.id === "mobile_satellite");
const satSection = document.getElementById("pw-sat-section") as HTMLElement;
satSection.hidden = !hasSat;
});
window.addEventListener("pw:step-next", (evt: any) => {
const PW = (window as any).PWIntake;
if (PW.steps[PW.get().step_index] !== "earth_station") return;
const sat = {
earth_station_license_ids: (document.getElementById("pw-sat-licenses") as HTMLTextAreaElement).value
.split("\n").map((s) => s.trim()).filter(Boolean),
orbital_slot: (document.getElementById("pw-sat-slot") as HTMLInputElement).value.trim() || null,
satellite_operator: (document.getElementById("pw-sat-op") as HTMLInputElement).value.trim() || null,
service_type: (document.getElementById("pw-sat-type") as HTMLSelectElement).value || null,
mss_us_subscribers: Number((document.getElementById("pw-sat-mss-subs") as HTMLInputElement).value) || null,
};
const circuits: any[] = [];
for (const row of Array.from(circuitsDiv.querySelectorAll<HTMLElement>(".pw-circuit"))) {
const c: Record<string, any> = {};
for (const inp of Array.from(row.querySelectorAll<HTMLInputElement>("[data-f]"))) {
c[inp.getAttribute("data-f")!] = inp.type === "checkbox" ? inp.checked : inp.value;
}
if (!c.endpoint_a_city && !c.endpoint_b_city) continue;
c.monthly_rev_cents = Math.round((Number(c.monthly_rev_usd) || 0) * 100);
delete c.monthly_rev_usd;
circuits.push(c);
}
PW.patchIntakeData({
satellite_meta: sat,
private_line_circuits: circuits,
});
});
</script>