fix(dashboard): show correct color indicators for payment and fulfillment status columns for view_configuration feature flag (#14215)

## Summary

**What** — What changes are introduced in this PR?

Show correct color indicator for payment and fulfillment status columns when `view_configuration` feature flag is enabled on order data table.

**Why** — Why are these changes relevant or necessary?  

For non canceled status, grey default indicator was shown, as these columns weren't handled with their dedicated helper functions.

**How** — How have these changes been implemented?

Updated `StatusRenderer` to resolve the label and color for these columns with their helper functions, just like we do for the normal order table.

**Testing** — How have these changes been tested, or how can the reviewer test the feature?

Validated Admin UI shows correct color indicators when `view_configuration` is enabled.

---

## Examples

Provide examples or code snippets that demonstrate how this feature works, or how it can be used in practice.  
This helps with documentation and ensures maintainers can quickly understand and verify the change.

```ts
// Example usage
```

---

## Checklist

Please ensure the following before requesting a review:

- [x] I have added a **changeset** for this PR
    - Every non-breaking change should be marked as a **patch**
    - To add a changeset, run `yarn changeset` and follow the prompts
- [ ] The changes are covered by relevant **tests**
- [x] I have verified the code works as intended locally
- [x] I have linked the related issue(s) if applicable

---

## Additional Context

Add any additional context, related issues, or references that might help the reviewer understand this PR.

closes CORE-1309
This commit is contained in:
Nicolas Gorga authored and GitHub committed 2025-12-05 11:59:34 +00:00
1 parent 24c469d217
commit 3e3e6c37bd
2 files changed
+107 -83

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/dashboard": patch
---
fix(dashboard): show correct color indicators for payment and fulfillment status columns for `view_configuration` feature flag
@@ -13,7 +13,10 @@ import { DisplayIdCell } from "../../components/table/table-cells/order/display-
import { TotalCell } from "../../components/table/table-cells/order/total-cell" import { TotalCell } from "../../components/table/table-cells/order/total-cell"
import { MoneyAmountCell } from "../../components/table/table-cells/common/money-amount-cell" import { MoneyAmountCell } from "../../components/table/table-cells/common/money-amount-cell"
import { TFunction } from "i18next" import { TFunction } from "i18next"
import { toCamelCase } from "../common" import {
getOrderPaymentStatus,
getOrderFulfillmentStatus,
} from "../order-helpers"
export type CellRenderer<TData = any> = ( export type CellRenderer<TData = any> = (
value: any, value: any,
@@ -27,96 +30,104 @@ export type RendererRegistry = Map<string, CellRenderer>
const cellRenderers: RendererRegistry = new Map() const cellRenderers: RendererRegistry = new Map()
const getNestedValue = (obj: any, path: string) => { const getNestedValue = (obj: any, path: string) => {
return path.split('.').reduce((current, key) => current?.[key], obj) return path.split(".").reduce((current, key) => current?.[key], obj)
} }
const TextRenderer: CellRenderer = (value, _row, _column, _t) => { const TextRenderer: CellRenderer = (value, _row, _column, _t) => {
if (value === null || value === undefined) return '-' if (value === null || value === undefined) return "-"
return String(value) return String(value)
} }
const CountRenderer: CellRenderer = (value, _row, _column, t) => { const CountRenderer: CellRenderer = (value, _row, _column, t) => {
const items = value || [] const items = value || []
const count = Array.isArray(items) ? items.length : 0 const count = Array.isArray(items) ? items.length : 0
return t('general.items', { count }) return t("general.items", { count })
} }
const StatusRenderer: CellRenderer = (value, row, column, t) => { const StatusRenderer: CellRenderer = (value, row, column, t) => {
if (!value) return '-' if (!value) return "-"
if (column.field === 'status' && row.status && (row.handle || row.is_giftcard !== undefined)) { if (
column.field === "status" &&
row.status &&
(row.handle || row.is_giftcard !== undefined)
) {
return <ProductStatusCell status={row.status} /> return <ProductStatusCell status={row.status} />
} }
// Generic status badge if (column.context === "payment" && t) {
const { label, color } = getOrderPaymentStatus(t, value)
return <StatusBadge color={color}>{label}</StatusBadge>
}
if (column.context === "fulfillment" && t) {
const { label, color } = getOrderFulfillmentStatus(t, value)
return <StatusBadge color={color}>{label}</StatusBadge>
}
// Generic status badge for other status types
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
switch (status?.toLowerCase()) { switch (status?.toLowerCase()) {
case 'active': case "active":
case 'published': case "published":
case 'fulfilled': case "fulfilled":
case 'paid': case "paid":
return 'green' return "green"
case 'pending': case "pending":
case 'proposed': case "proposed":
case 'processing': case "processing":
return 'orange' return "orange"
case 'draft': case "draft":
return 'grey' return "grey"
case 'rejected': case "rejected":
case 'failed': case "failed":
case 'canceled': case "canceled":
return 'red' return "red"
default: default:
return 'grey' return "grey"
} }
} }
// Use existing translation keys where available // Use existing translation keys where available
const getTranslatedStatus = (status: string, column: HttpTypes.AdminColumn): string => { const getTranslatedStatus = (status: string): string => {
if (!t) return status if (!t) return status
const lowerStatus = status.toLowerCase() const lowerStatus = status.toLowerCase()
const camelCaseStatus = toCamelCase(lowerStatus)
switch (lowerStatus) { switch (lowerStatus) {
case 'active': case "active":
return t('general.active', 'Active') as string return t("general.active", "Active") as string
case 'published': case "published":
return t('products.productStatus.published', 'Published') as string return t("products.productStatus.published", "Published") as string
case 'draft': case "draft":
return t('orders.status.draft', 'Draft') as string return t("orders.status.draft", "Draft") as string
case 'pending': case "pending":
return t('orders.status.pending', 'Pending') as string return t("orders.status.pending", "Pending") as string
case 'canceled': case "canceled":
return t('orders.status.canceled', 'Canceled') as string return t("orders.status.canceled", "Canceled") as string
default: default:
if (column.context === 'payment') {
return t(`orders.payment.status.${camelCaseStatus}`, status) as string
}
if (column.context === 'fulfillment') {
return t(`orders.fulfillment.status.${camelCaseStatus}`, status) as string
}
// Try generic status translation with fallback // Try generic status translation with fallback
return t(`status.${lowerStatus}`, status) as string return t(`status.${lowerStatus}`, status) as string
} }
} }
const translatedValue = getTranslatedStatus(value, column) const translatedValue = getTranslatedStatus(value)
return ( return (
<StatusBadge color={getStatusColor(value)}> <StatusBadge color={getStatusColor(value)}>{translatedValue}</StatusBadge>
{translatedValue}
</StatusBadge>
) )
} }
const BadgeListRenderer: CellRenderer = (value, row, column, t) => { const BadgeListRenderer: CellRenderer = (value, row, column, t) => {
// For sales channels // For sales channels
if (column.field === 'sales_channels_display' || column.field === 'sales_channels') { if (
column.field === "sales_channels_display" ||
column.field === "sales_channels"
) {
return <SalesChannelsCell salesChannels={row.sales_channels} /> return <SalesChannelsCell salesChannels={row.sales_channels} />
} }
// Generic badge list // Generic badge list
if (!Array.isArray(value)) return '-' if (!Array.isArray(value)) return "-"
const items = value.slice(0, 2) const items = value.slice(0, 2)
const remaining = value.length - 2 const remaining = value.length - 2
@@ -125,12 +136,16 @@ const BadgeListRenderer: CellRenderer = (value, row, column, t) => {
<div className="flex gap-1"> <div className="flex gap-1">
{items.map((item, index) => ( {items.map((item, index) => (
<Badge key={index} size="xsmall"> <Badge key={index} size="xsmall">
{typeof item === 'string' ? item : item.name || item.title || '-'} {typeof item === "string" ? item : item.name || item.title || "-"}
</Badge> </Badge>
))} ))}
{remaining > 0 && ( {remaining > 0 && (
<Badge size="xsmall" color="grey"> <Badge size="xsmall" color="grey">
{t ? t('general.plusCountMore', '+ {{count}} more', { count: remaining }) : `+${remaining}`} {t
? t("general.plusCountMore", "+ {{count}} more", {
count: remaining,
})
: `+${remaining}`}
</Badge> </Badge>
)} )}
</div> </div>
@@ -152,7 +167,9 @@ const VariantsRenderer: CellRenderer = (_, row, _column, _t) => {
// Order-specific renderers // Order-specific renderers
const CustomerNameRenderer: CellRenderer = (_, row, _column, t) => { const CustomerNameRenderer: CellRenderer = (_, row, _column, t) => {
if (row.customer?.first_name || row.customer?.last_name) { if (row.customer?.first_name || row.customer?.last_name) {
const fullName = `${row.customer.first_name || ''} ${row.customer.last_name || ''}`.trim() const fullName = `${row.customer.first_name || ""} ${
row.customer.last_name || ""
}`.trim()
if (fullName) return fullName if (fullName) return fullName
} }
@@ -166,20 +183,20 @@ const CustomerNameRenderer: CellRenderer = (_, row, _column, t) => {
return row.customer.phone return row.customer.phone
} }
return t ? t('customers.guest', 'Guest') : 'Guest' return t ? t("customers.guest", "Guest") : "Guest"
} }
const AddressSummaryRenderer: CellRenderer = (_, row, column, _t) => { const AddressSummaryRenderer: CellRenderer = (_, row, column, _t) => {
let address = null let address = null
if (column.field === 'shipping_address_display') { if (column.field === "shipping_address_display") {
address = row.shipping_address address = row.shipping_address
} else if (column.field === 'billing_address_display') { } else if (column.field === "billing_address_display") {
address = row.billing_address address = row.billing_address
} else { } else {
address = row.shipping_address || row.billing_address address = row.shipping_address || row.billing_address
} }
if (!address) return '-' if (!address) return "-"
const parts = [] const parts = []
@@ -193,14 +210,14 @@ const AddressSummaryRenderer: CellRenderer = (_, row, column, _t) => {
if (address.postal_code) locationParts.push(address.postal_code) if (address.postal_code) locationParts.push(address.postal_code)
if (locationParts.length > 0) { if (locationParts.length > 0) {
parts.push(locationParts.join(', ')) parts.push(locationParts.join(", "))
} }
if (address.country_code) { if (address.country_code) {
parts.push(address.country_code.toUpperCase()) parts.push(address.country_code.toUpperCase())
} }
return parts.join(' • ') || '-' return parts.join(" • ") || "-"
} }
const CountryCodeRenderer: CellRenderer = (_, row, _column, _t) => { const CountryCodeRenderer: CellRenderer = (_, row, _column, _t) => {
@@ -239,36 +256,38 @@ const DisplayIdRenderer: CellRenderer = (value, _row, _column, _t) => {
} }
const CurrencyRenderer: CellRenderer = (value, row, _column, _t) => { const CurrencyRenderer: CellRenderer = (value, row, _column, _t) => {
const currencyCode = row.currency_code || 'USD' const currencyCode = row.currency_code || "USD"
return <MoneyAmountCell currencyCode={currencyCode} amount={value} align="right" /> return (
<MoneyAmountCell currencyCode={currencyCode} amount={value} align="right" />
)
} }
const TotalRenderer: CellRenderer = (value, row, _column, _t) => { const TotalRenderer: CellRenderer = (value, row, _column, _t) => {
const currencyCode = row.currency_code || 'USD' const currencyCode = row.currency_code || "USD"
return <TotalCell currencyCode={currencyCode} total={value} /> return <TotalCell currencyCode={currencyCode} total={value} />
} }
// Register built-in renderers // Register built-in renderers
cellRenderers.set('text', TextRenderer) cellRenderers.set("text", TextRenderer)
cellRenderers.set('count', CountRenderer) cellRenderers.set("count", CountRenderer)
cellRenderers.set('status', StatusRenderer) cellRenderers.set("status", StatusRenderer)
cellRenderers.set('badge_list', BadgeListRenderer) cellRenderers.set("badge_list", BadgeListRenderer)
cellRenderers.set('date', DateRenderer) cellRenderers.set("date", DateRenderer)
cellRenderers.set('timestamp', DateRenderer) cellRenderers.set("timestamp", DateRenderer)
cellRenderers.set('currency', CurrencyRenderer) cellRenderers.set("currency", CurrencyRenderer)
cellRenderers.set('total', TotalRenderer) cellRenderers.set("total", TotalRenderer)
// Register product-specific renderers // Register product-specific renderers
cellRenderers.set('product_info', ProductInfoRenderer) cellRenderers.set("product_info", ProductInfoRenderer)
cellRenderers.set('collection', CollectionRenderer) cellRenderers.set("collection", CollectionRenderer)
cellRenderers.set('variants', VariantsRenderer) cellRenderers.set("variants", VariantsRenderer)
cellRenderers.set('sales_channels_list', BadgeListRenderer) cellRenderers.set("sales_channels_list", BadgeListRenderer)
// Register order-specific renderers // Register order-specific renderers
cellRenderers.set('customer_name', CustomerNameRenderer) cellRenderers.set("customer_name", CustomerNameRenderer)
cellRenderers.set('address_summary', AddressSummaryRenderer) cellRenderers.set("address_summary", AddressSummaryRenderer)
cellRenderers.set('country_code', CountryCodeRenderer) cellRenderers.set("country_code", CountryCodeRenderer)
cellRenderers.set('display_id', DisplayIdRenderer) cellRenderers.set("display_id", DisplayIdRenderer)
export function getCellRenderer( export function getCellRenderer(
renderType?: string, renderType?: string,
@@ -279,21 +298,21 @@ export function getCellRenderer(
} }
switch (dataType) { switch (dataType) {
case 'number': case "number":
case 'string': case "string":
return TextRenderer return TextRenderer
case 'date': case "date":
return DateRenderer return DateRenderer
case 'boolean': case "boolean":
return (value, _row, _column, t) => { return (value, _row, _column, t) => {
if (t) { if (t) {
return value ? t('fields.yes', 'Yes') : t('fields.no', 'No') return value ? t("fields.yes", "Yes") : t("fields.no", "No")
} }
return value ? 'Yes' : 'No' return value ? "Yes" : "No"
} }
case 'enum': case "enum":
return StatusRenderer return StatusRenderer
case 'currency': case "currency":
return CurrencyRenderer return CurrencyRenderer
default: default:
return TextRenderer return TextRenderer