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
2025-12-05 08:59:34 -03:00
committed by GitHub
parent 24c469d217
commit 3e3e6c37bd
2 changed files with 107 additions and 83 deletions

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

View File

@@ -13,7 +13,10 @@ import { DisplayIdCell } from "../../components/table/table-cells/order/display-
import { TotalCell } from "../../components/table/table-cells/order/total-cell"
import { MoneyAmountCell } from "../../components/table/table-cells/common/money-amount-cell"
import { TFunction } from "i18next"
import { toCamelCase } from "../common"
import {
getOrderPaymentStatus,
getOrderFulfillmentStatus,
} from "../order-helpers"
export type CellRenderer<TData = any> = (
value: any,
@@ -27,96 +30,104 @@ export type RendererRegistry = Map<string, CellRenderer>
const cellRenderers: RendererRegistry = new Map()
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) => {
if (value === null || value === undefined) return '-'
if (value === null || value === undefined) return "-"
return String(value)
}
const CountRenderer: CellRenderer = (value, _row, _column, t) => {
const items = value || []
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) => {
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} />
}
// 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) => {
switch (status?.toLowerCase()) {
case 'active':
case 'published':
case 'fulfilled':
case 'paid':
return 'green'
case 'pending':
case 'proposed':
case 'processing':
return 'orange'
case 'draft':
return 'grey'
case 'rejected':
case 'failed':
case 'canceled':
return 'red'
case "active":
case "published":
case "fulfilled":
case "paid":
return "green"
case "pending":
case "proposed":
case "processing":
return "orange"
case "draft":
return "grey"
case "rejected":
case "failed":
case "canceled":
return "red"
default:
return 'grey'
return "grey"
}
}
// Use existing translation keys where available
const getTranslatedStatus = (status: string, column: HttpTypes.AdminColumn): string => {
const getTranslatedStatus = (status: string): string => {
if (!t) return status
const lowerStatus = status.toLowerCase()
const camelCaseStatus = toCamelCase(lowerStatus)
switch (lowerStatus) {
case 'active':
return t('general.active', 'Active') as string
case 'published':
return t('products.productStatus.published', 'Published') as string
case 'draft':
return t('orders.status.draft', 'Draft') as string
case 'pending':
return t('orders.status.pending', 'Pending') as string
case 'canceled':
return t('orders.status.canceled', 'Canceled') as string
case "active":
return t("general.active", "Active") as string
case "published":
return t("products.productStatus.published", "Published") as string
case "draft":
return t("orders.status.draft", "Draft") as string
case "pending":
return t("orders.status.pending", "Pending") as string
case "canceled":
return t("orders.status.canceled", "Canceled") as string
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
return t(`status.${lowerStatus}`, status) as string
}
}
const translatedValue = getTranslatedStatus(value, column)
const translatedValue = getTranslatedStatus(value)
return (
<StatusBadge color={getStatusColor(value)}>
{translatedValue}
</StatusBadge>
<StatusBadge color={getStatusColor(value)}>{translatedValue}</StatusBadge>
)
}
const BadgeListRenderer: CellRenderer = (value, row, column, t) => {
// 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} />
}
// Generic badge list
if (!Array.isArray(value)) return '-'
if (!Array.isArray(value)) return "-"
const items = value.slice(0, 2)
const remaining = value.length - 2
@@ -125,12 +136,16 @@ const BadgeListRenderer: CellRenderer = (value, row, column, t) => {
<div className="flex gap-1">
{items.map((item, index) => (
<Badge key={index} size="xsmall">
{typeof item === 'string' ? item : item.name || item.title || '-'}
{typeof item === "string" ? item : item.name || item.title || "-"}
</Badge>
))}
{remaining > 0 && (
<Badge size="xsmall" color="grey">
{t ? t('general.plusCountMore', '+ {{count}} more', { count: remaining }) : `+${remaining}`}
{t
? t("general.plusCountMore", "+ {{count}} more", {
count: remaining,
})
: `+${remaining}`}
</Badge>
)}
</div>
@@ -152,7 +167,9 @@ const VariantsRenderer: CellRenderer = (_, row, _column, _t) => {
// Order-specific renderers
const CustomerNameRenderer: CellRenderer = (_, row, _column, t) => {
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
}
@@ -166,20 +183,20 @@ const CustomerNameRenderer: CellRenderer = (_, row, _column, t) => {
return row.customer.phone
}
return t ? t('customers.guest', 'Guest') : 'Guest'
return t ? t("customers.guest", "Guest") : "Guest"
}
const AddressSummaryRenderer: CellRenderer = (_, row, column, _t) => {
let address = null
if (column.field === 'shipping_address_display') {
if (column.field === "shipping_address_display") {
address = row.shipping_address
} else if (column.field === 'billing_address_display') {
} else if (column.field === "billing_address_display") {
address = row.billing_address
} else {
address = row.shipping_address || row.billing_address
}
if (!address) return '-'
if (!address) return "-"
const parts = []
@@ -193,14 +210,14 @@ const AddressSummaryRenderer: CellRenderer = (_, row, column, _t) => {
if (address.postal_code) locationParts.push(address.postal_code)
if (locationParts.length > 0) {
parts.push(locationParts.join(', '))
parts.push(locationParts.join(", "))
}
if (address.country_code) {
parts.push(address.country_code.toUpperCase())
}
return parts.join('') || '-'
return parts.join("") || "-"
}
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 currencyCode = row.currency_code || 'USD'
return <MoneyAmountCell currencyCode={currencyCode} amount={value} align="right" />
const currencyCode = row.currency_code || "USD"
return (
<MoneyAmountCell currencyCode={currencyCode} amount={value} align="right" />
)
}
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} />
}
// Register built-in renderers
cellRenderers.set('text', TextRenderer)
cellRenderers.set('count', CountRenderer)
cellRenderers.set('status', StatusRenderer)
cellRenderers.set('badge_list', BadgeListRenderer)
cellRenderers.set('date', DateRenderer)
cellRenderers.set('timestamp', DateRenderer)
cellRenderers.set('currency', CurrencyRenderer)
cellRenderers.set('total', TotalRenderer)
cellRenderers.set("text", TextRenderer)
cellRenderers.set("count", CountRenderer)
cellRenderers.set("status", StatusRenderer)
cellRenderers.set("badge_list", BadgeListRenderer)
cellRenderers.set("date", DateRenderer)
cellRenderers.set("timestamp", DateRenderer)
cellRenderers.set("currency", CurrencyRenderer)
cellRenderers.set("total", TotalRenderer)
// Register product-specific renderers
cellRenderers.set('product_info', ProductInfoRenderer)
cellRenderers.set('collection', CollectionRenderer)
cellRenderers.set('variants', VariantsRenderer)
cellRenderers.set('sales_channels_list', BadgeListRenderer)
cellRenderers.set("product_info", ProductInfoRenderer)
cellRenderers.set("collection", CollectionRenderer)
cellRenderers.set("variants", VariantsRenderer)
cellRenderers.set("sales_channels_list", BadgeListRenderer)
// Register order-specific renderers
cellRenderers.set('customer_name', CustomerNameRenderer)
cellRenderers.set('address_summary', AddressSummaryRenderer)
cellRenderers.set('country_code', CountryCodeRenderer)
cellRenderers.set('display_id', DisplayIdRenderer)
cellRenderers.set("customer_name", CustomerNameRenderer)
cellRenderers.set("address_summary", AddressSummaryRenderer)
cellRenderers.set("country_code", CountryCodeRenderer)
cellRenderers.set("display_id", DisplayIdRenderer)
export function getCellRenderer(
renderType?: string,
@@ -279,21 +298,21 @@ export function getCellRenderer(
}
switch (dataType) {
case 'number':
case 'string':
case "number":
case "string":
return TextRenderer
case 'date':
case "date":
return DateRenderer
case 'boolean':
case "boolean":
return (value, _row, _column, 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
case 'currency':
case "currency":
return CurrencyRenderer
default:
return TextRenderer