feat: notifications page, auto-apply settings, customer response profiles, job status UI
- NotificationsPage: full paginated notification list with unread filter, mark read, load more
- SettingsPage: AI auto-apply section for job seekers (toggle, preferences, skills/titles/locations, salary range)
- CustomerResponsesPage: enriched professional response cards with avatar, bio, skills, location
- CompanyJobsPage: show rejection reason banner and pending-approval notice on job cards
- NotificationBell: fix "View all" link to /dashboard?nav=notifications (deep-link support)
- dashboard.tsx: ?nav= param reads sidebar page on mount; Notifications added to all role sidebars
- PayU integration: payu.ts lib, payu-return route, wallet buy/invoice pages, marketplace route
- Razorpay removed, replaced by PayU across payments flow
- ProfilePage: photo upload UI with avatar preview for all roles
- PortfolioPage: showcase image upload with file picker and preview
- CompanyApplicationsPage: applicant profile snapshot with avatar, headline, skills, resume download
- profile-fields-config: removed resume_doc from job seeker (resume is now AI-generated)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 13:33:42 +02:00
|
|
|
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>
|
|
|
|
|
|
2026-07-17 00:47:32 +02:00
|
|
|
<Show when={data()} keyed>
|
feat: notifications page, auto-apply settings, customer response profiles, job status UI
- NotificationsPage: full paginated notification list with unread filter, mark read, load more
- SettingsPage: AI auto-apply section for job seekers (toggle, preferences, skills/titles/locations, salary range)
- CustomerResponsesPage: enriched professional response cards with avatar, bio, skills, location
- CompanyJobsPage: show rejection reason banner and pending-approval notice on job cards
- NotificationBell: fix "View all" link to /dashboard?nav=notifications (deep-link support)
- dashboard.tsx: ?nav= param reads sidebar page on mount; Notifications added to all role sidebars
- PayU integration: payu.ts lib, payu-return route, wallet buy/invoice pages, marketplace route
- Razorpay removed, replaced by PayU across payments flow
- ProfilePage: photo upload UI with avatar preview for all roles
- PortfolioPage: showcase image upload with file picker and preview
- CompanyApplicationsPage: applicant profile snapshot with avatar, headline, skills, resume download
- profile-fields-config: removed resume_doc from job seeker (resume is now AI-generated)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 13:33:42 +02:00
|
|
|
{(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>
|
|
|
|
|
);
|
|
|
|
|
}
|