From f8ee1ca1682f45759dc16a02c6d7606606a43558 Mon Sep 17 00:00:00 2001 From: keyhan Date: Thu, 11 Jun 2026 13:00:15 +0330 Subject: [PATCH] Localize user and admin invoice pages. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move both invoice views onto a shared invoices dictionary — statuses, filters, payment/line-item/transaction labels, manual status controls and toasts — with locale-aware Link, dates and RTL-aware table/search layout. Co-Authored-By: Claude Opus 4.8 --- .../[lang]/dashboard/admin/invoices/page.tsx | 106 +++++++++--------- .../app/[lang]/dashboard/invoices/page.tsx | 80 +++++++------ frontend/src/i18n/dictionaries/en.ts | 62 ++++++++++ frontend/src/i18n/dictionaries/fa.ts | 62 ++++++++++ frontend/tsconfig.tsbuildinfo | 2 +- 5 files changed, 215 insertions(+), 97 deletions(-) diff --git a/frontend/src/app/[lang]/dashboard/admin/invoices/page.tsx b/frontend/src/app/[lang]/dashboard/admin/invoices/page.tsx index 6c20fd1..4bcfc11 100644 --- a/frontend/src/app/[lang]/dashboard/admin/invoices/page.tsx +++ b/frontend/src/app/[lang]/dashboard/admin/invoices/page.tsx @@ -5,17 +5,9 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { FileText, Search, User, Wallet, CreditCard, XCircle, Download } from 'lucide-react'; import { toast } from 'react-toastify'; import api from '@/lib/api'; +import { useT, useLocale } from '@/i18n/I18nProvider'; import type { Invoice, InvoiceStatus, PaymentMethod } from '@/types'; -const statusLabels: Record = { - draft: 'Draft', - issued: 'Unpaid', - partially_paid: 'Partially paid', - paid: 'Paid', - void: 'Void', - failed: 'Failed', -}; - const statusClasses: Record = { draft: 'badge-gray', issued: 'badge-yellow', @@ -26,6 +18,10 @@ const statusClasses: Record = { }; export default function AdminInvoicesPage() { + const t = useT(); + const inv = t.dashboard.invoices; + const locale = useLocale(); + const statusLabel = (s: InvoiceStatus) => (inv.status as Record)[s] ?? s; const queryClient = useQueryClient(); const [status, setStatus] = useState<'all' | InvoiceStatus>('all'); const [paymentMethod, setPaymentMethod] = useState<'all' | PaymentMethod>('all'); @@ -54,12 +50,12 @@ export default function AdminInvoicesPage() { mutationFn: ({ invoiceId, nextStatus, reason }: { invoiceId: string; nextStatus: InvoiceStatus; reason: string }) => api.patch(`/billing/admin/invoices/${invoiceId}/status`, { status: nextStatus, reason }).then((r) => r.data), onSuccess: () => { - toast.success('Invoice status updated'); + toast.success(inv.statusUpdated); setStatusReason(''); queryClient.invalidateQueries({ queryKey: ['admin-invoices'] }); queryClient.invalidateQueries({ queryKey: ['admin-invoice', selectedId] }); }, - onError: (err: any) => toast.error(err.response?.data?.message || 'Failed to update invoice status'), + onError: (err: any) => toast.error(err.response?.data?.message || inv.statusUpdateFailed), }); const downloadPdfMutation = useMutation({ @@ -74,16 +70,16 @@ export default function AdminInvoicesPage() { link.remove(); window.URL.revokeObjectURL(url); }, - onError: () => toast.error('Failed to download invoice PDF'), + onError: () => toast.error(inv.downloadFailed), }); const formatPrice = (amount: number) => Number(amount || 0).toLocaleString('en-US'); - const formatDate = (value?: string) => value ? new Date(value).toLocaleString('en-US') : '-'; + const formatDate = (value?: string) => value ? new Date(value).toLocaleString(locale) : '-'; const handleStatusUpdate = (nextStatus: InvoiceStatus) => { if (!selectedInvoice) return; if (!statusReason.trim()) { - toast.error('Reason is required for manual status changes'); + toast.error(inv.reasonRequired); return; } updateStatusMutation.mutate({ @@ -97,32 +93,32 @@ export default function AdminInvoicesPage() {

- Invoice Management + {inv.adminTitle}

-

Track all user invoices, payments, gateway refs, and wallet transactions.

+

{inv.adminSubtitle}

- + setSearch(e.target.value)} - placeholder="Search invoice, email, or application" - className="input-field pl-9 w-full" + placeholder={inv.searchPlaceholder} + className="input-field pl-9 rtl:pl-3 rtl:pr-9 w-full" />
@@ -132,18 +128,18 @@ export default function AdminInvoicesPage() { - - - - - + + + + + {isLoading ? ( - + ) : invoices.length === 0 ? ( - + ) : invoices.map((invoice) => ( {invoice.user?.email || invoice.userId}

- ))} @@ -176,7 +172,7 @@ export default function AdminInvoicesPage() { {!selectedInvoice ? (
- Select an invoice + {inv.selectInvoiceShort}
) : (
@@ -186,7 +182,7 @@ export default function AdminInvoicesPage() {

{selectedInvoice.application?.name || selectedInvoice.reason}

- {statusLabels[selectedInvoice.status]} + {statusLabel(selectedInvoice.status)}
-

Total

-

{formatPrice(selectedInvoice.total)} T

+

{inv.total}

+

{formatPrice(selectedInvoice.total)} {t.common.currencyShort}

-

Due

-

{formatPrice(selectedInvoice.dueAmount)} T

+

{inv.due}

+

{formatPrice(selectedInvoice.dueAmount)} {t.common.currencyShort}

@@ -217,16 +213,16 @@ export default function AdminInvoicesPage() {
- Tracking: {selectedInvoice.gatewayTrackingCode || '-'} + {inv.tracking}: {selectedInvoice.gatewayTrackingCode || '-'}
- Method: {selectedInvoice.paymentMethod || '-'} + {inv.method}: {selectedInvoice.paymentMethod || '-'}
-

Line items

+

{inv.lineItems}

{(selectedInvoice.lines || []).map((line) => (
@@ -234,23 +230,23 @@ export default function AdminInvoicesPage() {

{line.label}

{line.description &&

{line.description}

}
- {formatPrice(line.amount)} T + {formatPrice(line.amount)} {t.common.currencyShort}
))}
-

Transactions

+

{inv.transactions}

{(selectedInvoice.transactions || []).length === 0 ? ( -

No linked transactions yet

+

{inv.noTransactions}

) : (
{(selectedInvoice.transactions || []).map((tx) => (
{tx.description || tx.type} - {formatPrice(tx.amount)} T + {formatPrice(tx.amount)} {t.common.currencyShort}

{tx.type} · {formatDate(tx.createdAt)}

@@ -260,12 +256,12 @@ export default function AdminInvoicesPage() {
-

Manual status change

+

{inv.manualStatusChange}

InvoiceUserStatusMethodDue{inv.colInvoice}{inv.colUser}{inv.colStatus}{inv.colMethod}{inv.colDue}
Loading...
{t.common.loading}
No invoices found
{inv.noInvoices}
- {statusLabels[invoice.status]} + {statusLabel(invoice.status)} {invoice.paymentMethod || '-'} -

{formatPrice(invoice.dueAmount)} T

-

Total {formatPrice(invoice.total)} T

+
+

{formatPrice(invoice.dueAmount)} {t.common.currencyShort}

+

{inv.total} {formatPrice(invoice.total)} {t.common.currencyShort}