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>
140 lines
7 KiB
Text
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 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>
|