Add UPC validation and automatic item number generation/validation
This commit is contained in:
@@ -269,10 +269,6 @@ export function ProductSearchDialog({ isOpen, onClose, onTemplateCreated }: Prod
|
||||
};
|
||||
|
||||
const handleProductSelect = (product: Product) => {
|
||||
console.log('Selected product supplier data:', {
|
||||
vendor: product.vendor,
|
||||
vendor_reference: product.vendor_reference
|
||||
});
|
||||
|
||||
// Ensure all values are of the correct type
|
||||
setFormData({
|
||||
@@ -303,17 +299,13 @@ export function ProductSearchDialog({ isOpen, onClose, onTemplateCreated }: Prod
|
||||
|
||||
// Try to find the supplier ID from the vendor name
|
||||
if (product.vendor && fieldOptions) {
|
||||
console.log('Available suppliers:', fieldOptions.suppliers);
|
||||
console.log('Looking for supplier match for vendor:', product.vendor);
|
||||
|
||||
// First try exact match
|
||||
let supplierOption = fieldOptions.suppliers.find(
|
||||
let supplierOption = fieldOptions.suppliers.find(
|
||||
supplier => supplier.label.toLowerCase() === product.vendor.toLowerCase()
|
||||
);
|
||||
|
||||
// If no exact match, try partial match
|
||||
if (!supplierOption) {
|
||||
console.log('No exact match found, trying partial match');
|
||||
|
||||
supplierOption = fieldOptions.suppliers.find(
|
||||
supplier => supplier.label.toLowerCase().includes(product.vendor.toLowerCase()) ||
|
||||
product.vendor.toLowerCase().includes(supplier.label.toLowerCase())
|
||||
@@ -325,11 +317,7 @@ export function ProductSearchDialog({ isOpen, onClose, onTemplateCreated }: Prod
|
||||
...prev,
|
||||
supplier: supplierOption.value
|
||||
}));
|
||||
console.log('Found supplier match:', {
|
||||
vendorName: product.vendor,
|
||||
matchedSupplier: supplierOption.label,
|
||||
supplierId: supplierOption.value
|
||||
});
|
||||
|
||||
} else {
|
||||
console.log('No supplier match found for vendor:', product.vendor);
|
||||
}
|
||||
@@ -337,7 +325,7 @@ export function ProductSearchDialog({ isOpen, onClose, onTemplateCreated }: Prod
|
||||
|
||||
// Fetch product categories
|
||||
if (product.pid) {
|
||||
console.log('Fetching categories for product ID:', product.pid);
|
||||
|
||||
fetchProductCategories(product.pid);
|
||||
}
|
||||
|
||||
@@ -348,7 +336,7 @@ export function ProductSearchDialog({ isOpen, onClose, onTemplateCreated }: Prod
|
||||
const fetchProductCategories = async (productId: number) => {
|
||||
try {
|
||||
const response = await axios.get(`/api/import/product-categories/${productId}`);
|
||||
console.log('Product categories:', response.data);
|
||||
|
||||
|
||||
if (response.data && Array.isArray(response.data)) {
|
||||
// Filter out categories with type 20 (themes) and type 21 (subthemes)
|
||||
@@ -356,7 +344,6 @@ export function ProductSearchDialog({ isOpen, onClose, onTemplateCreated }: Prod
|
||||
category.type !== 20 && category.type !== 21
|
||||
);
|
||||
|
||||
console.log('Filtered categories (excluding themes):', filteredCategories);
|
||||
|
||||
// Extract category IDs and update form data
|
||||
const categoryIds = filteredCategories.map((category: any) => category.value);
|
||||
@@ -422,28 +409,14 @@ export function ProductSearchDialog({ isOpen, onClose, onTemplateCreated }: Prod
|
||||
|
||||
// Log supplier information for debugging
|
||||
if (formData.supplier) {
|
||||
const supplierOption = fieldOptions?.suppliers.find(
|
||||
supplier => supplier.value === formData.supplier
|
||||
);
|
||||
console.log('Submitting supplier:', {
|
||||
id: formData.supplier,
|
||||
name: supplierOption?.label || 'Unknown',
|
||||
allSuppliers: fieldOptions?.suppliers.map(s => ({ id: s.value, name: s.label }))
|
||||
});
|
||||
|
||||
} else {
|
||||
console.log('No supplier selected for submission');
|
||||
}
|
||||
|
||||
// Log categories information for debugging
|
||||
if (formData.categories && formData.categories.length > 0) {
|
||||
const categoryOptions = formData.categories.map(catId => {
|
||||
const category = fieldOptions?.categories.find(c => c.value === catId);
|
||||
return {
|
||||
id: catId,
|
||||
name: category?.label || 'Unknown Category'
|
||||
};
|
||||
});
|
||||
console.log('Submitting categories:', categoryOptions);
|
||||
|
||||
} else {
|
||||
console.log('No categories selected for submission');
|
||||
}
|
||||
@@ -471,11 +444,9 @@ export function ProductSearchDialog({ isOpen, onClose, onTemplateCreated }: Prod
|
||||
ship_restrictions: formData.ship_restrictions || null
|
||||
};
|
||||
|
||||
console.log('Sending template data:', dataToSend);
|
||||
|
||||
const response = await axios.post('/api/templates', dataToSend);
|
||||
|
||||
console.log('Template creation response:', response);
|
||||
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
toast.success('Template created successfully');
|
||||
@@ -1040,9 +1011,7 @@ export function ProductSearchDialog({ isOpen, onClose, onTemplateCreated }: Prod
|
||||
const filteredCategories = fieldOptions.categories.filter(
|
||||
category => category.type && validCategoryTypes.includes(category.type)
|
||||
);
|
||||
|
||||
console.log('Filtered categories for dropdown:', filteredCategories.length);
|
||||
|
||||
|
||||
return [...filteredCategories].sort((a, b) => {
|
||||
const aSelected = selected.has(a.value);
|
||||
const bSelected = selected.has(b.value);
|
||||
@@ -1171,10 +1140,7 @@ export function ProductSearchDialog({ isOpen, onClose, onTemplateCreated }: Prod
|
||||
onSelect={() => {
|
||||
// Make sure we're setting the ID (value), not the label
|
||||
handleSelectChange('supplier', supplier.value);
|
||||
console.log('Selected supplier from dropdown:', {
|
||||
label: supplier.label,
|
||||
value: supplier.value
|
||||
});
|
||||
|
||||
}}
|
||||
>
|
||||
<Check
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,7 +65,7 @@ export type Data<T extends string> = {
|
||||
// Data model RSI uses for spreadsheet imports
|
||||
export type Fields<T extends string> = DeepReadonly<Field<T>[]>
|
||||
|
||||
export type Field<T extends string = string> = {
|
||||
export type Field<T extends string> = {
|
||||
// UI-facing field label
|
||||
label: string
|
||||
// Field's unique identifier
|
||||
@@ -75,14 +75,14 @@ export type Field<T extends string = string> = {
|
||||
// Alternate labels used for fields' auto-matching, e.g. "fname" -> "firstName"
|
||||
alternateMatches?: string[]
|
||||
// Validations used for field entries
|
||||
validations?: Validation[]
|
||||
validations?: ValidationConfig[]
|
||||
// Field entry component
|
||||
fieldType: FieldType
|
||||
// UI-facing values shown to user as field examples pre-upload phase
|
||||
example?: string
|
||||
width?: number
|
||||
disabled?: boolean
|
||||
onChange?: (value: string) => void
|
||||
onChange?: (value: any, additionalData?: any) => void
|
||||
}
|
||||
|
||||
export type FieldType =
|
||||
|
||||
@@ -23,6 +23,39 @@ const BASE_IMPORT_FIELDS = [
|
||||
width: 220,
|
||||
validations: [{ rule: "required" as const, errorMessage: "Required", level: "error" as ErrorLevel }],
|
||||
},
|
||||
{
|
||||
label: "Company",
|
||||
key: "company",
|
||||
description: "Company/Brand name",
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options: [], // Will be populated from API
|
||||
},
|
||||
width: 200,
|
||||
validations: [{ rule: "required", errorMessage: "Required", level: "error" }],
|
||||
},
|
||||
{
|
||||
label: "Line",
|
||||
key: "line",
|
||||
description: "Product line",
|
||||
alternateMatches: ["collection"],
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options: [], // Will be populated dynamically based on company selection
|
||||
},
|
||||
width: 180,
|
||||
validations: [{ rule: "required", errorMessage: "Required", level: "error" }],
|
||||
},
|
||||
{
|
||||
label: "Sub Line",
|
||||
key: "subline",
|
||||
description: "Product sub-line",
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options: [], // Will be populated dynamically based on line selection
|
||||
},
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
label: "UPC",
|
||||
key: "upc",
|
||||
@@ -78,7 +111,7 @@ const BASE_IMPORT_FIELDS = [
|
||||
key: "item_number",
|
||||
description: "Internal item reference number",
|
||||
fieldType: { type: "input" },
|
||||
width: 120,
|
||||
width: 130,
|
||||
validations: [
|
||||
{ rule: "required", errorMessage: "Required", level: "error" },
|
||||
{ rule: "unique", errorMessage: "Must be unique", level: "error" },
|
||||
@@ -148,39 +181,6 @@ const BASE_IMPORT_FIELDS = [
|
||||
width: 180,
|
||||
validations: [{ rule: "required", errorMessage: "Required", level: "error" }],
|
||||
},
|
||||
{
|
||||
label: "Company",
|
||||
key: "company",
|
||||
description: "Company/Brand name",
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options: [], // Will be populated from API
|
||||
},
|
||||
width: 200,
|
||||
validations: [{ rule: "required", errorMessage: "Required", level: "error" }],
|
||||
},
|
||||
{
|
||||
label: "Line",
|
||||
key: "line",
|
||||
description: "Product line",
|
||||
alternateMatches: ["collection"],
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options: [], // Will be populated dynamically based on company selection
|
||||
},
|
||||
width: 180,
|
||||
validations: [{ rule: "required", errorMessage: "Required", level: "error" }],
|
||||
},
|
||||
{
|
||||
label: "Sub Line",
|
||||
key: "subline",
|
||||
description: "Product sub-line",
|
||||
fieldType: {
|
||||
type: "select",
|
||||
options: [], // Will be populated dynamically based on line selection
|
||||
},
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
label: "Artist",
|
||||
key: "artist",
|
||||
|
||||
Reference in New Issue
Block a user