profile refactor
This commit is contained in:
1
payment.json
Normal file
1
payment.json
Normal file
File diff suppressed because one or more lines are too long
@@ -161,17 +161,61 @@ export interface Payment {
|
||||
expired_date: string
|
||||
}
|
||||
|
||||
export interface OrderWithPayment {
|
||||
order: Order
|
||||
export type SbpWithdrawalStatus =
|
||||
| 'created'
|
||||
| 'waiting_usdt'
|
||||
| 'usdt_underpaid'
|
||||
| 'usdt_received'
|
||||
| 'wallet_error'
|
||||
| 'payout_processing'
|
||||
| 'payout_completed'
|
||||
| 'payout_failed'
|
||||
| 'cancelled'
|
||||
| 'expired'
|
||||
|
||||
export interface SbpWithdrawalOperation {
|
||||
id: string | null
|
||||
created_at: string | null
|
||||
updated_at: string | null
|
||||
user_id: string | null
|
||||
bank_id: string | null
|
||||
bank_name: string | null
|
||||
phone: string | null
|
||||
usdt_amount: string | null
|
||||
rub_amount: string | null
|
||||
usdt_exchange_rate: string | null
|
||||
service_fee_rate: string | null
|
||||
service_fee_usdt: string | null
|
||||
mozen_order_id: string | null
|
||||
trace_id: string | null
|
||||
status: SbpWithdrawalStatus | null
|
||||
provider_error: string | null
|
||||
usdt_received_at: string | null
|
||||
payout_created_at: string | null
|
||||
payout_completed_at: string | null
|
||||
}
|
||||
|
||||
export type ClientOperationType = 'payment' | 'withdrawal'
|
||||
|
||||
export interface ClientOperation {
|
||||
type: ClientOperationType | string
|
||||
created_at: string
|
||||
payment: Payment | null
|
||||
withdrawal: SbpWithdrawalOperation | null
|
||||
}
|
||||
|
||||
export interface OrdersResponse {
|
||||
orders: OrderWithPayment[]
|
||||
export interface ClientOperationsResponse {
|
||||
status_code: number
|
||||
operations: ClientOperation[]
|
||||
limit: number
|
||||
offset: number
|
||||
}
|
||||
|
||||
export const ORDERS_LIMIT = 20
|
||||
export const OPERATIONS_LIMIT = 20
|
||||
|
||||
export function getOrders(offset: number, limit: number = ORDERS_LIMIT): Promise<OrdersResponse> {
|
||||
return doPaymentRequest(`/payment/orders?offset=${offset}&limit=${limit}`, {}, true)
|
||||
export function getClientOperations(
|
||||
offset: number,
|
||||
limit: number = OPERATIONS_LIMIT,
|
||||
): Promise<ClientOperationsResponse> {
|
||||
return doPaymentRequest(`/payment/payments?offset=${offset}&limit=${limit}`, {}, true)
|
||||
}
|
||||
|
||||
15
src/features/payment/hooks/useOperations.ts
Normal file
15
src/features/payment/hooks/useOperations.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { useInfiniteQuery } from '@tanstack/react-query'
|
||||
import { getClientOperations, OPERATIONS_LIMIT } from '../api/paymentApi'
|
||||
|
||||
export function useOperations() {
|
||||
return useInfiniteQuery({
|
||||
queryKey: ['payment', 'operations'],
|
||||
queryFn: ({ pageParam }) => getClientOperations(pageParam as number),
|
||||
initialPageParam: 0,
|
||||
getNextPageParam: (lastPage, allPages) => {
|
||||
if (lastPage.operations.length < OPERATIONS_LIMIT) return undefined
|
||||
return allPages.length * OPERATIONS_LIMIT
|
||||
},
|
||||
staleTime: 30_000,
|
||||
})
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { useInfiniteQuery } from '@tanstack/react-query'
|
||||
import { getOrders, ORDERS_LIMIT } from '../api/paymentApi'
|
||||
|
||||
export function useOrders() {
|
||||
return useInfiniteQuery({
|
||||
queryKey: ['payment', 'orders'],
|
||||
queryFn: ({ pageParam }) => getOrders(pageParam as number),
|
||||
initialPageParam: 0,
|
||||
getNextPageParam: (lastPage, allPages) => {
|
||||
if (lastPage.orders.length < ORDERS_LIMIT) return undefined
|
||||
return allPages.length * ORDERS_LIMIT
|
||||
},
|
||||
staleTime: 30_000,
|
||||
})
|
||||
}
|
||||
@@ -2,6 +2,20 @@ export { usePaymentConfig } from './hooks/usePaymentConfig'
|
||||
export { usePaymentQuote } from './hooks/usePaymentQuote'
|
||||
export { usePaymentQuoteByRub } from './hooks/usePaymentQuoteByRub'
|
||||
export { useCreateOrder } from './hooks/useCreateOrder'
|
||||
export { useOrders } from './hooks/useOrders'
|
||||
export { useOperations } from './hooks/useOperations'
|
||||
export { useCurrencyConversion } from './model/useCurrencyConversion'
|
||||
export type { PaymentConfig, PaymentQuote, CreateOrderPayload, OrderResult, Order, Payment, OrderWithPayment, OrderStatus, PaymentStatus } from './api/paymentApi'
|
||||
export type {
|
||||
PaymentConfig,
|
||||
PaymentQuote,
|
||||
CreateOrderPayload,
|
||||
OrderResult,
|
||||
Order,
|
||||
Payment,
|
||||
OrderStatus,
|
||||
PaymentStatus,
|
||||
ClientOperation,
|
||||
ClientOperationsResponse,
|
||||
ClientOperationType,
|
||||
SbpWithdrawalOperation,
|
||||
SbpWithdrawalStatus,
|
||||
} from './api/paymentApi'
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { SbpWithdrawalStatus } from '@features/payment'
|
||||
|
||||
export const SBP_WITHDRAWAL_STATUS_LABELS: Record<SbpWithdrawalStatus, string> = {
|
||||
created: 'Создан',
|
||||
waiting_usdt: 'Ожидание USDT',
|
||||
usdt_underpaid: 'USDT недоплачен',
|
||||
usdt_received: 'USDT получен',
|
||||
wallet_error: 'Ошибка кошелька',
|
||||
payout_processing: 'Выплата в обработке',
|
||||
payout_completed: 'Выплата завершена',
|
||||
payout_failed: 'Выплата не прошла',
|
||||
cancelled: 'Отменён',
|
||||
expired: 'Истёк',
|
||||
}
|
||||
@@ -31,6 +31,18 @@
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.opType {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.6px;
|
||||
color: var(--text-secondary);
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border-radius: 6px;
|
||||
padding: 2px 8px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.summaryDate {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
187
src/widgets/transactions-list/ui/OperationAccordion.tsx
Normal file
187
src/widgets/transactions-list/ui/OperationAccordion.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
import { useState } from 'react'
|
||||
import type { ClientOperation, Payment, SbpWithdrawalOperation } from '@features/payment'
|
||||
import { formatDate, formatRub, truncateHash } from '../model/format'
|
||||
import { StatusBadge } from './StatusBadge'
|
||||
import { WithdrawalStatusBadge } from './WithdrawalStatusBadge'
|
||||
import { CopyButton } from './CopyButton'
|
||||
import styles from './OperationAccordion.module.css'
|
||||
|
||||
interface Props {
|
||||
op: ClientOperation
|
||||
}
|
||||
|
||||
export function OperationAccordion({ op }: Props) {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
// type — основной признак, но опираемся и на наличие данных (защита от пустых полей).
|
||||
const isWithdrawal = op.type === 'withdrawal' ? !!op.withdrawal : !op.payment && !!op.withdrawal
|
||||
|
||||
if (isWithdrawal && op.withdrawal) {
|
||||
return <Accordion isOpen={isOpen} onToggle={() => setIsOpen((v) => !v)} summary={withdrawalSummary(op.withdrawal)}>
|
||||
{withdrawalBody(op.withdrawal)}
|
||||
</Accordion>
|
||||
}
|
||||
|
||||
if (op.payment) {
|
||||
return <Accordion isOpen={isOpen} onToggle={() => setIsOpen((v) => !v)} summary={paymentSummary(op.payment)}>
|
||||
{paymentBody(op.payment)}
|
||||
</Accordion>
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
// --- Каркас аккордеона (общий для обоих типов операций) ---
|
||||
|
||||
interface AccordionProps {
|
||||
isOpen: boolean
|
||||
onToggle: () => void
|
||||
summary: React.ReactNode
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
function Accordion({ isOpen, onToggle, summary, children }: AccordionProps) {
|
||||
return (
|
||||
<div className={styles.item}>
|
||||
<button className={styles.summary} onClick={onToggle} type="button" aria-expanded={isOpen}>
|
||||
{summary}
|
||||
<svg
|
||||
className={`${styles.chevron} ${isOpen ? styles.chevronOpen : ''}`}
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div className={`${styles.bodyOuter} ${isOpen ? styles.bodyOuterOpen : ''}`}>
|
||||
<div className={styles.bodyInner}>
|
||||
<div className={styles.body}>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// --- Платёж ---
|
||||
|
||||
function paymentSummary(payment: Payment) {
|
||||
return (
|
||||
<>
|
||||
<span className={styles.summaryLeft}>
|
||||
<span className={styles.opType}>Платёж</span>
|
||||
<span className={styles.summaryDate}>{formatDate(payment.created_at)}</span>
|
||||
{payment.status && <StatusBadge status={payment.status} />}
|
||||
</span>
|
||||
<span className={styles.summaryRight}>
|
||||
{payment.itpay_paid_amount && (
|
||||
<span className={styles.totalAmount}>{formatRub(payment.itpay_paid_amount)} ₽</span>
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function paymentBody(payment: Payment) {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.col}>
|
||||
<p className={styles.colTitle}>Платёж</p>
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Статус</span>
|
||||
{payment.status ? <StatusBadge status={payment.status} /> : <span className={styles.infoValue}>—</span>}
|
||||
</div>
|
||||
<InfoRow label="Оплачено" value={payment.itpay_paid_amount || '—'} />
|
||||
<InfoRow label="Истекает" value={formatDate(payment.expired_date ?? '')} />
|
||||
</div>
|
||||
|
||||
<div className={styles.col}>
|
||||
<p className={styles.colTitle}>Транзакция</p>
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Хэш транзакции</span>
|
||||
<span className={styles.infoValueRow}>
|
||||
<span className={styles.infoValue} title={payment.web3_transaction_hash || undefined}>
|
||||
{truncateHash(payment.web3_transaction_hash ?? '')}
|
||||
</span>
|
||||
{payment.web3_transaction_hash && <CopyButton value={payment.web3_transaction_hash} />}
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Чек</span>
|
||||
{payment.receipt_cloudekassir_link ? (
|
||||
<a
|
||||
href={payment.receipt_cloudekassir_link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.infoLink}
|
||||
>
|
||||
Открыть →
|
||||
</a>
|
||||
) : (
|
||||
<span className={styles.infoValue}>—</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// --- Вывод (СБП) ---
|
||||
|
||||
function withdrawalSummary(w: SbpWithdrawalOperation) {
|
||||
return (
|
||||
<>
|
||||
<span className={styles.summaryLeft}>
|
||||
<span className={styles.opType}>Вывод СБП</span>
|
||||
<span className={styles.summaryDate}>{formatDate(w.created_at ?? '')}</span>
|
||||
{w.status && <WithdrawalStatusBadge status={w.status} />}
|
||||
</span>
|
||||
<span className={styles.summaryRight}>
|
||||
{w.usdt_amount && <span className={styles.amount}>{w.usdt_amount} USDT</span>}
|
||||
{w.rub_amount && <span className={styles.totalAmount}>{formatRub(w.rub_amount)} ₽</span>}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function withdrawalBody(w: SbpWithdrawalOperation) {
|
||||
return (
|
||||
<>
|
||||
<div className={styles.col}>
|
||||
<p className={styles.colTitle}>Вывод (СБП)</p>
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Статус</span>
|
||||
{w.status ? <WithdrawalStatusBadge status={w.status} /> : <span className={styles.infoValue}>—</span>}
|
||||
</div>
|
||||
<InfoRow label="Банк" value={w.bank_name || '—'} />
|
||||
<InfoRow label="Телефон" value={w.phone || '—'} />
|
||||
<InfoRow label="Создан" value={formatDate(w.payout_created_at ?? w.created_at ?? '')} />
|
||||
<InfoRow label="Завершён" value={formatDate(w.payout_completed_at ?? '')} />
|
||||
{w.provider_error && <InfoRow label="Ошибка" value={w.provider_error} />}
|
||||
</div>
|
||||
|
||||
<div className={styles.col}>
|
||||
<p className={styles.colTitle}>Суммы</p>
|
||||
<InfoRow label="Сумма USDT" value={w.usdt_amount ? `${w.usdt_amount} USDT` : '—'} />
|
||||
<InfoRow label="Курс обмена" value={w.usdt_exchange_rate ? `1 USDT = ${w.usdt_exchange_rate} ₽` : '—'} />
|
||||
<InfoRow label="Сервисный сбор" value={w.service_fee_usdt ? `${w.service_fee_usdt} USDT` : '—'} />
|
||||
<InfoRow label="Сумма к выводу" value={w.rub_amount ? `${formatRub(w.rub_amount)} ₽` : '—'} emphasised />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function InfoRow({ label, value, emphasised }: { label: string; value: string; emphasised?: boolean }) {
|
||||
return (
|
||||
<div className={`${styles.infoRow} ${emphasised ? styles.infoRowTotal : ''}`}>
|
||||
<span className={styles.infoLabel}>{label}</span>
|
||||
<span className={styles.infoValue}>{value}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
import { useState } from 'react'
|
||||
import type { OrderWithPayment } from '@features/payment'
|
||||
import { formatDate, formatRub, truncateHash } from '../model/format'
|
||||
import { StatusBadge } from './StatusBadge'
|
||||
import { CopyButton } from './CopyButton'
|
||||
import styles from './OrderAccordion.module.css'
|
||||
|
||||
interface Props {
|
||||
item: OrderWithPayment
|
||||
}
|
||||
|
||||
export function OrderAccordion({ item }: Props) {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const { order, payment } = item
|
||||
|
||||
return (
|
||||
<div className={styles.item}>
|
||||
<button
|
||||
className={styles.summary}
|
||||
onClick={() => setIsOpen(v => !v)}
|
||||
type="button"
|
||||
aria-expanded={isOpen}
|
||||
>
|
||||
<div className={styles.summaryLeft}>
|
||||
<span className={styles.summaryDate}>{formatDate(order.created_at)}</span>
|
||||
{payment && <StatusBadge status={payment.status} />}
|
||||
</div>
|
||||
<div className={styles.summaryRight}>
|
||||
<span className={styles.amount}>{order.usdt_amount} USDT</span>
|
||||
<span className={styles.totalAmount}>{formatRub(order.total_price)} ₽</span>
|
||||
<svg
|
||||
className={`${styles.chevron} ${isOpen ? styles.chevronOpen : ''}`}
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className={`${styles.bodyOuter} ${isOpen ? styles.bodyOuterOpen : ''}`}>
|
||||
<div className={styles.bodyInner}>
|
||||
<div className={styles.body}>
|
||||
<div className={styles.col}>
|
||||
<p className={styles.colTitle}>Заказ</p>
|
||||
<InfoRow label="Сумма USDT" value={`${order.usdt_amount} USDT`} />
|
||||
<InfoRow label="Курс обмена" value={`1 USDT = ${order.usdt_exchange_rate} ₽`} />
|
||||
<InfoRow label="Сервисный сбор" value={`${order.service_fee} USDT`} />
|
||||
<InfoRow label="Комиссия за газ" value={`${order.gas_fee} USDT`} />
|
||||
<InfoRow
|
||||
label="Итого к оплате"
|
||||
value={`${formatRub(order.total_price)} ₽`}
|
||||
emphasised
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={styles.col}>
|
||||
<p className={styles.colTitle}>Платёж</p>
|
||||
{payment ? (
|
||||
<>
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Статус</span>
|
||||
<StatusBadge status={payment.status} />
|
||||
</div>
|
||||
<InfoRow label="Оплачено" value={payment.itpay_paid_amount || '—'} />
|
||||
<InfoRow label="Истекает" value={formatDate(payment.expired_date)} />
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Хэш транзакции</span>
|
||||
<span className={styles.infoValueRow}>
|
||||
<span className={styles.infoValue} title={payment.web3_transaction_hash || undefined}>
|
||||
{truncateHash(payment.web3_transaction_hash)}
|
||||
</span>
|
||||
{payment.web3_transaction_hash && (
|
||||
<CopyButton value={payment.web3_transaction_hash} />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoLabel}>Чек</span>
|
||||
{payment.receipt_cloudekassir_link ? (
|
||||
<a
|
||||
href={payment.receipt_cloudekassir_link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.infoLink}
|
||||
>
|
||||
Открыть →
|
||||
</a>
|
||||
) : (
|
||||
<span className={styles.infoValue}>—</span>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className={styles.infoRow}>
|
||||
<span className={styles.infoValue}>—</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function InfoRow({ label, value, emphasised }: { label: string; value: string; emphasised?: boolean }) {
|
||||
return (
|
||||
<div className={`${styles.infoRow} ${emphasised ? styles.infoRowTotal : ''}`}>
|
||||
<span className={styles.infoLabel}>{label}</span>
|
||||
<span className={styles.infoValue}>{value}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
import { useOrders } from '@features/payment'
|
||||
import { useOperations } from '@features/payment'
|
||||
import { Spinner } from '@shared/ui'
|
||||
import { OrderAccordion } from './OrderAccordion'
|
||||
import { OperationAccordion } from './OperationAccordion'
|
||||
import styles from './TransactionsList.module.css'
|
||||
|
||||
export function TransactionsList() {
|
||||
const { data, isLoading, isError, fetchNextPage, hasNextPage, isFetchingNextPage } = useOrders()
|
||||
const { data, isLoading, isError, fetchNextPage, hasNextPage, isFetchingNextPage } = useOperations()
|
||||
|
||||
const items = data?.pages.flatMap(p => p.orders) ?? []
|
||||
const items = data?.pages.flatMap(p => p.operations) ?? []
|
||||
|
||||
if (isLoading) return <Spinner fullscreen label="Загрузка" />
|
||||
if (isError) return <p className={styles.statusError}>Не удалось загрузить транзакции. Попробуйте обновить страницу.</p>
|
||||
if (items.length === 0) return <p className={styles.empty}>У вас пока нет транзакций.</p>
|
||||
if (isError) return <p className={styles.statusError}>Не удалось загрузить операции. Попробуйте обновить страницу.</p>
|
||||
if (items.length === 0) return <p className={styles.empty}>У вас пока нет операций.</p>
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={styles.list}>
|
||||
{items.map(item => (
|
||||
<OrderAccordion key={item.order.id} item={item} />
|
||||
{items.map((op, i) => (
|
||||
<OperationAccordion key={op.payment?.id ?? op.withdrawal?.id ?? i} op={op} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
.badge {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
padding: 3px 10px;
|
||||
border-radius: 999px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Успех */
|
||||
.status_usdt_received,
|
||||
.status_payout_completed {
|
||||
background: rgba(0, 196, 140, 0.15);
|
||||
color: var(--success, #00c48c);
|
||||
}
|
||||
|
||||
/* В процессе / ожидание */
|
||||
.status_created {
|
||||
background: rgba(74, 109, 255, 0.15);
|
||||
color: #4a6dff;
|
||||
}
|
||||
|
||||
.status_waiting_usdt,
|
||||
.status_payout_processing {
|
||||
background: rgba(155, 109, 255, 0.15);
|
||||
color: #9b6dff;
|
||||
}
|
||||
|
||||
/* Ошибки */
|
||||
.status_usdt_underpaid,
|
||||
.status_wallet_error,
|
||||
.status_payout_failed,
|
||||
.status_expired {
|
||||
background: rgba(255, 77, 77, 0.15);
|
||||
color: #ff4d4d;
|
||||
}
|
||||
|
||||
/* Нейтральное */
|
||||
.status_cancelled {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
15
src/widgets/transactions-list/ui/WithdrawalStatusBadge.tsx
Normal file
15
src/widgets/transactions-list/ui/WithdrawalStatusBadge.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { SbpWithdrawalStatus } from '@features/payment'
|
||||
import { SBP_WITHDRAWAL_STATUS_LABELS } from '../model/sbpWithdrawalStatusLabels'
|
||||
import styles from './WithdrawalStatusBadge.module.css'
|
||||
|
||||
interface Props {
|
||||
status: SbpWithdrawalStatus
|
||||
}
|
||||
|
||||
export function WithdrawalStatusBadge({ status }: Props) {
|
||||
return (
|
||||
<span className={`${styles.badge} ${styles[`status_${status}`] ?? ''}`}>
|
||||
{SBP_WITHDRAWAL_STATUS_LABELS[status] ?? status}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user