From 74c4c87ec076210b996cbe613b44d074784e223e Mon Sep 17 00:00:00 2001 From: Fringg Date: Tue, 17 Feb 2026 12:42:42 +0300 Subject: [PATCH] refactor: extract shared withdrawal utils and fix status badge fallbacks - extract formatDate, statusBadgeConfig, getRiskColor to shared utils - use explicit unknown fallback for unrecognized status values - add missing 'unknown' i18n keys for withdrawal status --- src/locales/en.json | 3 +- src/locales/ru.json | 3 +- src/pages/AdminPartnerDetail.tsx | 8 ++- src/pages/AdminWithdrawalDetail.tsx | 78 +++------------------------ src/pages/AdminWithdrawals.tsx | 56 +------------------ src/utils/withdrawalUtils.ts | 83 +++++++++++++++++++++++++++++ 6 files changed, 103 insertions(+), 128 deletions(-) create mode 100644 src/utils/withdrawalUtils.ts diff --git a/src/locales/en.json b/src/locales/en.json index 6930da5..23174df 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1825,7 +1825,8 @@ "approved": "Approved", "rejected": "Rejected", "completed": "Completed", - "cancelled": "Cancelled" + "cancelled": "Cancelled", + "unknown": "Unknown" }, "detail": { "title": "Withdrawal", diff --git a/src/locales/ru.json b/src/locales/ru.json index eb26074..1bec558 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -2347,7 +2347,8 @@ "approved": "Одобрено", "rejected": "Отклонено", "completed": "Выполнено", - "cancelled": "Отменено" + "cancelled": "Отменено", + "unknown": "Неизвестно" }, "detail": { "title": "Заявка на вывод", diff --git a/src/pages/AdminPartnerDetail.tsx b/src/pages/AdminPartnerDetail.tsx index 1d7c5c5..3349bf3 100644 --- a/src/pages/AdminPartnerDetail.tsx +++ b/src/pages/AdminPartnerDetail.tsx @@ -29,6 +29,12 @@ const statusConfig: Record(); @@ -82,7 +88,7 @@ export default function AdminPartnerDetail() { ); } - const badge = statusConfig[partner.partner_status] || statusConfig.none; + const badge = statusConfig[partner.partner_status] || unknownStatus; return (
diff --git a/src/pages/AdminWithdrawalDetail.tsx b/src/pages/AdminWithdrawalDetail.tsx index 1cd7c0d..d243bbb 100644 --- a/src/pages/AdminWithdrawalDetail.tsx +++ b/src/pages/AdminWithdrawalDetail.tsx @@ -1,79 +1,15 @@ import { useParams, useNavigate } from 'react-router'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; -import i18n from '../i18n'; import { withdrawalApi } from '../api/withdrawals'; import { AdminBackButton } from '../components/admin'; import { useCurrency } from '../hooks/useCurrency'; - -// Locale mapping for formatting -const localeMap: Record = { ru: 'ru-RU', en: 'en-US', zh: 'zh-CN', fa: 'fa-IR' }; - -const formatDate = (date: string | null): string => { - if (!date) return '-'; - const locale = localeMap[i18n.language] || 'ru-RU'; - return new Date(date).toLocaleDateString(locale, { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit', - }); -}; - -// Status badge config -const statusBadgeConfig: Record = { - pending: { - labelKey: 'admin.withdrawals.status.pending', - color: 'text-yellow-400', - bgColor: 'bg-yellow-500/20', - }, - approved: { - labelKey: 'admin.withdrawals.status.approved', - color: 'text-accent-400', - bgColor: 'bg-accent-500/20', - }, - rejected: { - labelKey: 'admin.withdrawals.status.rejected', - color: 'text-error-400', - bgColor: 'bg-error-500/20', - }, - completed: { - labelKey: 'admin.withdrawals.status.completed', - color: 'text-success-400', - bgColor: 'bg-success-500/20', - }, - cancelled: { - labelKey: 'admin.withdrawals.status.cancelled', - color: 'text-dark-400', - bgColor: 'bg-dark-500/20', - }, -}; - -// Risk score color helper -function getRiskColor(score: number): { text: string; bg: string; bar: string } { - if (score < 30) - return { text: 'text-success-400', bg: 'bg-success-500/20', bar: 'bg-success-500' }; - if (score < 50) return { text: 'text-yellow-400', bg: 'bg-yellow-500/20', bar: 'bg-yellow-500' }; - if (score < 70) return { text: 'text-orange-400', bg: 'bg-orange-500/20', bar: 'bg-orange-500' }; - return { text: 'text-error-400', bg: 'bg-error-500/20', bar: 'bg-error-500' }; -} - -// Risk level badge color -function getRiskLevelColor(level: string): { text: string; bg: string } { - switch (level) { - case 'low': - return { text: 'text-success-400', bg: 'bg-success-500/20' }; - case 'medium': - return { text: 'text-yellow-400', bg: 'bg-yellow-500/20' }; - case 'high': - return { text: 'text-orange-400', bg: 'bg-orange-500/20' }; - case 'critical': - return { text: 'text-error-400', bg: 'bg-error-500/20' }; - default: - return { text: 'text-dark-400', bg: 'bg-dark-500/20' }; - } -} +import { + formatDate, + getWithdrawalStatusBadge, + getRiskColor, + getRiskLevelColor, +} from '../utils/withdrawalUtils'; // Type for parsed risk analysis interface RiskAnalysis { @@ -152,7 +88,7 @@ export default function AdminWithdrawalDetail() { ); } - const badge = statusBadgeConfig[detail.status] || statusBadgeConfig.cancelled; + const badge = getWithdrawalStatusBadge(detail.status); const riskColor = getRiskColor(detail.risk_score); // Parse risk analysis diff --git a/src/pages/AdminWithdrawals.tsx b/src/pages/AdminWithdrawals.tsx index fa6d12a..ce8aa87 100644 --- a/src/pages/AdminWithdrawals.tsx +++ b/src/pages/AdminWithdrawals.tsx @@ -2,25 +2,10 @@ import { useState } from 'react'; import { useNavigate } from 'react-router'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; -import i18n from '../i18n'; import { withdrawalApi, AdminWithdrawalItem } from '../api/withdrawals'; import { AdminBackButton } from '../components/admin'; import { useCurrency } from '../hooks/useCurrency'; - -// Locale mapping for formatting -const localeMap: Record = { ru: 'ru-RU', en: 'en-US', zh: 'zh-CN', fa: 'fa-IR' }; - -const formatDate = (date: string | null): string => { - if (!date) return '-'; - const locale = localeMap[i18n.language] || 'ru-RU'; - return new Date(date).toLocaleDateString(locale, { - day: '2-digit', - month: '2-digit', - year: 'numeric', - hour: '2-digit', - minute: '2-digit', - }); -}; +import { formatDate, getWithdrawalStatusBadge, getRiskColor } from '../utils/withdrawalUtils'; // Status filter tabs type StatusFilter = 'all' | 'pending' | 'approved' | 'rejected' | 'completed' | 'cancelled'; @@ -34,43 +19,6 @@ const STATUS_FILTERS: StatusFilter[] = [ 'cancelled', ]; -// Status badge config -const statusBadgeConfig: Record = { - pending: { - labelKey: 'admin.withdrawals.status.pending', - color: 'text-yellow-400', - bgColor: 'bg-yellow-500/20', - }, - approved: { - labelKey: 'admin.withdrawals.status.approved', - color: 'text-accent-400', - bgColor: 'bg-accent-500/20', - }, - rejected: { - labelKey: 'admin.withdrawals.status.rejected', - color: 'text-error-400', - bgColor: 'bg-error-500/20', - }, - completed: { - labelKey: 'admin.withdrawals.status.completed', - color: 'text-success-400', - bgColor: 'bg-success-500/20', - }, - cancelled: { - labelKey: 'admin.withdrawals.status.cancelled', - color: 'text-dark-400', - bgColor: 'bg-dark-500/20', - }, -}; - -// Risk score color helper -function getRiskColor(score: number): { text: string; bg: string } { - if (score < 30) return { text: 'text-success-400', bg: 'bg-success-500/20' }; - if (score < 50) return { text: 'text-yellow-400', bg: 'bg-yellow-500/20' }; - if (score < 70) return { text: 'text-orange-400', bg: 'bg-orange-500/20' }; - return { text: 'text-error-400', bg: 'bg-error-500/20' }; -} - export default function AdminWithdrawals() { const { t } = useTranslation(); const navigate = useNavigate(); @@ -154,7 +102,7 @@ export default function AdminWithdrawals() { ) : (
{items.map((item: AdminWithdrawalItem) => { - const badge = statusBadgeConfig[item.status] || statusBadgeConfig.cancelled; + const badge = getWithdrawalStatusBadge(item.status); const riskColor = getRiskColor(item.risk_score); return ( diff --git a/src/utils/withdrawalUtils.ts b/src/utils/withdrawalUtils.ts new file mode 100644 index 0000000..a168bfb --- /dev/null +++ b/src/utils/withdrawalUtils.ts @@ -0,0 +1,83 @@ +import i18n from '../i18n'; + +export const localeMap: Record = { + ru: 'ru-RU', + en: 'en-US', + zh: 'zh-CN', + fa: 'fa-IR', +}; + +export const formatDate = (date: string | null): string => { + if (!date) return '-'; + const locale = localeMap[i18n.language] || 'ru-RU'; + return new Date(date).toLocaleDateString(locale, { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + }); +}; + +export type StatusBadge = { labelKey: string; color: string; bgColor: string }; + +export const withdrawalStatusBadgeConfig: Record = { + pending: { + labelKey: 'admin.withdrawals.status.pending', + color: 'text-yellow-400', + bgColor: 'bg-yellow-500/20', + }, + approved: { + labelKey: 'admin.withdrawals.status.approved', + color: 'text-accent-400', + bgColor: 'bg-accent-500/20', + }, + rejected: { + labelKey: 'admin.withdrawals.status.rejected', + color: 'text-error-400', + bgColor: 'bg-error-500/20', + }, + completed: { + labelKey: 'admin.withdrawals.status.completed', + color: 'text-success-400', + bgColor: 'bg-success-500/20', + }, + cancelled: { + labelKey: 'admin.withdrawals.status.cancelled', + color: 'text-dark-400', + bgColor: 'bg-dark-500/20', + }, +}; + +const unknownBadge: StatusBadge = { + labelKey: 'admin.withdrawals.status.unknown', + color: 'text-dark-400', + bgColor: 'bg-dark-600', +}; + +export function getWithdrawalStatusBadge(status: string): StatusBadge { + return withdrawalStatusBadgeConfig[status] || unknownBadge; +} + +export function getRiskColor(score: number): { text: string; bg: string; bar: string } { + if (score < 30) + return { text: 'text-success-400', bg: 'bg-success-500/20', bar: 'bg-success-500' }; + if (score < 50) return { text: 'text-yellow-400', bg: 'bg-yellow-500/20', bar: 'bg-yellow-500' }; + if (score < 70) return { text: 'text-orange-400', bg: 'bg-orange-500/20', bar: 'bg-orange-500' }; + return { text: 'text-error-400', bg: 'bg-error-500/20', bar: 'bg-error-500' }; +} + +export function getRiskLevelColor(level: string): { text: string; bg: string } { + switch (level) { + case 'low': + return { text: 'text-success-400', bg: 'bg-success-500/20' }; + case 'medium': + return { text: 'text-yellow-400', bg: 'bg-yellow-500/20' }; + case 'high': + return { text: 'text-orange-400', bg: 'bg-orange-500/20' }; + case 'critical': + return { text: 'text-error-400', bg: 'bg-error-500/20' }; + default: + return { text: 'text-dark-400', bg: 'bg-dark-500/20' }; + } +}