Remove standalone Astro portal page — compliance orders now in Frappe
Compliance orders portal page moved to ERPNext/Frappe at: portal.performancewest.net/compliance-orders Includes USAC delegation confirmation via Frappe API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
438d3a6e2e
commit
783eeeb645
1 changed files with 0 additions and 264 deletions
|
|
@ -1,264 +0,0 @@
|
|||
---
|
||||
import Base from "../../layouts/Base.astro";
|
||||
---
|
||||
|
||||
<Base
|
||||
title="My Orders — Performance West"
|
||||
description="View and manage your compliance service orders with Performance West."
|
||||
>
|
||||
<main class="max-w-4xl mx-auto px-4 py-10">
|
||||
<!-- Breadcrumb -->
|
||||
<nav class="text-sm text-gray-500 mb-6" aria-label="Breadcrumb">
|
||||
<a href="/" class="hover:text-pw-600">Home</a>
|
||||
<span class="mx-1">/</span>
|
||||
<a href="/account" class="hover:text-pw-600">My Account</a>
|
||||
<span class="mx-1">/</span>
|
||||
<span class="text-gray-800 font-medium">Orders</span>
|
||||
</nav>
|
||||
|
||||
<h1 class="text-3xl font-bold mb-8" style="color:#1a2744;">My Orders</h1>
|
||||
|
||||
<!-- Loading state -->
|
||||
<div id="loading-state" style="text-align:center;padding:48px 0;">
|
||||
<div style="display:inline-block;width:40px;height:40px;border:4px solid #e5e7eb;border-top-color:#1a2744;border-radius:50%;animation:pw-spin 0.8s linear infinite;"></div>
|
||||
<p style="color:#6b7280;margin-top:12px;">Loading your orders…</p>
|
||||
</div>
|
||||
|
||||
<!-- Auth prompt (hidden by default) -->
|
||||
<div id="auth-state" style="display:none;text-align:center;padding:48px 0;">
|
||||
<p style="font-size:1.125rem;color:#374151;margin-bottom:16px;">Please sign in to view your orders.</p>
|
||||
<button
|
||||
id="login-btn"
|
||||
style="background:#1a2744;color:#fff;font-weight:700;padding:12px 32px;border-radius:8px;border:none;cursor:pointer;font-size:1rem;"
|
||||
>Sign In</button>
|
||||
</div>
|
||||
|
||||
<!-- Orders container -->
|
||||
<div id="orders-container" style="display:none;"></div>
|
||||
|
||||
<!-- Error state -->
|
||||
<div id="error-state" style="display:none;text-align:center;padding:48px 0;">
|
||||
<p style="color:#dc2626;font-size:1.125rem;" id="error-message">Something went wrong. Please try again later.</p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
@keyframes pw-spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="module" is:inline>
|
||||
var API = window.__PW_API || "https://api.performancewest.net";
|
||||
|
||||
var loadingEl = document.getElementById("loading-state");
|
||||
var authEl = document.getElementById("auth-state");
|
||||
var ordersEl = document.getElementById("orders-container");
|
||||
var errorEl = document.getElementById("error-state");
|
||||
var errorMsg = document.getElementById("error-message");
|
||||
var loginBtn = document.getElementById("login-btn");
|
||||
|
||||
loginBtn.addEventListener("click", function () {
|
||||
if (window.pwOpenAuth) window.pwOpenAuth("login");
|
||||
});
|
||||
|
||||
function formatCents(cents) {
|
||||
return "$" + (cents / 100).toFixed(2);
|
||||
}
|
||||
|
||||
function formatDate(d) {
|
||||
return new Date(d).toLocaleDateString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
function statusBadge(status) {
|
||||
var colors = {
|
||||
paid: "background:#dcfce7;color:#166534;",
|
||||
pending: "background:#fef9c3;color:#854d0e;",
|
||||
cancelled: "background:#fee2e2;color:#991b1b;",
|
||||
};
|
||||
var label = status.charAt(0).toUpperCase() + status.slice(1);
|
||||
var style = colors[status] || "background:#f3f4f6;color:#374151;";
|
||||
return (
|
||||
'<span style="display:inline-block;padding:2px 10px;border-radius:9999px;font-size:0.75rem;font-weight:600;' +
|
||||
style +
|
||||
'">' +
|
||||
label +
|
||||
"</span>"
|
||||
);
|
||||
}
|
||||
|
||||
function renderDelegationCallout(batchId) {
|
||||
return (
|
||||
'<div id="delegation-' + batchId + '" style="background:#fefce8;border:1px solid #facc15;border-radius:8px;padding:16px;margin-top:12px;">' +
|
||||
'<p style="font-weight:700;color:#854d0e;margin:0 0 8px;">Action Required: USAC E-File Delegation</p>' +
|
||||
'<ol style="margin:0 0 12px 20px;padding:0;color:#713f12;font-size:0.875rem;line-height:1.7;">' +
|
||||
"<li>Log in to USAC E-File</li>" +
|
||||
"<li>Delegate Access</li>" +
|
||||
"<li>Add <strong>filings@performancewest.net</strong></li>" +
|
||||
"<li>Grant read and file permissions</li>" +
|
||||
"</ol>" +
|
||||
'<button onclick="confirmDelegation(\'' + batchId + '\')" style="background:#1a2744;color:#fff;font-weight:700;padding:10px 24px;border-radius:8px;border:none;cursor:pointer;font-size:0.875rem;">' +
|
||||
"I've completed the delegation →" +
|
||||
"</button>" +
|
||||
"</div>"
|
||||
);
|
||||
}
|
||||
|
||||
window.confirmDelegation = async function (batchId) {
|
||||
var container = document.getElementById("delegation-" + batchId);
|
||||
if (!container) return;
|
||||
|
||||
var btn = container.querySelector("button");
|
||||
if (btn) {
|
||||
btn.disabled = true;
|
||||
btn.textContent = "Confirming\u2026";
|
||||
}
|
||||
|
||||
try {
|
||||
var res = await fetch(
|
||||
API + "/api/v1/compliance-orders/" + batchId + "/usac-delegation",
|
||||
{ method: "POST", credentials: "include" }
|
||||
);
|
||||
if (!res.ok) {
|
||||
var body = await res.json().catch(function () { return {}; });
|
||||
throw new Error(body.message || "Failed to confirm delegation");
|
||||
}
|
||||
container.innerHTML =
|
||||
'<div style="background:#dcfce7;border:1px solid #86efac;border-radius:8px;padding:16px;color:#166534;font-weight:600;">' +
|
||||
"\u2713 Delegation confirmed — we'll begin your filing within 1 business day" +
|
||||
"</div>";
|
||||
} catch (err) {
|
||||
if (btn) {
|
||||
btn.disabled = false;
|
||||
btn.textContent = "I've completed the delegation \u2192";
|
||||
}
|
||||
var errDiv = container.querySelector(".delegation-error");
|
||||
if (!errDiv) {
|
||||
errDiv = document.createElement("p");
|
||||
errDiv.className = "delegation-error";
|
||||
errDiv.style.cssText = "color:#dc2626;font-size:0.875rem;margin-top:8px;";
|
||||
container.appendChild(errDiv);
|
||||
}
|
||||
errDiv.textContent = err.message || "Something went wrong. Please try again.";
|
||||
}
|
||||
};
|
||||
|
||||
function renderOrderCard(batch) {
|
||||
var totalCents = 0;
|
||||
var servicesHtml = "";
|
||||
|
||||
for (var i = 0; i < batch.services.length; i++) {
|
||||
var s = batch.services[i];
|
||||
var net = s.service_fee_cents - (s.discount_cents || 0);
|
||||
totalCents += net;
|
||||
|
||||
servicesHtml +=
|
||||
'<div style="display:flex;justify-content:space-between;align-items:center;padding:8px 0;' +
|
||||
(i < batch.services.length - 1 ? "border-bottom:1px solid #f3f4f6;" : "") +
|
||||
'">' +
|
||||
'<div>' +
|
||||
'<span style="font-size:0.875rem;color:#374151;">' + s.service_name + "</span>" +
|
||||
(s.discount_cents > 0
|
||||
? ' <span style="font-size:0.75rem;color:#059669;">(-' + formatCents(s.discount_cents) + " discount)</span>"
|
||||
: "") +
|
||||
"</div>" +
|
||||
'<span style="font-size:0.875rem;font-weight:600;color:#374151;">' + formatCents(net) + "</span>" +
|
||||
"</div>";
|
||||
}
|
||||
|
||||
var delegationHtml = batch.needs_usac_delegation
|
||||
? renderDelegationCallout(batch.batch_id)
|
||||
: "";
|
||||
|
||||
return (
|
||||
'<div style="background:#fff;border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 1px 3px rgba(0,0,0,0.06);padding:24px;margin-bottom:16px;">' +
|
||||
'<div style="display:flex;justify-content:space-between;align-items:flex-start;flex-wrap:wrap;gap:8px;margin-bottom:12px;">' +
|
||||
"<div>" +
|
||||
'<p style="font-weight:700;font-size:1.125rem;color:#1a2744;margin:0;">' +
|
||||
batch.entity_name +
|
||||
(batch.frn ? ' <span style="font-weight:400;font-size:0.875rem;color:#6b7280;">FRN ' + batch.frn + "</span>" : "") +
|
||||
"</p>" +
|
||||
'<p style="font-family:monospace;font-size:0.8125rem;color:#6b7280;margin:4px 0 0;">' + batch.batch_id + "</p>" +
|
||||
"</div>" +
|
||||
"<div style=\"text-align:right;\">" +
|
||||
statusBadge(batch.payment_status) +
|
||||
'<p style="font-size:0.8125rem;color:#9ca3af;margin:6px 0 0;">' + formatDate(batch.created_at) + "</p>" +
|
||||
"</div>" +
|
||||
"</div>" +
|
||||
'<div style="border-top:1px solid #e5e7eb;padding-top:12px;">' +
|
||||
servicesHtml +
|
||||
"</div>" +
|
||||
'<div style="border-top:1px solid #e5e7eb;margin-top:8px;padding-top:12px;display:flex;justify-content:flex-end;">' +
|
||||
'<span style="font-weight:700;font-size:1rem;color:#1a2744;">Total: ' + formatCents(totalCents) + "</span>" +
|
||||
"</div>" +
|
||||
delegationHtml +
|
||||
"</div>"
|
||||
);
|
||||
}
|
||||
|
||||
function renderEmpty() {
|
||||
return (
|
||||
'<div style="text-align:center;padding:48px 0;">' +
|
||||
'<p style="font-size:1.125rem;color:#6b7280;margin-bottom:12px;">No orders yet.</p>' +
|
||||
'<a href="/tools/fcc-compliance-check" style="color:#1a2744;font-weight:600;text-decoration:underline;">Run a free FCC Compliance Check to get started.</a>' +
|
||||
"</div>"
|
||||
);
|
||||
}
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
var meRes = await fetch(API + "/api/v1/auth/me", {
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
if (!meRes.ok) {
|
||||
loadingEl.style.display = "none";
|
||||
authEl.style.display = "";
|
||||
if (window.pwOpenAuth) window.pwOpenAuth("login");
|
||||
return;
|
||||
}
|
||||
|
||||
var customer = await meRes.json();
|
||||
|
||||
var ordersRes = await fetch(
|
||||
API +
|
||||
"/api/v1/compliance-orders/my-orders?email=" +
|
||||
encodeURIComponent(customer.email),
|
||||
{ credentials: "include" }
|
||||
);
|
||||
|
||||
if (!ordersRes.ok) {
|
||||
throw new Error("Failed to load orders");
|
||||
}
|
||||
|
||||
var data = await ordersRes.json();
|
||||
var allBatches = (data.batches || []).concat(data.standalone || []);
|
||||
|
||||
loadingEl.style.display = "none";
|
||||
|
||||
if (allBatches.length === 0) {
|
||||
ordersEl.innerHTML = renderEmpty();
|
||||
ordersEl.style.display = "";
|
||||
return;
|
||||
}
|
||||
|
||||
var html = "";
|
||||
for (var i = 0; i < allBatches.length; i++) {
|
||||
html += renderOrderCard(allBatches[i]);
|
||||
}
|
||||
ordersEl.innerHTML = html;
|
||||
ordersEl.style.display = "";
|
||||
} catch (err) {
|
||||
loadingEl.style.display = "none";
|
||||
errorMsg.textContent = err.message || "Something went wrong. Please try again later.";
|
||||
errorEl.style.display = "";
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
</script>
|
||||
</Base>
|
||||
Loading…
Reference in a new issue