Misc product fixes

This commit is contained in:
2025-06-22 15:52:16 -04:00
parent fcfe7e2fab
commit 38f6688f10
5 changed files with 139 additions and 76 deletions

View File

@@ -74,10 +74,12 @@ const BASE_FILTER_OPTIONS: FilterOption[] = [
{ id: 'isReplenishable', label: 'Replenishable', type: 'boolean', group: 'Basic Info', operators: BOOLEAN_OPERATORS },
{ id: 'abcClass', label: 'ABC Class', type: 'select', group: 'Basic Info', operators: SELECT_OPERATORS, options: [] },
{ id: 'status', label: 'Status', type: 'select', group: 'Basic Info', operators: SELECT_OPERATORS, options: [
{ value: 'in_stock', label: 'In Stock' },
{ value: 'low_stock', label: 'Low Stock' },
{ value: 'out_of_stock', label: 'Out of Stock' },
{ value: 'discontinued', label: 'Discontinued' },
{ value: 'Critical', label: 'Critical' },
{ value: 'At Risk', label: 'At Risk' },
{ value: 'Reorder', label: 'Reorder' },
{ value: 'Overstocked', label: 'Overstocked' },
{ value: 'Healthy', label: 'Healthy' },
{ value: 'New', label: 'New' },
]},
{ id: 'dateCreated', label: 'Created Date', type: 'date', group: 'Basic Info', operators: DATE_OPERATORS },
@@ -91,6 +93,9 @@ const BASE_FILTER_OPTIONS: FilterOption[] = [
// Physical Properties group
{ id: 'weight', label: 'Weight', type: 'number', group: 'Physical', operators: NUMBER_OPERATORS },
{ id: 'length', label: 'Length', type: 'number', group: 'Physical', operators: NUMBER_OPERATORS },
{ id: 'width', label: 'Width', type: 'number', group: 'Physical', operators: NUMBER_OPERATORS },
{ id: 'height', label: 'Height', type: 'number', group: 'Physical', operators: NUMBER_OPERATORS },
{ id: 'dimensions', label: 'Dimensions', type: 'text', group: 'Physical', operators: STRING_OPERATORS },
// Customer Engagement group
@@ -99,18 +104,24 @@ const BASE_FILTER_OPTIONS: FilterOption[] = [
{ id: 'baskets', label: 'Basket Adds', type: 'number', group: 'Customer', operators: NUMBER_OPERATORS },
{ id: 'notifies', label: 'Stock Alerts', type: 'number', group: 'Customer', operators: NUMBER_OPERATORS },
// Stock group
{ id: 'currentStock', label: 'Current Stock', type: 'number', group: 'Stock', operators: NUMBER_OPERATORS },
{ id: 'preorderCount', label: 'Preorders', type: 'number', group: 'Stock', operators: NUMBER_OPERATORS },
{ id: 'notionsInvCount', label: 'Notions Inventory', type: 'number', group: 'Stock', operators: NUMBER_OPERATORS },
{ id: 'onOrderQty', label: 'On Order', type: 'number', group: 'Stock', operators: NUMBER_OPERATORS },
{ id: 'configSafetyStock', label: 'Safety Stock', type: 'number', group: 'Stock', operators: NUMBER_OPERATORS },
{ id: 'replenishmentUnits', label: 'Replenish Qty', type: 'number', group: 'Stock', operators: NUMBER_OPERATORS },
{ id: 'toOrderUnits', label: 'To Order', type: 'number', group: 'Stock', operators: NUMBER_OPERATORS },
{ id: 'stockCoverInDays', label: 'Stock Cover (Days)', type: 'number', group: 'Stock', operators: NUMBER_OPERATORS },
{ id: 'sellsOutInDays', label: 'Sells Out In (Days)', type: 'number', group: 'Stock', operators: NUMBER_OPERATORS },
{ id: 'isOldStock', label: 'Old Stock', type: 'boolean', group: 'Stock', operators: BOOLEAN_OPERATORS },
{ id: 'overstockedUnits', label: 'Overstock Qty', type: 'number', group: 'Stock', operators: NUMBER_OPERATORS },
// Inventory & Stock group
{ id: 'currentStock', label: 'Current Stock', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'preorderCount', label: 'Preorders', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'notionsInvCount', label: 'Notions Inventory', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'onOrderQty', label: 'On Order', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'configSafetyStock', label: 'Safety Stock', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'replenishmentUnits', label: 'Replenish Qty', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'toOrderUnits', label: 'To Order', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'stockCoverInDays', label: 'Stock Cover (Days)', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'sellsOutInDays', label: 'Sells Out In (Days)', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'isOldStock', label: 'Old Stock', type: 'boolean', group: 'Inventory', operators: BOOLEAN_OPERATORS },
{ id: 'overstockedUnits', label: 'Overstock Qty', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'stockoutDays30d', label: 'Stockout Days (30d)', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'stockoutRate30d', label: 'Stockout Rate %', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'avgStockUnits30d', label: 'Avg Stock Units (30d)', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'receivedQty30d', label: 'Received Qty (30d)', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'poCoverInDays', label: 'PO Cover (Days)', type: 'number', group: 'Inventory', operators: NUMBER_OPERATORS },
{ id: 'earliestExpectedDate', label: 'Expected Date', type: 'date', group: 'Inventory', operators: DATE_OPERATORS },
// Pricing Group
{ id: "currentPrice", label: "Current Price", type: "number", group: "Pricing", operators: ["=", ">", ">=", "<", "<=", "between"] },
@@ -119,6 +130,9 @@ const BASE_FILTER_OPTIONS: FilterOption[] = [
{ id: "currentLandingCostPrice", label: "Landing Cost", type: "number", group: "Pricing", operators: ["=", ">", ">=", "<", "<=", "between"] },
// Valuation Group
{ id: "currentStockCost", label: "Current Stock Cost", type: "number", group: "Valuation", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "currentStockRetail", label: "Current Stock Retail", type: "number", group: "Valuation", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "currentStockGross", label: "Current Stock Gross", type: "number", group: "Valuation", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "avgStockCost30d", label: "Avg Stock Cost (30d)", type: "number", group: "Valuation", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "avgStockRetail30d", label: "Avg Stock Retail (30d)", type: "number", group: "Valuation", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "avgStockGross30d", label: "Avg Stock Gross (30d)", type: "number", group: "Valuation", operators: ["=", ">", ">=", "<", "<=", "between"] },
@@ -132,6 +146,8 @@ const BASE_FILTER_OPTIONS: FilterOption[] = [
{ id: "overstockedRetail", label: "Overstock Retail", type: "number", group: "Valuation", operators: ["=", ">", ">=", "<", "<=", "between"] },
// Sales Metrics Group
{ id: "salesVelocityDaily", label: "Daily Velocity", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "yesterdaySales", label: "Yesterday Sales", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "sales7d", label: "Sales (7d)", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "revenue7d", label: "Revenue (7d)", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "sales14d", label: "Sales (14d)", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
@@ -140,9 +156,7 @@ const BASE_FILTER_OPTIONS: FilterOption[] = [
{ id: "revenue30d", label: "Revenue (30d)", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "sales365d", label: "Sales (365d)", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "revenue365d", label: "Revenue (365d)", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "salesVelocityDaily", label: "Daily Velocity", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "dateLastSold", label: "Date Last Sold", type: "date", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "yesterdaySales", label: "Sales (Yesterday)", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "avgSalesPerDay30d", label: "Avg Sales/Day (30d)", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "avgSalesPerMonth30d", label: "Avg Sales/Month (30d)", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "returnsUnits30d", label: "Returns Units (30d)", type: "number", group: "Sales Metrics", operators: ["=", ">", ">=", "<", "<=", "between"] },
@@ -188,20 +202,45 @@ const BASE_FILTER_OPTIONS: FilterOption[] = [
{ id: "replenishmentNeededRaw", label: "Replenishment Needed Raw", type: "number", group: "Forecasting", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "forecastLostSalesUnits", label: "Forecast Lost Sales Units", type: "number", group: "Forecasting", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "forecastLostRevenue", label: "Forecast Lost Revenue", type: "number", group: "Forecasting", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "stockoutDays30d", label: "Stockout Days (30d)", type: "number", group: "Stock", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "stockoutRate30d", label: "Stockout Rate %", type: "number", group: "Stock", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "avgStockUnits30d", label: "Avg Stock Units (30d)", type: "number", group: "Stock", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "receivedQty30d", label: "Received Qty (30d)", type: "number", group: "Stock", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "poCoverInDays", label: "PO Cover (Days)", type: "number", group: "Stock", operators: ["=", ">", ">=", "<", "<=", "between"] },
// Dates & Timing Group
{ id: "dateFirstReceived", label: "First Received", type: "date", group: "Dates", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "dateLastReceived", label: "Last Received", type: "date", group: "Dates", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "dateFirstSold", label: "First Sold", type: "date", group: "Dates", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "ageDays", label: "Age (Days)", type: "number", group: "Dates", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "avgLeadTimeDays", label: "Avg Lead Time", type: "number", group: "Dates", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "replenishDate", label: "Replenish Date", type: "date", group: "Dates", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "planningPeriodDays", label: "Planning Period (Days)", type: "number", group: "Dates", operators: ["=", ">", ">=", "<", "<=", "between"] },
// Lead Time & Replenishment
{ id: "configLeadTime", label: "Config Lead Time", type: "number", group: "Lead Time", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "configDaysOfStock", label: "Config Days of Stock", type: "number", group: "Lead Time", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "avgLeadTimeDays", label: "Avg Lead Time", type: "number", group: "Lead Time", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "earliestExpectedDate", label: "Next Arrival Date", type: "date", group: "Lead Time", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "dateLastReceived", label: "Date Last Received", type: "date", group: "Lead Time", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "dateFirstReceived", label: "First Received", type: "date", group: "Dates", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "dateFirstSold", label: "First Sold", type: "date", group: "Dates", operators: ["=", ">", ">=", "<", "<=", "between"] },
// Growth Analysis Group
{ id: "salesGrowth30dVsPrev", label: "Sales Growth % (30d vs Prev)", type: "number", group: "Growth Analysis", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "revenueGrowth30dVsPrev", label: "Revenue Growth % (30d vs Prev)", type: "number", group: "Growth Analysis", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "salesGrowthYoy", label: "Sales Growth % YoY", type: "number", group: "Growth Analysis", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "revenueGrowthYoy", label: "Revenue Growth % YoY", type: "number", group: "Growth Analysis", operators: ["=", ">", ">=", "<", "<=", "between"] },
// Demand Variability Group
{ id: "salesVariance30d", label: "Sales Variance (30d)", type: "number", group: "Demand Variability", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "salesStdDev30d", label: "Sales Std Dev (30d)", type: "number", group: "Demand Variability", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "salesCv30d", label: "Sales CV % (30d)", type: "number", group: "Demand Variability", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "demandPattern", label: "Demand Pattern", type: "text", group: "Demand Variability", operators: STRING_OPERATORS },
// Service Level Group
{ id: "fillRate30d", label: "Fill Rate % (30d)", type: "number", group: "Service Level", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "stockoutIncidents30d", label: "Stockout Incidents (30d)", type: "number", group: "Service Level", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "serviceLevel30d", label: "Service Level % (30d)", type: "number", group: "Service Level", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "lostSalesIncidents30d", label: "Lost Sales Incidents (30d)", type: "number", group: "Service Level", operators: ["=", ">", ">=", "<", "<=", "between"] },
// Seasonality Group
{ id: "seasonalityIndex", label: "Seasonality Index", type: "number", group: "Seasonality", operators: ["=", ">", ">=", "<", "<=", "between"] },
{ id: "seasonalPattern", label: "Seasonal Pattern", type: "text", group: "Seasonality", operators: STRING_OPERATORS },
{ id: "peakSeason", label: "Peak Season", type: "text", group: "Seasonality", operators: STRING_OPERATORS },
// Data Quality Group
{ id: "lifetimeRevenueQuality", label: "Lifetime Revenue Quality", type: "text", group: "Data Quality", operators: STRING_OPERATORS },
];
interface ProductFiltersProps {

View File

@@ -26,7 +26,7 @@ import {
useSortable,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { ProductMetric, ProductMetricColumnKey } from "@/types/products";
import { ProductMetric, ProductMetricColumnKey, ProductStatus } from "@/types/products";
import { Skeleton } from "@/components/ui/skeleton";
import { getStatusBadge } from "@/utils/productUtils";
@@ -166,19 +166,21 @@ export function ProductTable({
}
};
const formatColumnValue = (product: ProductMetric, columnKey: ProductMetricColumnKey) => {
const columnDef = columnDefs.find(def => def.key === columnKey);
const formatColumnValue = (product: ProductMetric, columnKey: ProductMetricColumnKey): React.ReactNode => {
const value = product[columnKey as keyof ProductMetric];
if (columnKey === 'status') {
return <div dangerouslySetInnerHTML={{ __html: getStatusBadge(product.status || 'Unknown') }} />;
}
const columnDef = columnDefs.find(col => col.key === columnKey);
// Use the format function from column definition if available
if (columnDef?.format) {
return columnDef.format(value, product);
}
// Default formatting for common types if no formatter provided
// Special handling for status
if (columnKey === 'status') {
return <div dangerouslySetInnerHTML={{ __html: getStatusBadge(value as ProductStatus) }} />;
}
// Special handling for boolean values
if (typeof value === 'boolean') {
return value ? 'Yes' : 'No';
}
@@ -308,4 +310,4 @@ export function ProductTable({
</div>
</DndContext>
);
}
}

View File

@@ -39,7 +39,7 @@ interface ColumnDef {
group: string;
noLabel?: boolean;
width?: string;
format?: (value: any) => string;
format?: (value: any, product?: ProductMetric) => React.ReactNode;
}
// Define available columns with their groups
@@ -70,7 +70,19 @@ const AVAILABLE_COLUMNS: ColumnDef[] = [
// Physical Properties
{ key: 'weight', label: 'Weight', group: 'Physical', format: (v) => v === 0 ? '0' : v ? v.toFixed(2) : '-' },
{ key: 'dimensions', label: 'Dimensions', group: 'Physical', format: (v) => v ? `${v.length}×${v.width}×${v.height}` : '-' },
{ key: 'length', label: 'Length', group: 'Physical', format: (v) => v === 0 ? '0' : v ? v.toFixed(2) : '-' },
{ key: 'width', label: 'Width', group: 'Physical', format: (v) => v === 0 ? '0' : v ? v.toFixed(2) : '-' },
{ key: 'height', label: 'Height', group: 'Physical', format: (v) => v === 0 ? '0' : v ? v.toFixed(2) : '-' },
{ key: 'dimensions', label: 'Dimensions', group: 'Physical', format: (v, product) => {
// Handle dimensions as separate length, width, height fields
const length = product?.length;
const width = product?.width;
const height = product?.height;
if (length && width && height) {
return `${length}×${width}×${height}`;
}
return '-';
}},
// Customer Engagement
{ key: 'rating', label: 'Rating', group: 'Customer', format: (v) => v === 0 ? '0' : v ? v.toFixed(1) : '-' },
@@ -530,6 +542,16 @@ export function Products() {
console.log('revenue30d:', transformedProducts[0].revenue30d);
console.log('margin30d:', transformedProducts[0].margin30d);
console.log('markup30d:', transformedProducts[0].markup30d);
// Debug specific fields with issues
console.log('configSafetyStock:', transformedProducts[0].configSafetyStock);
console.log('length:', transformedProducts[0].length);
console.log('width:', transformedProducts[0].width);
console.log('height:', transformedProducts[0].height);
console.log('first7DaysSales:', transformedProducts[0].first7DaysSales);
console.log('first30DaysSales:', transformedProducts[0].first30DaysSales);
console.log('first7DaysRevenue:', transformedProducts[0].first7DaysRevenue);
console.log('first30DaysRevenue:', transformedProducts[0].first30DaysRevenue);
}
// Transform the metrics response to match our expected format

View File

@@ -112,6 +112,10 @@ export interface ProductMetric {
width: number | null;
height: number | null;
} | null;
// Individual dimension fields for backend compatibility
length: number | null;
width: number | null;
height: number | null;
countryOfOrigin: string | null;
location: string | null;
baskets: number | null; // Number of times added to basket
@@ -280,6 +284,9 @@ export type ProductMetricColumnKey =
| 'location'
| 'moq'
| 'weight'
| 'length'
| 'width'
| 'height'
| 'dimensions'
| 'rating'
| 'reviews'
@@ -308,9 +315,13 @@ export type ProductMetricColumnKey =
| 'stockCoverInDays'
| 'sellsOutInDays'
| 'onOrderQty'
| 'onOrderCost'
| 'onOrderRetail'
| 'earliestExpectedDate'
| 'isOldStock'
| 'overstockedUnits'
| 'overstockedCost'
| 'overstockedRetail'
| 'stockoutDays30d'
| 'stockoutRate30d'
| 'avgStockUnits30d'
@@ -331,10 +342,6 @@ export type ProductMetricColumnKey =
| 'replenishmentCost'
| 'replenishmentRetail'
| 'replenishmentProfit'
| 'onOrderCost'
| 'onOrderRetail'
| 'overstockedCost'
| 'overstockedRetail'
| 'sales7d'
| 'revenue7d'
| 'sales14d'
@@ -354,24 +361,13 @@ export type ProductMetricColumnKey =
| 'roas30d'
| 'adSpend30d'
| 'gmroi30d'
| 'first7DaysSales'
| 'first7DaysRevenue'
| 'first30DaysSales'
| 'first30DaysRevenue'
| 'first60DaysSales'
| 'first60DaysRevenue'
| 'first90DaysSales'
| 'first90DaysRevenue'
| 'lifetimeSales'
| 'lifetimeRevenue'
| 'lifetimeAvgPrice'
| 'forecastSalesUnits'
| 'forecastSalesValue'
| 'forecastStockCover'
| 'forecastedOutOfStockDate'
| 'salesVelocity'
| 'stockturn30d'
| 'sellThrough30d'
| 'returnRate30d'
| 'discountRate30d'
| 'markdown30d'
| 'markdownRate30d'
| 'salesVelocityDaily'
| 'dateLastSold'
| 'yesterdaySales'
| 'avgSalesPerDay30d'
| 'avgSalesPerMonth30d'
@@ -383,13 +379,22 @@ export type ProductMetricColumnKey =
| 'asp30d'
| 'acp30d'
| 'avgRos30d'
| 'markup30d'
| 'stockturn30d'
| 'sellThrough30d'
| 'returnRate30d'
| 'discountRate30d'
| 'markdown30d'
| 'markdownRate30d'
| 'lifetimeSales'
| 'lifetimeRevenue'
| 'lifetimeRevenueQuality'
| 'first7DaysSales'
| 'first7DaysRevenue'
| 'first30DaysSales'
| 'first30DaysRevenue'
| 'first60DaysSales'
| 'first60DaysRevenue'
| 'first90DaysSales'
| 'first90DaysRevenue'
| 'dateFirstReceived'
| 'dateLastReceived'
| 'dateFirstSold'
| 'dateLastSold'
| 'avgLeadTimeDays'
| 'leadTimeForecastUnits'
| 'daysOfStockForecastUnits'
| 'planningPeriodForecastUnits'
@@ -398,12 +403,6 @@ export type ProductMetricColumnKey =
| 'replenishmentNeededRaw'
| 'forecastLostSalesUnits'
| 'forecastLostRevenue'
| 'avgLeadTimeDays'
| 'dateLastReceived'
| 'dateFirstReceived'
| 'dateFirstSold'
| 'imageUrl'
// New metrics from P3-P5 implementation
| 'salesGrowth30dVsPrev'
| 'revenueGrowth30dVsPrev'
| 'salesGrowthYoy'
@@ -419,7 +418,7 @@ export type ProductMetricColumnKey =
| 'seasonalityIndex'
| 'seasonalPattern'
| 'peakSeason'
| 'lifetimeRevenueQuality';
| 'imageUrl';
// Mapping frontend keys to backend query param keys
export const FRONTEND_TO_BACKEND_KEY_MAP: Record<string, string> = {