|
|
|
|
@@ -43,6 +43,14 @@ const formatMoneyTotals = (totals: { currency: string; amount: string }[], lang:
|
|
|
|
|
return totals.map((item) => `${formatAmount(item.amount, lang)} ${currencyLabel(item.currency, lang)}`).join(" | ");
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const formatHourlyRate = (
|
|
|
|
|
rate: { currency: string; amount: string } | null,
|
|
|
|
|
lang: "en" | "fa",
|
|
|
|
|
) => {
|
|
|
|
|
if (!rate) return "-";
|
|
|
|
|
return `${formatAmount(rate.amount, lang)} ${currencyLabel(rate.currency, lang)}`;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const formatDisplayDate = (value: string, lang: "en" | "fa") => {
|
|
|
|
|
const parsed = new Date(`${value}T00:00:00`);
|
|
|
|
|
return new Intl.DateTimeFormat(lang === "fa" ? "fa-IR" : "en-US", {
|
|
|
|
|
@@ -209,6 +217,10 @@ export function ReportsTablePanel({
|
|
|
|
|
<div className="mb-1 text-[11px] uppercase tracking-[0.12em] text-slate-400 dark:text-slate-500">{labels.nonBillableHours}</div>
|
|
|
|
|
<div className="font-medium">{localizeDigits(day.non_billable_duration, lang)}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<div className="mb-1 text-[11px] uppercase tracking-[0.12em] text-slate-400 dark:text-slate-500">{labels.hourlyRate}</div>
|
|
|
|
|
<div className="font-medium">{formatHourlyRate(day.latest_hourly_rate, lang)}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="col-span-2">
|
|
|
|
|
<div className="mb-1 text-[11px] uppercase tracking-[0.12em] text-slate-400 dark:text-slate-500">{labels.totalIncome}</div>
|
|
|
|
|
<div className="font-medium">{formatMoneyTotals(day.income_totals, lang)}</div>
|
|
|
|
|
@@ -249,9 +261,10 @@ export function ReportsTablePanel({
|
|
|
|
|
<thead>
|
|
|
|
|
<tr className="border-b border-slate-200 text-slate-500 dark:border-slate-800 dark:text-slate-400">
|
|
|
|
|
<th className="w-[18%] px-3 py-3 text-start font-medium">{labels.date}</th>
|
|
|
|
|
<th className="w-[16%] px-3 py-3 text-start font-medium">{labels.billableHours}</th>
|
|
|
|
|
<th className="w-[20%] px-3 py-3 text-start font-medium">{labels.nonBillableHours}</th>
|
|
|
|
|
<th className="w-[36%] px-3 py-3 text-start font-medium">{labels.totalIncome}</th>
|
|
|
|
|
<th className="w-[14%] px-3 py-3 text-start font-medium">{labels.billableHours}</th>
|
|
|
|
|
<th className="w-[14%] px-3 py-3 text-start font-medium">{labels.nonBillableHours}</th>
|
|
|
|
|
<th className="w-[18%] px-3 py-3 text-start font-medium">{labels.hourlyRate}</th>
|
|
|
|
|
<th className="w-[26%] px-3 py-3 text-start font-medium">{labels.totalIncome}</th>
|
|
|
|
|
<th className="w-[10%] px-3 py-3 text-start font-medium">{labels.details}</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
@@ -264,6 +277,7 @@ export function ReportsTablePanel({
|
|
|
|
|
<td className="px-3 py-3 font-medium text-slate-900 dark:text-slate-100">{formatDisplayDate(day.date, lang)}</td>
|
|
|
|
|
<td className="px-3 py-3 text-slate-700 dark:text-slate-300">{localizeDigits(day.billable_duration, lang)}</td>
|
|
|
|
|
<td className="px-3 py-3 text-slate-700 dark:text-slate-300">{localizeDigits(day.non_billable_duration, lang)}</td>
|
|
|
|
|
<td className="px-3 py-3 text-slate-700 dark:text-slate-300">{formatHourlyRate(day.latest_hourly_rate, lang)}</td>
|
|
|
|
|
<td className="px-3 py-3 text-slate-700 dark:text-slate-300">{formatMoneyTotals(day.income_totals, lang)}</td>
|
|
|
|
|
<td className="px-3 py-3">
|
|
|
|
|
<button
|
|
|
|
|
@@ -300,6 +314,7 @@ export function ReportsTablePanel({
|
|
|
|
|
<td className="px-3 py-3">{labels.total}</td>
|
|
|
|
|
<td className="px-3 py-3">{localizeDigits(data.summary.billable_duration, lang)}</td>
|
|
|
|
|
<td className="px-3 py-3">{localizeDigits(data.summary.non_billable_duration, lang)}</td>
|
|
|
|
|
<td className="px-3 py-3">-</td>
|
|
|
|
|
<td className="px-3 py-3">{formatMoneyTotals(data.summary.income_totals, lang)}</td>
|
|
|
|
|
<td className="px-3 py-3" />
|
|
|
|
|
</tr>
|
|
|
|
|
|