fix: improve campaign stats, shared chart components, and i18n coverage

- Extract shared chart constants to constants/charts.ts
- Add TREND_STYLES to stats/constants.ts for reuse
- Fix useChartColors with MutationObserver for theme changes
- Fix DailyChart date parsing to prevent timezone shift
- Add truncate to StatCard value for overflow safety
- Fix CampaignCard copy feedback inside try block
- Add NaN validation and staleTime to AdminCampaignStats
- Replace hardcoded strings with i18n keys in Referral page
- Add missing i18n keys across all 4 locales (en, ru, zh, fa)
- Add sales stats route to AdminPanel and App router
This commit is contained in:
Fringg
2026-03-02 20:34:24 +03:00
parent c7d05c4809
commit 673de08dd4
16 changed files with 449 additions and 84 deletions

View File

@@ -56,12 +56,15 @@ export function CampaignCard({ campaign }: CampaignCardProps) {
try {
await copyToClipboard(url);
haptic.notification('success');
setCopiedLink(key);
clearTimeout(copyTimerRef.current);
copyTimerRef.current = setTimeout(
() => setCopiedLink(null),
PARTNER_STATS.COPY_FEEDBACK_MS,
);
} catch {
haptic.notification('error');
}
setCopiedLink(key);
clearTimeout(copyTimerRef.current);
copyTimerRef.current = setTimeout(() => setCopiedLink(null), PARTNER_STATS.COPY_FEEDBACK_MS);
},
[haptic],
);

View File

@@ -42,7 +42,7 @@ export function DailyChart({ data, chartId, title, earningsLabel, countLabel }:
data.map((item) => ({
...item,
earnings_display: item.earnings_kopeks / PARTNER_STATS.KOPEKS_DIVISOR,
label: new Date(item.date).toLocaleDateString(i18n.language, {
label: new Date(item.date + 'T00:00:00').toLocaleDateString(i18n.language, {
month: 'short',
day: 'numeric',
}),

View File

@@ -1,6 +1,7 @@
import { useTranslation } from 'react-i18next';
import type { PeriodComparison as PeriodComparisonType } from './types';
import { TREND_STYLES } from './constants';
import { PARTNER_STATS } from '../../constants/partner';
import { useCurrency } from '../../hooks/useCurrency';
@@ -12,16 +13,6 @@ interface PeriodComparisonProps {
comparisonLabel?: string;
}
const TREND_ARROW_UP = '\u2191';
const TREND_ARROW_DOWN = '\u2193';
const TREND_ARROW_STABLE = '\u2192';
const TREND_STYLES = {
up: { arrow: TREND_ARROW_UP, className: 'text-success-400' },
down: { arrow: TREND_ARROW_DOWN, className: 'text-error-400' },
stable: { arrow: TREND_ARROW_STABLE, className: 'text-dark-400' },
} as const;
interface TrendBadgeProps {
trend: 'up' | 'down' | 'stable';
percent: number;

View File

@@ -21,7 +21,9 @@ export function StatCard({
{icon}
<span>{label}</span>
</div>
<div className={`mt-1 text-base font-semibold sm:text-lg ${valueClassName}`}>{value}</div>
<div className={`mt-1 truncate text-base font-semibold sm:text-lg ${valueClassName}`}>
{value}
</div>
</div>
);
}

View File

@@ -0,0 +1,5 @@
export const TREND_STYLES = {
up: { arrow: '\u2191', className: 'text-success-400' },
down: { arrow: '\u2193', className: 'text-error-400' },
stable: { arrow: '\u2192', className: 'text-dark-400' },
} as const;