Add COD-in-transit summary and received tab to the dashboard
- GET /orders/summary: count and sum of cash-on-delivery for parcels not yet picked up (excludes received, refused, deleted/unknown TTNs, paid COD); shown as a card above the tabs. - New «Полученные» tab: orders with NP received codes (9, 10, 11, 106) move there automatically, regardless of receipt; 106 is now a final status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ import '@/pages/DashboardPage.css'
|
||||
import { useAuth } from '@/features/auth/useAuth'
|
||||
import { OrderDetailModal } from '@/features/orders/OrderDetailModal'
|
||||
import type { Order, OrderTab } from '@/features/orders/types'
|
||||
import { useOrders } from '@/features/orders/useOrders'
|
||||
import { useOrders, useOrdersSummary } from '@/features/orders/useOrders'
|
||||
import { defaultPrepayment, prepaymentMatches, toKopecks } from '@/features/receipts/money'
|
||||
import { CANCELLABLE, RECEIPT_STATUS } from '@/features/receipts/types'
|
||||
import type { ReceiptRequestItem } from '@/features/receipts/types'
|
||||
@@ -22,6 +22,7 @@ const ROLE_LABEL: Record<string, string> = {
|
||||
const TABS: { key: OrderTab; label: string }[] = [
|
||||
{ key: 'no_receipt', label: 'Без чека' },
|
||||
{ key: 'has_receipt', label: 'Выписаны чеки' },
|
||||
{ key: 'received', label: 'Полученные' },
|
||||
{ key: 'refused', label: 'Отказы' },
|
||||
]
|
||||
|
||||
@@ -44,10 +45,17 @@ function paymentBadge(order: Order): { label: string; tone: 'delivered' | 'proce
|
||||
return null
|
||||
}
|
||||
|
||||
const MONEY_FORMAT = new Intl.NumberFormat('ru-RU', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
||||
|
||||
function formatMoney(amount: string): string {
|
||||
return MONEY_FORMAT.format(Number(amount))
|
||||
}
|
||||
|
||||
export function DashboardPage() {
|
||||
const { user, logout } = useAuth()
|
||||
const [tab, setTab] = useState<OrderTab>('no_receipt')
|
||||
const { data: orders, isLoading, isError } = useOrders(tab)
|
||||
const { data: summary } = useOrdersSummary()
|
||||
const queryClient = useQueryClient()
|
||||
const [selected, setSelected] = useState<Set<string>>(new Set())
|
||||
const [search, setSearch] = useState('')
|
||||
@@ -102,7 +110,12 @@ export function DashboardPage() {
|
||||
|
||||
async function handleCancel(order: Order) {
|
||||
if (!order.receipt_id) return
|
||||
const outcome = tab === 'refused' ? 'Заказ останется в отказах.' : 'Заказ вернётся в очередь.'
|
||||
const outcome = {
|
||||
no_receipt: 'Заказ вернётся в очередь.',
|
||||
has_receipt: 'Заказ вернётся в очередь.',
|
||||
received: 'Заказ останется в полученных.',
|
||||
refused: 'Заказ останется в отказах.',
|
||||
}[tab]
|
||||
if (!window.confirm(`Отменить ЕТТН-чек по заказу ${order.id}? ${outcome}`)) return
|
||||
setCancellingId(order.id)
|
||||
try {
|
||||
@@ -193,6 +206,18 @@ export function DashboardPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="orders-summary">
|
||||
<div className="orders-summary-card" title="Посылки с наложкой, которые ещё не забрали и по которым нет отказа">
|
||||
<span className="orders-summary-label">Наложка в пути</span>
|
||||
<span className="orders-summary-value">
|
||||
{summary ? `${formatMoney(summary.cod_in_transit_amount)} ₴` : '—'}
|
||||
</span>
|
||||
<span className="orders-summary-hint">
|
||||
{summary ? `${summary.cod_in_transit_count} посылок` : ' '}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{notice && (
|
||||
<div className={`orders-notice orders-notice--${notice.tone}`}>
|
||||
<ul>
|
||||
@@ -354,7 +379,7 @@ export function DashboardPage() {
|
||||
Отменить
|
||||
</button>
|
||||
)}
|
||||
{canDelete && (tab === 'no_receipt' || (tab === 'refused' && !order.has_receipt)) && (
|
||||
{canDelete && (tab === 'no_receipt' || ((tab === 'refused' || tab === 'received') && !order.has_receipt)) && (
|
||||
<button
|
||||
type="button"
|
||||
className="orders-delete-btn"
|
||||
|
||||
Reference in New Issue
Block a user