diff --git a/src/components/DashboardLayout.tsx b/src/components/DashboardLayout.tsx deleted file mode 100644 index 2d3c528..0000000 --- a/src/components/DashboardLayout.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import { type ParentProps, createMemo, createSignal, onMount } from "solid-js"; -import { useLocation, useNavigate } from "@solidjs/router"; -import DashboardShell from "~/components/DashboardShell"; - -const SIDEBAR_ITEMS = [ - "My Dashboard", - "Leads", - "My Requests", - "Credits", - "Settings", - "Logout", -]; - -const ROUTE_BY_LABEL: Record = { - "my dashboard": "/dashboard", - leads: "/dashboard/leads/accepted", - "my requests": "/dashboard/requests", - credits: "/dashboard?nav=credits", - settings: "/dashboard?nav=settings", - logout: "/dashboard?nav=logout", -}; - -function readUserName() { - if (typeof window === "undefined") return "User"; - try { - const raw = - sessionStorage.getItem("nxtgauge_auth_user") || - sessionStorage.getItem("nxtgauge_user") || - localStorage.getItem("nxtgauge_auth_user") || - localStorage.getItem("nxtgauge_user"); - if (!raw) return "User"; - const parsed = JSON.parse(raw); - return parsed?.full_name || parsed?.name || parsed?.email || "User"; - } catch { - return "User"; - } -} - -export default function DashboardLayout(props: ParentProps) { - const location = useLocation(); - const navigate = useNavigate(); - const [roleKey, setRoleKey] = createSignal("DEVELOPER"); - const [userName, setUserName] = createSignal("User"); - - const activeSidebar = createMemo(() => { - const path = location.pathname || ""; - if (path.startsWith("/dashboard/requests")) return "My Requests"; - if (path.startsWith("/dashboard/leads")) return "Leads"; - if (path.startsWith("/dashboard/credits")) return "Credits"; - if (path.startsWith("/dashboard/settings")) return "Settings"; - return "My Dashboard"; - }); - - const handleSidebarSelect = (item: string) => { - const target = ROUTE_BY_LABEL[item.toLowerCase()]; - if (target) navigate(target); - }; - - onMount(async () => { - if (typeof window === "undefined") return; - - const fromUrl = new URLSearchParams(window.location.search).get("role"); - if (fromUrl && fromUrl.trim()) { - setRoleKey(fromUrl.trim().toUpperCase()); - return; - } - - const storageKeys: [string, Storage][] = [ - ["nxtgauge_signup_profile_v1", localStorage], - ["nxtgauge_auth_user", localStorage], - ["nxtgauge_user", localStorage], - ["nxtgauge_signup_profile_v1", sessionStorage], - ["nxtgauge_auth_user", sessionStorage], - ["nxtgauge_user", sessionStorage], - ]; - - for (const [key, storage] of storageKeys) { - try { - const raw = storage.getItem(key); - if (raw) { - const parsed = JSON.parse(raw); - const candidate = String( - parsed?.selectedProfessionalRole || parsed?.active_role || parsed?.roleKey || parsed?.role || "" - ) - .trim() - .toUpperCase(); - if (candidate && candidate !== "PROFESSIONAL") { - setRoleKey(candidate); - if (parsed?.full_name || parsed?.name || parsed?.email) { - setUserName(parsed.full_name || parsed.name || parsed.email || "User"); - } - return; - } - } - } catch { - // continue - } - } - - const token = sessionStorage.getItem("nxtgauge_access_token"); - if (token) { - try { - const res = await fetch("/api/auth/session", { - headers: { - Accept: "application/json", - Authorization: `Bearer ${token}`, - }, - credentials: "include", - }); - if (res.ok) { - const data = await res.json(); - const role = String(data?.active_role || data?.role || "").trim().toUpperCase(); - setRoleKey(role && role !== "PROFESSIONAL" ? role : "DEVELOPER"); - setUserName(data?.full_name || data?.name || data?.email || "User"); - return; - } - } catch { - // fall through - } - } - }); - - return ( - - {props.children} - - ); -} diff --git a/src/components/NotificationBell.tsx b/src/components/NotificationBell.tsx index 602acb7..c5cf0c3 100644 --- a/src/components/NotificationBell.tsx +++ b/src/components/NotificationBell.tsx @@ -1,5 +1,5 @@ import { createSignal, createEffect, onCleanup, Show } from "solid-js"; -import { api } from "~/lib/api"; +import { apiFetch } from "~/lib/api"; const ORANGE = "#FF5E13"; const NAVY = "#0D0D2A"; @@ -13,8 +13,8 @@ export default function NotificationBell() { createEffect(() => { const fetchUnreadCount = async () => { try { - const res = await api.get("/me/notifications/unread-count"); - setUnreadCount(res.data?.unread_count || 0); + const res = await apiFetch("/api/me/notifications/unread-count"); + setUnreadCount(res?.unread_count || 0); } catch (e) { // Silently fail } @@ -32,8 +32,8 @@ export default function NotificationBell() { // Fetch notifications when dropdown opens const fetchNotifications = async () => { try { - const res = await api.get("/me/notifications?limit=5"); - setNotifications(res.data?.data || []); + const res = await apiFetch("/api/me/notifications?limit=5"); + setNotifications(res?.data || []); } catch (e) { setNotifications([]); } @@ -49,7 +49,7 @@ export default function NotificationBell() { const markAsRead = async (id: string) => { try { - await api.patch(`/me/notifications/${id}/read`); + await apiFetch(`/api/me/notifications/${id}/read`, { method: "PATCH" }); // Update local state setNotifications((prev) => prev.map((n) => (n.id === id ? { ...n, is_read: true } : n))); setUnreadCount((prev) => Math.max(0, prev - 1)); @@ -60,7 +60,7 @@ export default function NotificationBell() { const markAllAsRead = async () => { try { - await api.patch("/me/notifications/read-all"); + await apiFetch("/api/me/notifications/read-all", { method: "PATCH" }); setNotifications((prev) => prev.map((n) => ({ ...n, is_read: true }))); setUnreadCount(0); } catch (e) { diff --git a/src/components/dashboard/AcceptedLeadsView.tsx b/src/components/dashboard/AcceptedLeadsView.tsx deleted file mode 100644 index e33bcc2..0000000 --- a/src/components/dashboard/AcceptedLeadsView.tsx +++ /dev/null @@ -1,274 +0,0 @@ -import { createResource, createSignal, Show, For } from "solid-js"; -import { useNavigate, useParams } from "@solidjs/router"; -import DashboardLayout from "~/components/DashboardLayout"; -import { RequireAuth } from "~/lib/auth"; -import { api } from "~/lib/api"; - -interface AcceptedLead { - id: string; - requirement: { - id: string; - title: string; - profession_key: string; - location: string; - budget: number; - preferred_date: string; - description: string; - }; - customer_contact: { - full_name: string; - phone: string; - email: string; - city: string; - }; - tracecoins_deducted: number; - accepted_at: string; -} - -export default function AcceptedLeadsView() { - const navigate = useNavigate(); - const params = useParams(); - - // If ID param exists, show detail view - const isDetailView = () => !!params.id; - - const [leads] = createResource(async () => { - const res = await api.get("/leads/accepted/me"); - return res.data?.data || []; - }); - - const [selectedLead, setSelectedLead] = createSignal(null); - - // Fetch single lead if in detail view - createResource(async () => { - if (params.id) { - try { - const res = await api.get(`/leads/accepted/${params.id}`); - setSelectedLead(res.data); - } catch (e) { - navigate("/dashboard/leads/accepted"); - } - } - }); - - const formatDate = (date: string) => { - return new Date(date).toLocaleDateString("en-IN", { - day: "numeric", - month: "long", - year: "numeric", - }); - }; - - const formatPhone = (phone: string) => { - return phone.replace(/(\d{5})(\d{5})/, "$1-$2"); - }; - - // Detail View - if (isDetailView() && selectedLead()) { - const lead = selectedLead()!; - return ( - -
- - -
- {/* Header */} -
-
-
- - Lead Accepted - -

{lead.requirement.title}

-

Accepted on {formatDate(lead.accepted_at)}

-
-
-

Tracecoins Deducted

-

{lead.tracecoins_deducted} TC

-
-
-
- -
- {/* Customer Contact Card */} -
-

- πŸ‘€ Customer Contact -

-
-
-

Name

-

{lead.customer_contact.full_name}

-
- - -
-

Location

-

{lead.customer_contact.city}

-
-
- - -
- - {/* Requirement Details */} -
-

- πŸ“‹ Requirement Details -

-
-
-

Service Type

-

- {lead.requirement.profession_key.toLowerCase().replace(/_/g, " ")} -

-
-
-

Location

-

{lead.requirement.location}

-
-
-

Budget

-

- β‚Ή{lead.requirement.budget?.toLocaleString() || "Not specified"} -

-
- {lead.requirement.preferred_date && ( -
-

Preferred Date

-

{formatDate(lead.requirement.preferred_date)}

-
- )} -
-

Description

-

{lead.requirement.description}

-
-
-
-
- - {/* Footer */} -
-

- Contact the customer to discuss the project details and finalize the engagement. -

-
-
-
-
- ); - } - - // List View - return ( - -
-

Accepted Leads

- - {/* Loading State */} - -
-
-

Loading leads...

-
- - - {/* Empty State */} - -
-
🀝
-

No accepted leads yet

-

- Browse the marketplace and send requests to view customer contacts. -

- -
-
- - {/* Leads Grid */} -
- - {(lead: AcceptedLead) => ( -
navigate(`/dashboard/leads/accepted/${lead.id}`)} - class="bg-white border rounded-xl p-6 hover:shadow-lg transition-all cursor-pointer" - > -
- - Accepted - - - -{lead.tracecoins_deducted} TC - -
- -

- {lead.requirement.title} -

- -
-

- πŸ‘€ {lead.customer_contact.full_name} -

-

- πŸ“ {lead.requirement.location} -

- {lead.requirement.budget && ( -

- πŸ’° β‚Ή{lead.requirement.budget.toLocaleString()} -

- )} -
- -
-

Accepted {formatDate(lead.accepted_at)}

-
-
- )} -
-
-
- - ); -} diff --git a/src/lib/api.ts b/src/lib/api.ts index 2339a2c..400568e 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -59,19 +59,6 @@ export async function request( return { data: data as T, status: res.status, headers: res.headers }; } -export const api = { - get: (path: string, headers?: HeadersInit) => - request(path, { method: "GET", headers }), - post: (path: string, body?: unknown, headers?: HeadersInit) => - request(path, { method: "POST", body, headers }), - put: (path: string, body?: unknown, headers?: HeadersInit) => - request(path, { method: "PUT", body, headers }), - patch: (path: string, body?: unknown, headers?: HeadersInit) => - request(path, { method: "PATCH", body, headers }), - delete: (path: string, headers?: HeadersInit) => - request(path, { method: "DELETE", headers }), -}; - export async function fetchProfile(rolePrefix: string): Promise { return apiFetch(`/api/${rolePrefix}/profile/me`); } diff --git a/src/routes/dashboard/leads/accepted/[id].tsx b/src/routes/dashboard/leads/accepted/[id].tsx deleted file mode 100644 index 0456b15..0000000 --- a/src/routes/dashboard/leads/accepted/[id].tsx +++ /dev/null @@ -1,5 +0,0 @@ -import AcceptedLeadsView from "~/components/dashboard/AcceptedLeadsView"; - -export default function AcceptedLeadDetailRoute() { - return ; -} diff --git a/src/routes/dashboard/leads/accepted/index.tsx b/src/routes/dashboard/leads/accepted/index.tsx deleted file mode 100644 index 4eb4011..0000000 --- a/src/routes/dashboard/leads/accepted/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import AcceptedLeadsView from "~/components/dashboard/AcceptedLeadsView"; - -export default function AcceptedLeadsIndexRoute() { - return ; -} diff --git a/src/routes/dashboard/marketplace/[id].tsx b/src/routes/dashboard/marketplace/[id].tsx deleted file mode 100644 index da31e80..0000000 --- a/src/routes/dashboard/marketplace/[id].tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { createResource, Show } from "solid-js"; -import { useNavigate, useParams } from "@solidjs/router"; -import DashboardLayout from "~/components/DashboardLayout"; -import { RequireAuth } from "~/lib/auth"; -import { apiFetch } from "~/lib/api"; - -export default function MarketplaceRequirementDetailRoute() { - const navigate = useNavigate(); - const params = useParams(); - - const [requirement] = createResource(() => params.id, async (id) => { - return apiFetch(`/api/customers/requirements/${id}`); - }); - - const formatDate = (date: string) => { - return new Date(date).toLocaleDateString("en-IN", { - day: "numeric", - month: "long", - year: "numeric", - }); - }; - - return ( - - -
- - - -
-
-

Loading requirement...

-
- - - -
-

Requirement not found

-

It may have been closed or removed.

-
-
- - - {(req: any) => { - const r = req().data || req(); - return ( -
-
-

{r.title}

-

Posted on {r.created_at ? formatDate(r.created_at) : "β€”"}

-
- -
-
-

Service Type

-

- {(r.profession_key || "").toLowerCase().replace(/_/g, " ")} -

-
-
-

Location

-

{r.location}

-
-
-

Budget

-

- β‚Ή{r.budget?.toLocaleString() || "Not specified"} -

-
- -
-

Preferred Date

-

{formatDate(r.preferred_date)}

-
-
-
-

Description

-

{r.description}

-
-
-
- ); - }} -
-
- - - ); -} diff --git a/src/routes/dashboard/marketplace/index.tsx b/src/routes/dashboard/marketplace/index.tsx deleted file mode 100644 index 6ce2ef3..0000000 --- a/src/routes/dashboard/marketplace/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import DashboardLayout from "~/components/DashboardLayout"; -import { RequireAuth } from "~/lib/auth"; -import ExploreServicesPage from "~/components/dashboard/ExploreServicesPage"; - -export default function MarketplacePage() { - return ( - - - - - - ); -} diff --git a/src/routes/dashboard/requests.tsx b/src/routes/dashboard/requests.tsx deleted file mode 100644 index e6a3304..0000000 --- a/src/routes/dashboard/requests.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import { createResource, createSignal, Show, For } from "solid-js"; -import { useNavigate } from "@solidjs/router"; -import DashboardLayout from "~/components/DashboardLayout"; -import { RequireAuth } from "~/lib/auth"; -import { api } from "~/lib/api"; - -interface LeadRequest { - id: string; - requirement: { - id: string; - title: string; - location: string; - budget: number; - }; - status: "PENDING" | "ACCEPTED" | "REJECTED" | "EXPIRED"; - tracecoins_reserved: number; - expires_at: string; - requested_at: string; -} - -export default function MyRequestsPage() { - const navigate = useNavigate(); - const [activeTab, setActiveTab] = createSignal("ALL"); - - const [requests, { refetch }] = createResource(async () => { - const res = await api.get("/leads/requests/me"); - return res.data?.data || []; - }); - - const filteredRequests = () => { - const all = requests() || []; - if (activeTab() === "ALL") return all; - return all.filter((r: LeadRequest) => r.status === activeTab()); - }; - - const cancelRequest = async (id: string) => { - if (!confirm("Are you sure you want to cancel this request?")) return; - - try { - await api.delete(`/leads/requests/${id}`); - refetch(); - } catch (e) { - alert("Failed to cancel request"); - } - }; - - const formatDate = (date: string) => { - return new Date(date).toLocaleDateString("en-IN", { - day: "numeric", - month: "short", - hour: "2-digit", - minute: "2-digit", - }); - }; - - const getTimeRemaining = (expiresAt: string) => { - const now = new Date(); - const expiry = new Date(expiresAt); - const diff = expiry.getTime() - now.getTime(); - - if (diff <= 0) return "Expired"; - - const hours = Math.floor(diff / (1000 * 60 * 60)); - if (hours < 1) { - const mins = Math.floor(diff / (1000 * 60)); - return `${mins}m remaining`; - } - return `${hours}h remaining`; - }; - - const getStatusBadge = (status: string) => { - const classes = { - PENDING: "bg-yellow-100 text-yellow-800", - ACCEPTED: "bg-green-100 text-green-800", - REJECTED: "bg-red-100 text-red-800", - EXPIRED: "bg-gray-100 text-gray-800", - }; - return classes[status as keyof typeof classes] || classes.EXPIRED; - }; - - return ( - - -
-

My Lead Requests

- - {/* Filter Tabs */} -
- {["ALL", "PENDING", "ACCEPTED", "REJECTED", "EXPIRED", "CANCELLED"].map((tab) => ( - - ))} -
- - {/* Loading State */} - -
-
-

Loading requests...

-
- - - {/* Empty State */} - -
-
πŸ“‹
-

No requests found

-

- {activeTab() === "ALL" - ? "You haven't sent any lead requests yet." - : `No ${activeTab().toLowerCase()} requests.`} -

- -
-
- - {/* Requests List */} -
- - {(request: LeadRequest) => ( -
-
-
-

{request.requirement.title}

-

{request.requirement.location}

-
- - {request.status} - -
- -
-
-

Budget

-

- β‚Ή{request.requirement.budget?.toLocaleString() || "Not specified"} -

-
-
-

Reserved

-

{request.tracecoins_reserved} TC

-
-
-

- {request.status === "PENDING" ? "Expires" : "Requested"} -

-

- {request.status === "PENDING" - ? getTimeRemaining(request.expires_at) - : formatDate(request.requested_at)} -

-
-
- -
- - - - - - - - - -
-
- )} -
-
-
- - - ); -} diff --git a/src/routes/dashboard/wallet/buy.tsx b/src/routes/dashboard/wallet/buy.tsx deleted file mode 100644 index a4756df..0000000 --- a/src/routes/dashboard/wallet/buy.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import { createResource, createSignal, Show, For } from "solid-js"; -import { useNavigate } from "@solidjs/router"; -import DashboardLayout from "~/components/DashboardLayout"; -import { RequireAuth } from "~/lib/auth"; -import { api } from "~/lib/api"; -import { openPayuCheckout } from "~/lib/payu"; - -interface Package { - id: string; - name: string; - tracecoins_amount: number; - price_inr: number; - description: string; -} - -export default function BuyTracecoinsPage() { - const navigate = useNavigate(); - const [selectedPackage, setSelectedPackage] = createSignal(null); - const [loading, setLoading] = createSignal(false); - const [error, setError] = createSignal(""); - const [success, setSuccess] = createSignal(false); - - const resolveRoleKey = () => { - if (typeof window === "undefined") return "DEVELOPER"; - const fromUrl = new URLSearchParams(window.location.search).get("role"); - if (fromUrl && fromUrl.trim()) return fromUrl.trim().toUpperCase(); - const keys = ["nxtgauge_signup_profile_v1", "nxtgauge_auth_user", "nxtgauge_user"]; - for (const key of keys) { - try { - const raw = window.localStorage.getItem(key) || window.sessionStorage.getItem(key); - if (!raw) continue; - const parsed = JSON.parse(raw); - const candidate = String( - parsed?.selectedProfessionalRole || parsed?.active_role || parsed?.roleKey || parsed?.role || "" - ) - .trim() - .toUpperCase(); - if (candidate && candidate !== "PROFESSIONAL") return candidate; - } catch { - // ignore malformed storage payloads - } - } - return "DEVELOPER"; - }; - - const [packages] = createResource(async () => { - const roleKey = resolveRoleKey(); - const res = await api.get(`/packages?role=${encodeURIComponent(roleKey)}&roleKey=${encodeURIComponent(roleKey)}`); - return res.data?.packages || []; - }); - - const handlePurchase = async () => { - const pkg = selectedPackage(); - if (!pkg) return; - - setLoading(true); - setError(""); - - try { - const paymentData = await openPayuCheckout({ - amount: pkg.price_inr, - currency: "INR", - txnId: pkg.id, - description: `${pkg.name} - ${pkg.tracecoins_amount} Tracecoins`, - }); - - await api.post("/payments/verify", { - txnid: paymentData.txnid, - mihpayid: paymentData.mihpayid, - status: paymentData.status, - hash: paymentData.hash, - amount: paymentData.amount, - productinfo: paymentData.productinfo, - firstname: paymentData.firstname, - email: paymentData.email, - phone: paymentData.phone, - udf1: paymentData.udf1, - udf2: paymentData.udf2, - }); - - setSuccess(true); - setTimeout(() => { - navigate("/dashboard?nav=credits"); - }, 2000); - } catch (e: any) { - setError(e.message || "Payment failed. Please try again."); - } finally { - setLoading(false); - } - }; - - const formatPrice = (paise: number) => { - return `β‚Ή${(paise / 100).toLocaleString()}`; - }; - - return ( - - -
-

Buy Tracecoins

-

Purchase Tracecoins to send lead requests to customers

- - -
-
πŸŽ‰
-

Payment Successful!

-

Tracecoins have been added to your wallet.

-
-
- - -
-

{error()}

-
-
- - - -
-
-

Loading packages...

-
- - - -
- - {(pkg: Package) => ( -
setSelectedPackage(pkg)} - class={`border-2 rounded-xl p-6 cursor-pointer transition-all ${ - selectedPackage()?.id === pkg.id - ? "border-orange-500 bg-orange-50" - : "border-gray-200 hover:border-orange-300" - }`} - > -
-

{pkg.name}

- - βœ“ - -
- -
- {pkg.tracecoins_amount} - TC -
- -

- {formatPrice(pkg.price_inr)} -

- -

{pkg.description}

-
- )} -
-
- - -
-

Order Summary

-
- {selectedPackage()?.name} - {formatPrice(selectedPackage()?.price_inr || 0)} -
-
-
- Total - {formatPrice(selectedPackage()?.price_inr || 0)} -
-
-
- -
- - -
-
-
- -
- - - ); -} diff --git a/src/routes/dashboard/wallet/invoices/[id].tsx b/src/routes/dashboard/wallet/invoices/[id].tsx deleted file mode 100644 index b5274e3..0000000 --- a/src/routes/dashboard/wallet/invoices/[id].tsx +++ /dev/null @@ -1,284 +0,0 @@ -import { createResource, Show, For } from "solid-js"; -import { useParams, useNavigate } from "@solidjs/router"; -import DashboardLayout from "~/components/DashboardLayout"; -import { api } from "~/lib/api"; - -interface Invoice { - id: string; - invoice_number: string; - payment_id: string; - user_id: string; - status: string; - currency: string; - invoice_type: string; - subtotal: number; - discount_amount: number; - cgst_rate: number; - cgst_amount: number; - sgst_rate: number; - sgst_amount: number; - igst_rate: number; - igst_amount: number; - total: number; - reverse_charge: boolean; - seller_name: string; - seller_address: string; - seller_gstin: string | null; - seller_pan: string | null; - seller_state_code: string | null; - place_of_supply_state: string | null; - customer_name: string | null; - customer_email: string | null; - customer_phone: string | null; - customer_billing_address: string | null; - customer_gstin: string | null; - customer_state_code: string | null; - discount_label: string | null; - notes: string | null; - pdf_object_key: string | null; - issued_at: string; - paid_at: string | null; - voided_at: string | null; - void_reason: string | null; -} - -interface LineItem { - line_number: number; - description: string; - hsn_sac_code: string | null; - quantity: number; - unit_price_paise: number; - tax_rate_percent: number; -} - -interface InvoiceResponse { - invoice: Invoice; - lines: LineItem[]; - totals: { - subtotal: number; - discount: number; - taxable_value: number; - cgst: number; - sgst: number; - igst: number; - total_tax: number; - total: number; - }; -} - -export default function InvoiceDetailPage() { - const params = useParams(); - const navigate = useNavigate(); - - const [data] = createResource(async () => { - const res = await api.get(`/wallet/me/invoices/${params.id}`); - return res.data as InvoiceResponse; - }); - - const formatDate = (date: string) => { - return new Date(date).toLocaleDateString("en-IN", { - day: "numeric", - month: "long", - year: "numeric", - }); - }; - - const formatPrice = (paise: number) => { - const rupees = paise / 100; - return new Intl.NumberFormat("en-IN", { - style: "currency", - currency: "INR", - minimumFractionDigits: 2, - }).format(rupees); - }; - - const downloadPdf = () => { - window.open(`/api/wallet/me/invoices/${params.id}/html`, "_blank"); - }; - - return ( - -
- - - -
-
-

Loading invoice...

-
- - - -
-

Failed to load invoice: {String(data.error)}

-
-
- - - {(d: InvoiceResponse) => ( -
-
-
-
-

TAX INVOICE

-

{d.invoice.invoice_number}

-
-
- - {d.invoice.status} - -

Issued {formatDate(d.invoice.issued_at)}

-
-
-
- -
-
-
-

From

-

{d.invoice.seller_name}

-

{d.invoice.seller_address}

-

- {d.invoice.seller_gstin && ( - <>GSTIN: {d.invoice.seller_gstin}
- )} - {d.invoice.seller_pan && <>PAN: {d.invoice.seller_pan}} -

-
-
-

Bill To

-

{d.invoice.customer_name ?? "β€”"}

-

- {d.invoice.customer_billing_address ?? "β€”"} -

-

- {d.invoice.customer_gstin && ( - <>GSTIN: {d.invoice.customer_gstin}
- )} - {d.invoice.place_of_supply_state && ( - <>Place of Supply: {d.invoice.place_of_supply_state} - )} -

-
-
- - - - - - - - - - - - - - - {(line) => ( - - - - - - - - - )} - - -
#DescriptionHSN/SACQtyRateAmount
{line.line_number}{line.description}{line.hsn_sac_code ?? "β€”"}{line.quantity}{formatPrice(line.unit_price_paise)} - {formatPrice(line.unit_price_paise * line.quantity)} -
- -
-
- Subtotal - {formatPrice(d.totals.subtotal)} -
- 0}> -
- Discount - -{formatPrice(d.totals.discount)} -
-
-
- Taxable value - {formatPrice(d.totals.taxable_value)} -
- 0}> -
- CGST ({(d.invoice.cgst_rate).toFixed(2)}%) - {formatPrice(d.totals.cgst)} -
-
- SGST ({(d.invoice.sgst_rate).toFixed(2)}%) - {formatPrice(d.totals.sgst)} -
-
- 0}> -
- IGST ({(d.invoice.igst_rate).toFixed(2)}%) - {formatPrice(d.totals.igst)} -
-
-
- Total ({d.invoice.currency}) - {formatPrice(d.totals.total)} -
-
- - -
-

- βœ“ Paid on {formatDate(d.invoice.paid_at!)} -

-
-
- - -
-

βœ— Invoice voided

- -

Reason: {d.invoice.void_reason}

-
-
-
- -
- - -
-
-
- )} -
-
- - ); -} diff --git a/src/routes/dashboard/wallet/invoices/index.tsx b/src/routes/dashboard/wallet/invoices/index.tsx deleted file mode 100644 index 04adeb4..0000000 --- a/src/routes/dashboard/wallet/invoices/index.tsx +++ /dev/null @@ -1,134 +0,0 @@ -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("/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 ( - -
-

Invoices

-

All your Tracecoin purchase invoices

- - -
-
-

Loading invoices...

-
- - - - {(d) => ( - 0} - fallback={ -
-

No invoices yet.

-

- Invoices are generated automatically when you purchase a Tracecoin package. -

-
- } - > -
- - - - - - - - - - - - - - {(inv) => ( - - - - - - - - - )} - - -
Invoice #PackageDateTotalStatus
{inv.invoice_number}{inv.package_name ?? "Tracecoin purchase"}{formatDate(inv.issued_at)} - {formatPrice(inv.total)} - - - {inv.status} - - - - View β†’ - -
-
-
- )} -
-
- - ); -} diff --git a/src/routes/dashboard/wallet/payu-return.tsx b/src/routes/dashboard/wallet/payu-return.tsx deleted file mode 100644 index b3a4459..0000000 --- a/src/routes/dashboard/wallet/payu-return.tsx +++ /dev/null @@ -1,130 +0,0 @@ -import { createSignal, onMount, Show } from "solid-js"; -import { useNavigate } from "@solidjs/router"; -import DashboardLayout from "~/components/DashboardLayout"; -import { RequireAuth } from "~/lib/auth"; -import { api } from "~/lib/api"; - -type PayuResponseFields = { - status: string; - txnid: string; - amount: string; - mihpayid: string; - hash: string; - firstname: string; - email: string; - phone: string; - productinfo: string; - udf1?: string; - udf2?: string; - udf3?: string; - udf4?: string; - udf5?: string; -}; - -function readFormFields(): PayuResponseFields { - if (typeof window === "undefined") { - return emptyFields(); - } - - const search = window.location.search.replace(/^\?/, ""); - const params = new URLSearchParams(search); - - return { - status: params.get("status") || "", - txnid: params.get("txnid") || "", - amount: params.get("amount") || "", - mihpayid: params.get("mihpayid") || "", - hash: params.get("hash") || "", - firstname: params.get("firstname") || "", - email: params.get("email") || "", - phone: params.get("phone") || "", - productinfo: params.get("productinfo") || "", - udf1: params.get("udf1") || undefined, - udf2: params.get("udf2") || undefined, - udf3: params.get("udf3") || undefined, - udf4: params.get("udf4") || undefined, - udf5: params.get("udf5") || undefined, - }; -} - -function emptyFields(): PayuResponseFields { - return { - status: "", - txnid: "", - amount: "", - mihpayid: "", - hash: "", - firstname: "", - email: "", - phone: "", - productinfo: "", - }; -} - -export default function PayuReturnPage() { - const navigate = useNavigate(); - const [status, setStatus] = createSignal<"verifying" | "success" | "failure">("verifying"); - const [error, setError] = createSignal(""); - - onMount(async () => { - const fields = readFormFields(); - if (!fields.txnid) { - setStatus("failure"); - setError("Missing PayU transaction id. Please try again from your wallet."); - return; - } - - try { - const res = await api.post("/payments/verify", fields); - if (res?.data?.verified) { - setStatus("success"); - setTimeout(() => { - navigate("/dashboard?nav=credits"); - }, 2500); - } else { - setStatus("failure"); - setError(res?.data?.message || "PayU reported a non-success status."); - } - } catch (e: any) { - setStatus("failure"); - setError(e?.message || "Failed to verify PayU payment."); - } - }); - - return ( - - -
- -
-
-

Verifying your PayU payment…

-

Please do not refresh this page.

-
- - - -
-
πŸŽ‰
-

Payment Successful

-

Your Tracecoins have been credited. Redirecting to your wallet…

-
-
- - -
-

Payment Failed

-

{error()}

- -
-
-
- - - ); -}