80 lines
2.3 KiB
TypeScript
80 lines
2.3 KiB
TypeScript
import { render } from "preact";
|
|
import { useEffect, useState } from "preact/hooks";
|
|
|
|
interface InvoiceRow {
|
|
id: string;
|
|
invoiceNumber: string;
|
|
version: number;
|
|
kind: string;
|
|
issuedAt: string;
|
|
status: string;
|
|
pdfUrl: string;
|
|
cancelledAt: string | null;
|
|
sentAt: string | null;
|
|
}
|
|
|
|
interface Payload {
|
|
latest: InvoiceRow | null;
|
|
history: InvoiceRow[];
|
|
}
|
|
|
|
export default async () => {
|
|
render(<Extension />, document.body);
|
|
};
|
|
|
|
function Extension() {
|
|
const { data } = (globalThis as any).shopify;
|
|
const orderGid: string | undefined = data?.selected?.[0]?.id;
|
|
const orderId = orderGid ? orderGid.split("/").pop() : undefined;
|
|
|
|
const [payload, setPayload] = useState<Payload | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!orderId) return;
|
|
let cancelled = false;
|
|
(async () => {
|
|
try {
|
|
const res = await fetch(`/api/orders/${orderId}/invoice`);
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
const json: Payload = await res.json();
|
|
if (!cancelled) setPayload(json);
|
|
} catch (e: any) {
|
|
if (!cancelled) setError(e?.message ?? "Failed to load");
|
|
} finally {
|
|
if (!cancelled) setLoading(false);
|
|
}
|
|
})();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [orderId]);
|
|
|
|
return (
|
|
<s-admin-block heading="Invoice">
|
|
{loading ? (
|
|
<s-text>Loading…</s-text>
|
|
) : error ? (
|
|
<s-banner tone="critical">{error}</s-banner>
|
|
) : !payload?.latest ? (
|
|
<s-text>No invoice yet for this order.</s-text>
|
|
) : (
|
|
<s-stack gap="200">
|
|
<s-text weight="bold">{payload.latest.invoiceNumber} (v{payload.latest.version})</s-text>
|
|
<s-text>Issued {new Date(payload.latest.issuedAt).toLocaleDateString()}</s-text>
|
|
<s-badge tone={payload.latest.sentAt ? "success" : "info"}>
|
|
{payload.latest.sentAt ? "Sent" : "Not sent"}
|
|
</s-badge>
|
|
{payload.latest.pdfUrl ? (
|
|
<s-link href={payload.latest.pdfUrl} target="_blank">View PDF</s-link>
|
|
) : null}
|
|
{payload.history.length > 1 ? (
|
|
<s-text tone="subdued">{payload.history.length} versions in history</s-text>
|
|
) : null}
|
|
</s-stack>
|
|
)}
|
|
</s-admin-block>
|
|
);
|
|
}
|