import { Badge, StatusBadge, Tooltip } from "@medusajs/ui"
import ReactCountryFlag from "react-country-flag"
import { getCountryByIso2 } from "./data/countries"
import { getStylizedAmount } from "./money-amount-helpers"
// Helper function to get nested value from object using dot notation
const getNestedValue = (obj: any, path: string) => {
return path.split(".").reduce((current, key) => current?.[key], obj)
}
// Helper function to format date
const formatDate = (
date: string | Date,
format: "short" | "long" | "relative" = "short"
) => {
const dateObj = new Date(date)
switch (format) {
case "short":
return dateObj.toLocaleDateString("en-GB", {
day: "numeric",
month: "short",
year: "numeric",
})
case "long":
return dateObj.toLocaleDateString("en-GB", {
day: "numeric",
month: "long",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
})
case "relative": {
const now = new Date()
const diffInMs = now.getTime() - dateObj.getTime()
const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24))
if (diffInDays === 0) {
return "Today"
}
if (diffInDays === 1) {
return "Yesterday"
}
if (diffInDays < 7) {
return `${diffInDays} days ago`
}
return dateObj.toLocaleDateString("en-GB", {
day: "numeric",
month: "short",
})
}
default:
return dateObj.toLocaleDateString()
}
}
// Payment status display
const PaymentStatusBadge = ({ status }: { status: string }) => {
const getStatusColor = (status: string) => {
switch (status?.toLowerCase()) {
case "paid":
case "captured":
return "green"
case "pending":
case "awaiting":
return "orange"
case "failed":
case "canceled":
return "red"
default:
return "grey"
}
}
return {status}
}
// Fulfillment status display
const FulfillmentStatusBadge = ({ status }: { status: string }) => {
const getStatusColor = (status: string) => {
switch (status?.toLowerCase()) {
case "fulfilled":
case "shipped":
return "green"
case "partially_fulfilled":
case "preparing":
return "orange"
case "canceled":
case "returned":
return "red"
case "pending":
case "not_fulfilled":
return "grey"
default:
return "grey"
}
}
return {status}
}
// Generic status badge
const GenericStatusBadge = ({ status }: { status: string }) => {
return (
{status}
)
}
// Display strategies registry
export const DISPLAY_STRATEGIES = {
// Known semantic types with pixel-perfect display
status: {
payment: (value: any) => ,
fulfillment: (value: any) => ,
default: (value: any) => ,
},
currency: {
default: (value: any, row: any) => {
if (value === null || value === undefined) {
return "-"
}
const currencyCode = row.currency_code || "USD"
const formatted = getStylizedAmount(value, currencyCode)
return (
{formatted}
)
},
},
timestamp: {
creation: (value: any) => {
return value ? formatDate(value, "short") : "-"
},
update: (value: any) => {
return value ? formatDate(value, "relative") : "-"
},
default: (value: any) => {
return value ? formatDate(value, "short") : "-"
},
},
identifier: {
order: (value: any) => `#${value}`,
default: (value: any) => value,
},
email: {
default: (value: any) => value || "-",
},
// Generic fallbacks for custom fields
enum: {
default: (value: any) => ,
},
// Base type fallbacks
string: {
default: (value: any) => value || "-",
},
number: {
default: (value: any) => value?.toLocaleString() || "0",
},
boolean: {
default: (value: any) => (
{value ? "Yes" : "No"}
),
},
object: {
relationship: (value: any) => {
if (!value || typeof value !== "object") {
return "-"
}
// Try common display fields
if (value.name) {
return value.name
}
if (value.title) {
return value.title
}
if (value.email) {
return value.email
}
if (value.display_name) {
return value.display_name
}
return JSON.stringify(value)
},
default: (value: any) => {
if (!value || typeof value !== "object") {
return "-"
}
// Try common display fields
if (value.name) {
return value.name
}
if (value.title) {
return value.title
}
if (value.email) {
return value.email
}
return JSON.stringify(value)
},
},
// Date types (in addition to timestamp)
date: {
default: (value: any) => {
return value ? formatDate(value, "short") : "-"
},
},
datetime: {
default: (value: any) => {
return value ? formatDate(value, "long") : "-"
},
},
// Computed columns
computed: {
display: (value: any) => value || "-",
default: (value: any) => value || "-",
},
}
// Strategy selection function
export const getDisplayStrategy = (column: any) => {
const semanticStrategies =
DISPLAY_STRATEGIES[column.semantic_type as keyof typeof DISPLAY_STRATEGIES]
if (semanticStrategies) {
const contextStrategy =
semanticStrategies[column.context as keyof typeof semanticStrategies]
if (contextStrategy) {
return contextStrategy
}
const defaultStrategy = semanticStrategies.default
if (defaultStrategy) {
return defaultStrategy
}
}
// Fallback to data type
// Map 'text' data type to 'string' strategy
const dataType = column.data_type === "text" ? "string" : column.data_type
const dataTypeStrategies =
DISPLAY_STRATEGIES[dataType as keyof typeof DISPLAY_STRATEGIES]
if (dataTypeStrategies) {
const defaultStrategy = dataTypeStrategies.default
if (defaultStrategy) {
return defaultStrategy
}
}
// Final fallback
return (value: any) => String(value || "-")
}
// Computed column computation functions
export const COMPUTED_COLUMN_FUNCTIONS = {
customer_name: (row: any) => {
// Try customer object first
if (row.customer?.first_name || row.customer?.last_name) {
const fullName = `${row.customer.first_name || ""} ${
row.customer.last_name || ""
}`.trim()
if (fullName) {
return fullName
}
}
// Fall back to email
if (row.customer?.email) {
return row.customer.email
}
// Fall back to phone
if (row.customer?.phone) {
return row.customer.phone
}
return "Guest"
},
address_summary: (row: any, column?: any) => {
// Determine which address to use based on the column field
let address: Record | null = null
if (column?.field === "shipping_address_display") {
address = row.shipping_address
} else if (column?.field === "billing_address_display") {
address = row.billing_address
} else {
// Fallback to shipping address if no specific field
address = row.shipping_address || row.billing_address
}
if (!address) {
return "-"
}
// Build address parts in a meaningful order
const parts: string[] = []
// Include street address if available
if (address.address_1) {
parts.push(address.address_1)
}
// City, Province/State, Postal Code
const locationParts: string[] = []
if (address.city) {
locationParts.push(address.city)
}
if (address.province) {
locationParts.push(address.province)
}
if (address.postal_code) {
locationParts.push(address.postal_code)
}
if (locationParts.length > 0) {
parts.push(locationParts.join(", "))
}
// Country
if (address.country_code) {
parts.push(address.country_code.toUpperCase())
}
return parts.join(" • ") || "-"
},
country_code: (row: any) => {
// Get country code from shipping address
const countryCode = row.shipping_address?.country_code
if (!countryCode) {
return -
}
// Get country information
const country = getCountryByIso2(countryCode)
const displayName = country?.display_name || countryCode.toUpperCase()
// Display country flag with tooltip - centered in the cell
return (
)
},
}
// Entity-specific column overrides
export const ENTITY_COLUMN_OVERRIDES = {
orders: {
// Override for customer column that combines multiple fields
customer: {
accessor: (row: any) => {
// Complex logic for combining fields
const shipping = row.shipping_address
const customer = row.customer
if (shipping?.first_name || shipping?.last_name) {
return `${shipping.first_name || ""} ${
shipping.last_name || ""
}`.trim()
}
if (customer?.first_name || customer?.last_name) {
return `${customer.first_name || ""} ${
customer.last_name || ""
}`.trim()
}
return customer?.email || "Guest"
},
},
},
}
// Helper function to get entity-specific accessor
export const getEntityAccessor = (
entity: string,
fieldName: string,
column?: any
) => {
// Check if this is a computed column
if (column?.computed) {
const computationFn =
COMPUTED_COLUMN_FUNCTIONS[
column.computed.type as keyof typeof COMPUTED_COLUMN_FUNCTIONS
]
if (computationFn) {
// Return a wrapper function that passes the column info
return (row: any) => computationFn(row, column)
}
}
const entityOverrides =
ENTITY_COLUMN_OVERRIDES[entity as keyof typeof ENTITY_COLUMN_OVERRIDES]
if (entityOverrides) {
const fieldOverride =
entityOverrides[fieldName as keyof typeof entityOverrides]
if (fieldOverride?.accessor) {
return fieldOverride.accessor
}
}
// Default accessor using dot notation
return (row: any) => getNestedValue(row, fieldName)
}