Skip to content

Commit 3e313e4

Browse files
committed
fix: align order dashboard sidebar badge count
1 parent e722db3 commit 3e313e4

7 files changed

Lines changed: 95 additions & 11 deletions

File tree

apps/medusa-be/src/api/admin/order-expedition/summary/route.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
ACTION_REQUIRED_ORDER_BUSINESS_STATUS_IDS,
1010
ORDER_BUSINESS_STATUS_IDS,
1111
type OrderBusinessStatusId,
12+
isPendingUnpaidOrder,
1213
resolveOrderBusinessStatus,
1314
} from "../../../../utils/order-business-status"
1415
import {
@@ -22,6 +23,7 @@ const ORDER_EXPEDITION_SUMMARY_BATCH_SIZE = 500
2223

2324
type OrderExpeditionSummaryResponse = {
2425
action_required_count: number
26+
pending_unpaid_count: number
2527
scanned_count: number
2628
status_counts: Record<OrderBusinessStatusId, number>
2729
total_count: number
@@ -40,6 +42,7 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
4042
const query = req.scope.resolve<Query>(ContainerRegistrationKeys.QUERY)
4143
let offset = 0
4244
let totalCount: number | null = null
45+
let pendingUnpaidCount = 0
4346
let scannedCount = 0
4447
const statusCounts = createEmptyStatusCounts()
4548

@@ -59,6 +62,7 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
5962
for (const order of orders) {
6063
const statusId = resolveOrderBusinessStatus(order).id
6164
statusCounts[statusId] += 1
65+
pendingUnpaidCount += isPendingUnpaidOrder(order) ? 1 : 0
6266
}
6367

6468
offset += orders.length
@@ -70,6 +74,7 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
7074

7175
const summary: OrderExpeditionSummaryResponse = {
7276
action_required_count: getActionRequiredCount(statusCounts),
77+
pending_unpaid_count: pendingUnpaidCount,
7378
scanned_count: scannedCount,
7479
status_counts: statusCounts,
7580
total_count: totalCount ?? scannedCount,
@@ -130,6 +135,7 @@ function isOrderExpeditionSummaryResponse(
130135

131136
return (
132137
typeof summary.action_required_count === "number" &&
138+
typeof summary.pending_unpaid_count === "number" &&
133139
typeof summary.scanned_count === "number" &&
134140
typeof summary.total_count === "number" &&
135141
typeof summary.unhandled_count === "number" &&

apps/medusa-be/src/utils/order-business-status.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@ const AWAITING_PAYMENT_STATUSES = new Set([
123123
])
124124

125125
const PAID_PAYMENT_STATUSES = new Set(["captured", "completed"])
126+
const PENDING_UNPAID_PAYMENT_STATUSES = new Set([
127+
"authorized",
128+
"awaiting",
129+
"not_paid",
130+
"partially_authorized",
131+
"requires_action",
132+
])
126133

127134
const SHIPPED_FULFILLMENT_STATUSES = new Set([
128135
"partially_delivered",
@@ -224,6 +231,16 @@ export function getOrderBusinessPaymentStatus(order: OrderBusinessStatusInput) {
224231
)
225232
}
226233

234+
export function isPendingUnpaidOrder(order: OrderBusinessStatusInput) {
235+
if (order.status !== "pending") {
236+
return false
237+
}
238+
239+
return PENDING_UNPAID_PAYMENT_STATUSES.has(
240+
getOrderBusinessPaymentStatus(order) ?? ""
241+
)
242+
}
243+
227244
function hasPaidPaymentSignal(order: OrderBusinessStatusInput) {
228245
const paymentStatus = order.payment_status
229246

apps/medusa-be/tests/unit/src/utils/order-business-status.unit.spec.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it } from "vitest"
22
import {
33
getOrderBusinessManualStatusUpdateBlockReason,
4+
isPendingUnpaidOrder,
45
isManualOrderBusinessStatusId,
56
ORDER_BUSINESS_STATUS_METADATA_KEY,
67
ORDER_BUSINESS_STATUSES,
@@ -116,6 +117,55 @@ describe("order business status", () => {
116117
).not.toBe("paid")
117118
})
118119

120+
it.each([
121+
{
122+
name: "there are no payment collections",
123+
overrides: { payment_collections: [] },
124+
},
125+
{
126+
name: "payment requires action",
127+
overrides: { payment_status: "requires_action" },
128+
},
129+
{
130+
name: "payment is only authorized",
131+
overrides: { payment_status: "authorized" },
132+
},
133+
{
134+
name: "payment is partially authorized",
135+
overrides: { payment_status: "partially_authorized" },
136+
},
137+
] satisfies {
138+
name: string
139+
overrides: Partial<OrderBusinessStatusInput>
140+
}[])("counts pending unpaid orders when $name", ({ overrides }) => {
141+
expect(isPendingUnpaidOrder(createOrder({ status: "pending", ...overrides })))
142+
.toBe(true)
143+
})
144+
145+
it.each([
146+
{
147+
name: "the order is not pending",
148+
overrides: { payment_status: "authorized", status: "completed" },
149+
},
150+
{
151+
name: "payment is captured",
152+
overrides: { payment_status: "captured", status: "pending" },
153+
},
154+
{
155+
name: "payment is completed",
156+
overrides: { payment_status: "completed", status: "pending" },
157+
},
158+
{
159+
name: "payment is partially captured",
160+
overrides: { payment_status: "partially_captured", status: "pending" },
161+
},
162+
] satisfies {
163+
name: string
164+
overrides: Partial<OrderBusinessStatusInput>
165+
}[])("does not count pending unpaid orders when $name", ({ overrides }) => {
166+
expect(isPendingUnpaidOrder(createOrder(overrides))).toBe(false)
167+
})
168+
119169
it("lets manual processing states override paid orders", () => {
120170
expect(
121171
resolveOrderBusinessStatus(

apps/medusa-order-dashboard-plugin/src/admin/routes/order-dashboard/i18n.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,8 @@ const englishOrderDashboardAdminI18n = {
330330
all: "All",
331331
},
332332
sidebar: {
333-
actionRequiredOrders: "{{count}} action required orders",
333+
actionRequiredOrders:
334+
"{{count}} orders waiting for admin confirmation without captured payment",
334335
},
335336
statuses: {
336337
awaiting_payment: "Awaiting payment",

apps/medusa-order-dashboard-plugin/src/admin/routes/order-dashboard/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -699,13 +699,13 @@ const OrderDashboardPage = () => {
699699
const errorMessage = ordersQuery.error
700700
? getErrorMessage(ordersQuery.error, t("toast.requestFailed"))
701701
: null
702-
const actionRequiredCount = summaryQuery.data?.action_required_count ?? 0
702+
const pendingUnpaidCount = summaryQuery.data?.pending_unpaid_count ?? 0
703703

704704
useEffect(() => {
705705
setOrderDashboardSidebarBadgeCount(
706-
summaryQuery.isLoading ? null : actionRequiredCount
706+
summaryQuery.isLoading ? null : pendingUnpaidCount
707707
)
708-
}, [actionRequiredCount, summaryQuery.isLoading])
708+
}, [pendingUnpaidCount, summaryQuery.isLoading])
709709

710710
useEffect(() => {
711711
if (selectedCount > 0) {

apps/medusa-order-dashboard-plugin/src/admin/routes/order-dashboard/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ export type OrderDashboardOrdersResponse = {
201201

202202
export type OrderDashboardSummaryResponse = {
203203
action_required_count: number
204+
pending_unpaid_count: number
204205
scanned_count: number
205206
status_counts: Record<OrderDashboardBusinessStatusId, number>
206207
total_count: number

apps/medusa-order-dashboard-plugin/src/admin/sidebar-badge.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ export function startOrderDashboardSidebarBadge() {
5555
)
5656
}
5757

58-
export function setOrderDashboardSidebarBadgeCount(count: number | null) {
59-
currentCount = count
58+
export function setOrderDashboardSidebarBadgeCount(
59+
count: number | null | undefined
60+
) {
61+
currentCount = normalizeOrderDashboardSidebarBadgeCount(count)
6062
renderOrderDashboardSidebarBadge(currentCount)
6163
}
6264

@@ -73,7 +75,7 @@ async function refreshOrderDashboardSidebarBadge() {
7375
const summary = await sdk.client.fetch<OrderDashboardSummaryResponse>(
7476
"/admin/order-expedition/summary"
7577
)
76-
setOrderDashboardSidebarBadgeCount(summary.action_required_count)
78+
setOrderDashboardSidebarBadgeCount(summary.pending_unpaid_count)
7779
} catch {
7880
renderOrderDashboardSidebarBadge(currentCount)
7981
} finally {
@@ -88,14 +90,15 @@ function canRefreshOrderDashboardSidebarBadge() {
8890
)
8991
}
9092

91-
function renderOrderDashboardSidebarBadge(count: number | null) {
93+
function renderOrderDashboardSidebarBadge(count: number | null | undefined) {
9294
if (typeof document === "undefined") {
9395
return
9496
}
9597

9698
const link = getOrderDashboardSidebarLink()
99+
const normalizedCount = normalizeOrderDashboardSidebarBadgeCount(count)
97100

98-
if (!link || count === null || count <= 0) {
101+
if (!link || normalizedCount === null || normalizedCount <= 0) {
99102
removeOrderDashboardSidebarBadge()
100103
return
101104
}
@@ -124,8 +127,8 @@ function renderOrderDashboardSidebarBadge(count: number | null) {
124127
link.appendChild(badge)
125128
}
126129

127-
const countText = String(count)
128-
const label = getOrderDashboardSidebarBadgeLabel(count)
130+
const countText = String(normalizedCount)
131+
const label = getOrderDashboardSidebarBadgeLabel(normalizedCount)
129132

130133
if (badge.textContent !== countText) {
131134
badge.textContent = countText
@@ -140,6 +143,12 @@ function renderOrderDashboardSidebarBadge(count: number | null) {
140143
}
141144
}
142145

146+
function normalizeOrderDashboardSidebarBadgeCount(
147+
count: number | null | undefined
148+
) {
149+
return typeof count === "number" && Number.isFinite(count) ? count : null
150+
}
151+
143152
function removeOrderDashboardSidebarBadge() {
144153
document.getElementById(ORDER_DASHBOARD_SIDEBAR_BADGE_ID)?.remove()
145154
}

0 commit comments

Comments
 (0)