All checks were successful
build-and-release / build (push) Successful in 2m15s
- middleware.ts: default GATEWAY_URL to localhost:9100 (was K8s hostname); proxy all /api/* to gateway instead of falling through to SolidStart renderer - signup/index.tsx: fix kebab-case style property (align-items) - dashboard/CreditsPage.tsx: fix IIFE closing in Show children - AskAsh/index.ts: use ~ alias to fix bundler module resolution - vite.config.ts: add string type to rewrite callback - global.d.ts: add Window.__captchaCode and __testMode declarations - help-center/article/[slug].tsx: add missing ContentBlock import - test/setup.ts: add vitest/globals reference, fix IntersectionObserver mock - tests/e2e: fix implicit any, string|null/undefined type errors in e2e specs - tests/tsconfig.json: add tests-specific tsconfig with node types Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
134 lines
4.8 KiB
TypeScript
134 lines
4.8 KiB
TypeScript
import { createResource, Show, For } from "solid-js";
|
|
import { A } from "@solidjs/router";
|
|
import DashboardLayout from "~/components/DashboardLayout";
|
|
import { api } from "~/lib/api";
|
|
|
|
interface InvoiceSummary {
|
|
id: string;
|
|
invoice_number: string;
|
|
payment_id: string;
|
|
status: string;
|
|
currency: string;
|
|
subtotal: number;
|
|
cgst_amount: number;
|
|
sgst_amount: number;
|
|
igst_amount: number;
|
|
total: number;
|
|
issued_at: string;
|
|
paid_at: string | null;
|
|
pdf_object_key: string | null;
|
|
package_name: string | null;
|
|
}
|
|
|
|
interface InvoicesResponse {
|
|
invoices: InvoiceSummary[];
|
|
page: number;
|
|
limit: number;
|
|
}
|
|
|
|
export default function InvoicesPage() {
|
|
const [data] = createResource(async () => {
|
|
const res = await api.get<InvoicesResponse>("/wallet/me/invoices");
|
|
return res.data;
|
|
});
|
|
|
|
const formatDate = (date: string) => {
|
|
return new Date(date).toLocaleDateString("en-IN", {
|
|
day: "numeric",
|
|
month: "short",
|
|
year: "numeric",
|
|
});
|
|
};
|
|
|
|
const formatPrice = (paise: number) => {
|
|
return new Intl.NumberFormat("en-IN", {
|
|
style: "currency",
|
|
currency: "INR",
|
|
minimumFractionDigits: 0,
|
|
}).format(paise / 100);
|
|
};
|
|
|
|
return (
|
|
<DashboardLayout>
|
|
<div class="p-6 max-w-5xl mx-auto">
|
|
<h1 class="text-2xl font-bold mb-2">Invoices</h1>
|
|
<p class="text-gray-600 mb-6">All your Tracecoin purchase invoices</p>
|
|
|
|
<Show when={data.loading}>
|
|
<div class="text-center py-12">
|
|
<div class="animate-spin w-8 h-8 border-2 border-orange-500 border-t-transparent rounded-full mx-auto mb-4" />
|
|
<p class="text-gray-500">Loading invoices...</p>
|
|
</div>
|
|
</Show>
|
|
|
|
<Show when={data()} keyed>
|
|
{(d) => (
|
|
<Show
|
|
when={d.invoices.length > 0}
|
|
fallback={
|
|
<div class="bg-white border rounded-xl p-12 text-center">
|
|
<p class="text-gray-500 text-lg">No invoices yet.</p>
|
|
<p class="text-gray-400 text-sm mt-2">
|
|
Invoices are generated automatically when you purchase a Tracecoin package.
|
|
</p>
|
|
</div>
|
|
}
|
|
>
|
|
<div class="bg-white border rounded-xl overflow-hidden">
|
|
<table class="w-full text-sm">
|
|
<thead>
|
|
<tr class="text-left text-xs uppercase text-gray-500 border-b bg-gray-50">
|
|
<th class="px-4 py-3">Invoice #</th>
|
|
<th class="px-4 py-3">Package</th>
|
|
<th class="px-4 py-3">Date</th>
|
|
<th class="px-4 py-3 text-right">Total</th>
|
|
<th class="px-4 py-3">Status</th>
|
|
<th class="px-4 py-3"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<For each={d.invoices}>
|
|
{(inv) => (
|
|
<tr class="border-b hover:bg-gray-50">
|
|
<td class="px-4 py-3 font-mono text-sm">{inv.invoice_number}</td>
|
|
<td class="px-4 py-3">{inv.package_name ?? "Tracecoin purchase"}</td>
|
|
<td class="px-4 py-3 text-gray-600">{formatDate(inv.issued_at)}</td>
|
|
<td class="px-4 py-3 text-right font-medium">
|
|
{formatPrice(inv.total)}
|
|
</td>
|
|
<td class="px-4 py-3">
|
|
<span
|
|
class={`inline-block px-2 py-1 rounded-full text-xs font-medium ${
|
|
inv.status === "PAID"
|
|
? "bg-green-100 text-green-700"
|
|
: inv.status === "ISSUED"
|
|
? "bg-blue-100 text-blue-700"
|
|
: inv.status === "VOID"
|
|
? "bg-red-100 text-red-700"
|
|
: "bg-gray-100 text-gray-700"
|
|
}`}
|
|
>
|
|
{inv.status}
|
|
</span>
|
|
</td>
|
|
<td class="px-4 py-3 text-right">
|
|
<A
|
|
href={`/dashboard/wallet/invoices/${inv.id}`}
|
|
class="text-orange-600 hover:underline text-sm"
|
|
>
|
|
View →
|
|
</A>
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</For>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</Show>
|
|
)}
|
|
</Show>
|
|
</div>
|
|
</DashboardLayout>
|
|
);
|
|
}
|