feat(reports): enrich all-user report details
Some checks failed
Frontend CI/CD / build (push) Has been cancelled
Frontend CI/CD / deploy (push) Has been cancelled

This commit is contained in:
2026-05-23 20:49:08 +03:30
parent 993dffb51d
commit 9a217fcd54
6 changed files with 755 additions and 325 deletions

View File

@@ -25,7 +25,12 @@ type ChartRow = {
const localizeDigits = (value: string, lang: "en" | "fa") =>
lang === "fa" ? value.replace(/\d/g, (digit) => PERSIAN_DIGITS[Number(digit)] || digit) : value
const formatAmount = (value: string, lang: "en" | "fa") => {
const shouldTrimCurrencyDecimals = (currency?: string | null) => {
const normalized = (currency || "").toUpperCase()
return normalized === "IRR" || normalized === "IRT"
}
const formatAmount = (value: string, lang: "en" | "fa", currency?: string | null) => {
const numeric = Number(value.replace(/,/g, ""))
if (Number.isNaN(numeric)) {
return localizeDigits(value, lang)
@@ -34,7 +39,9 @@ const formatAmount = (value: string, lang: "en" | "fa") => {
const [integerPart, fractionalPart] = value.replace(/,/g, "").split(".")
const grouped = Math.abs(Number(integerPart)).toLocaleString("en-US")
const signed = value.startsWith("-") ? `-${grouped}` : grouped
const formatted = fractionalPart ? `${signed}.${fractionalPart}` : signed
const normalizedFraction =
fractionalPart && !shouldTrimCurrencyDecimals(currency) ? fractionalPart.replace(/0+$/, "") : ""
const formatted = normalizedFraction ? `${signed}.${normalizedFraction}` : signed
return localizeDigits(formatted, lang)
}
@@ -61,7 +68,7 @@ const formatMoneyTotals = (totals: CurrencyTotal[], lang: "en" | "fa") => {
return "-"
}
return totals.map((item) => `${formatAmount(item.amount, lang)} ${currencyLabel(item.currency, lang)}`).join(" | ")
return totals.map((item) => `${formatAmount(item.amount, lang, item.currency)} ${currencyLabel(item.currency, lang)}`).join(" | ")
}
const formatSecondsTick = (value: number, lang: "en" | "fa") => {
@@ -260,12 +267,39 @@ function ChartTooltip({
export function ReportsChartPanel({
data,
labels,
isLoading,
}: {
data: ChartReportResponse | null
labels: Record<string, string>
isLoading: boolean
}) {
const { lang } = useTranslation()
if (isLoading) {
return (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-3 xl:grid-cols-4">
{Array.from({ length: 4 }).map((_, index) => (
<div key={index} className="rounded-3xl border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900">
<div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.16em] text-slate-500 dark:text-slate-400">
{labels.loading}
</div>
<div className="h-8 animate-pulse rounded-xl bg-slate-200/80 dark:bg-slate-800/80" />
</div>
))}
</div>
<div className="rounded-3xl border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900 sm:p-5">
<div className="mb-4 flex items-center justify-between gap-3">
<div className="text-sm font-semibold text-slate-900 dark:text-white">{labels.chart}</div>
<div className="text-xs text-slate-500 dark:text-slate-400">{labels.loading}</div>
</div>
<div className="h-[320px] animate-pulse rounded-2xl bg-slate-200/80 dark:bg-slate-800/80 sm:h-[360px]" />
</div>
</div>
)
}
if (!data || data.series.length === 0) {
return null
}