From d56beb51433cfb1453247618aca9218a9eb6c937 Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 24 Oct 2025 12:58:20 -0400 Subject: [PATCH] Tweak import results UI --- inventory/src/index.css | 6 +- inventory/src/pages/Import.tsx | 302 ++++++++++++++++++++++++++++----- 2 files changed, 258 insertions(+), 50 deletions(-) diff --git a/inventory/src/index.css b/inventory/src/index.css index 0199cfe..765e381 100644 --- a/inventory/src/index.css +++ b/inventory/src/index.css @@ -26,13 +26,13 @@ --accent-foreground: 222.2 47.4% 11.2%; --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; + --destructive-foreground: 222.2 47.4% 11.2%; --info: 217.2 91.2% 59.8%; - --info-foreground: 210 40% 98%; + --info-foreground: 222.2 47.4% 11.2%; --success: 142.1 76.2% 36.3%; - --success-foreground: 210 40% 98%; + --success-foreground: 222.2 47.4% 11.2%; --warning: 45.4 93.4% 47.5%; --warning-foreground: 222.2 47.4% 11.2%; diff --git a/inventory/src/pages/Import.tsx b/inventory/src/pages/Import.tsx index 41c292f..7866c43 100644 --- a/inventory/src/pages/Import.tsx +++ b/inventory/src/pages/Import.tsx @@ -8,8 +8,10 @@ import { toast } from "sonner"; import { motion } from "framer-motion"; import { useQuery } from "@tanstack/react-query"; import config from "@/config"; -import { Loader2, AlertCircle, AlertTriangle, Info, CheckCircle } from "lucide-react"; +import { Loader2, AlertCircle, AlertTriangle, Info, CheckCircle, ExternalLink } from "lucide-react"; import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert"; +import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"; +import { Separator } from "@/components/ui/separator"; import type { Data, DataValue, FieldType, Result, SubmitOptions } from "@/components/product-import/types"; import { BASE_IMPORT_FIELDS, type ImportFieldKey } from "@/components/product-import/config"; import { submitNewProducts, type SubmitNewProductsResponse } from "@/services/apiv2"; @@ -254,6 +256,199 @@ export function Import() { const { user } = useContext(AuthContext); const hasDebugPermission = Boolean(user?.is_admin || user?.permissions?.includes("admin:debug")); + // ========== TEMPORARY TEST DATA ========== + // Uncomment the useEffect below to test the results page without submitting actual data + + // useEffect(() => { + // // Test scenario: Mix of successful and failed products + // const testSubmittedProducts: NormalizedProduct[] = [ + // { + // name: "Test Product 1", + // upc: "123456789012", + // item_number: "ITEM-001", + // company: "Test Company", + // line: "Test Line", + // subline: "Test Subline", + // product_images: ["https://picsum.photos/200/200?random=1"], + // short_description: "This is a test product", + // retail: "29.99", + // wholesale: "15.00", + // weight: "1.5", + // categories: ["Category 1", "Category 2"], + // colors: ["Red", "Blue"], + // size_cat: "Medium", + // tax_cat: "Taxable", + // ship_restrictions: "None", + // supplier: "Test Supplier", + // artist: null, + // themes: ["Theme 1"], + // vendor_sku: "VS-001", + // publish: true, + // list_on_marketplace: false, + // }, + // { + // name: "Test Product 2", + // upc: "234567890123", + // item_number: "ITEM-002", + // company: "Test Company", + // line: "Test Line", + // subline: "Test Subline", + // product_images: ["https://picsum.photos/200/200?random=2"], + // short_description: "Another test product", + // retail: "49.99", + // wholesale: "25.00", + // weight: "2.0", + // categories: ["Category 3"], + // colors: ["Green"], + // size_cat: "Large", + // tax_cat: "Taxable", + // ship_restrictions: "None", + // supplier: "Test Supplier", + // artist: "Test Artist", + // themes: [], + // vendor_sku: "VS-002", + // publish: true, + // list_on_marketplace: true, + // }, + // { + // name: "Failed Product 1", + // upc: "345678901234", + // item_number: "ITEM-003", + // company: "Test Company", + // line: "Test Line", + // subline: null, + // product_images: ["https://picsum.photos/200/200?random=3"], + // short_description: "This product will fail", + // retail: "19.99", + // wholesale: "10.00", + // weight: "0.5", + // categories: [], + // colors: [], + // size_cat: null, + // tax_cat: "Taxable", + // ship_restrictions: null, + // supplier: null, + // artist: null, + // themes: [], + // vendor_sku: "VS-003", + // publish: false, + // list_on_marketplace: false, + // }, + // { + // name: "Failed Product 2", + // upc: "456789012345", + // item_number: "ITEM-004", + // company: "Test Company", + // line: null, + // subline: null, + // product_images: null, + // description: "Another failed product", + // msrp: "99.99", + // cost_each: "50.00", + // weight: "5.0", + // categories: ["Category 1"], + // colors: ["Yellow"], + // size_cat: "Small", + // tax_cat: null, + // ship_restrictions: "Hazmat", + // supplier: "Test Supplier", + // artist: null, + // themes: [], + // vendor_sku: null, + // publish: true, + // list_on_marketplace: false, + // }, + // ]; + + // const testSubmittedRows: Data[] = testSubmittedProducts.map(product => ({ ...product } as Data)); + + // //Scenario 1: All successful + // const testResponse: SubmitNewProductsResponse = { + // success: true, + // message: "Successfully created 4 products", + // data: { + // created: [ + // { pid: 12345, upc: "123456789012", item_number: "ITEM-001" }, + // { pid: 12346, upc: "234567890123", item_number: "ITEM-002" }, + // { pid: 12347, upc: "345678901234", item_number: "ITEM-003" }, + // { pid: 12348, upc: "456789012345", item_number: "ITEM-004" }, + // ], + // errored: [], + // }, + // }; + + // // Scenario 2: Partial success (2 created, 2 failed) + // const testResponse: SubmitNewProductsResponse = { + // success: true, + // message: "Created 2 of 4 products. 2 products had errors.", + // data: { + // created: [ + // { pid: 12345, upc: "123456789012", item_number: "ITEM-001" }, + // { pid: 12346, upc: "234567890123", item_number: "ITEM-002" }, + // ], + // errored: [ + // { + // upc: "345678901234", + // item_number: "ITEM-003", + // error_msg: "Missing required field: supplier", + // errors: { + // supplier: ["Supplier is required for this product line"], + // categories: ["At least one category must be selected"], + // }, + // }, + // { + // upc: "456789012345", + // item_number: "ITEM-004", + // error_msg: "Invalid product configuration", + // errors: { + // line: ["Product line is required"], + // tax_cat: ["Tax category must be specified"], + // }, + // }, + // ], + // }, + // }; + + // // Scenario 3: Complete failure + // const testResponse: SubmitNewProductsResponse = { + // success: false, + // message: "Failed to create products. Please check the errors below.", + // data: { + // created: [], + // errored: [ + // { + // upc: "123456789012", + // item_number: "ITEM-001", + // error_msg: "UPC already exists in the system", + // }, + // { + // upc: "234567890123", + // item_number: "ITEM-002", + // error_msg: "Invalid wholesale price", + // }, + // { + // upc: "345678901234", + // item_number: "ITEM-003", + // error_msg: "Missing required field: supplier", + // }, + // { + // upc: "456789012345", + // item_number: "ITEM-004", + // error_msg: "Invalid product configuration", + // }, + // ], + // }, + // }; + + // setImportOutcome({ + // submittedProducts: testSubmittedProducts, + // submittedRows: testSubmittedRows, + // response: testResponse, + // }); + // }, []); + + // ========== END TEST DATA ========== + // Fetch initial field options from the API const { data: fieldOptions, isLoading: isLoadingOptions } = useQuery({ queryKey: ["import-field-options"], @@ -759,7 +954,7 @@ export function Import() { Import Results {hasDebugPermission && ( - )} {createdProducts.length > 0 && (
@@ -834,7 +1016,7 @@ export function Import() { ); return ( -
+
{product.url ? ( )} -
- {product.url ? ( - - {product.name} - - ) : ( - {product.name} - )} +
+
+
+ {product.url ? ( + + {product.name} + + + + + + + +

View in Backend

+
+
+
+ ) : ( + {product.name} + )} +
+
UPC: {product.upc} @@ -876,10 +1072,17 @@ export function Import() { {erroredProducts.length > 0 && (
-

Products with Errors

-
+
+

Products with Errors

+ {hasErroredRowsForEditing && ( + + )} +
+
{erroredProducts.map((product, index) => ( -
+
{product.imageUrl ? ( {product.name} @@ -891,10 +1094,15 @@ export function Import() {
{product.name} +
UPC: {product.upc} + Item #: {product.itemNumber} +
{product.errorDetails && ( - {product.errorDetails} +
+ {product.errorDetails} +
)}