enh
This commit is contained in:
parent
9d2e01c6d2
commit
21002332fb
1 changed files with 61 additions and 22 deletions
|
|
@ -123,21 +123,37 @@ const productValidationSchema = Yup.object().shape({
|
|||
}
|
||||
return true
|
||||
})
|
||||
.test('quantity-feature', 'Every SKU must have a quantity feature', function (variants) {
|
||||
.test('quantity-feature', 'Each SKU must have exactly one quantity feature', function (variants) {
|
||||
if (!Array.isArray(variants)) return true
|
||||
for (const variant of variants) {
|
||||
const attrs = variant?.attributes || []
|
||||
const hasQuantity = attrs.some(
|
||||
const quantityCount = attrs.filter(
|
||||
(a) => ((a as Attribute)?.featureName ?? '').trim().toLowerCase() === 'quantity'
|
||||
)
|
||||
if (!hasQuantity) {
|
||||
).length
|
||||
if (quantityCount === 0) {
|
||||
return this.createError({ message: 'Every SKU must have a quantity feature' })
|
||||
}
|
||||
if (quantityCount > 1) {
|
||||
return this.createError({ message: 'Each SKU can have only one quantity feature' })
|
||||
}
|
||||
}
|
||||
return true
|
||||
}),
|
||||
})
|
||||
|
||||
// Collect every string message from a Formik errors tree (objects, arrays, strings).
|
||||
// Variant-level errors (e.g. 'Each SKU can have only one quantity feature') can get
|
||||
// hidden by Formik when index-level errors are also present, so we walk everything.
|
||||
const collectErrorMessages = (errors: unknown, depth = 0): string[] => {
|
||||
if (depth > 6) return []
|
||||
if (typeof errors === 'string') return [errors]
|
||||
if (Array.isArray(errors)) return errors.flatMap((e) => collectErrorMessages(e, depth + 1))
|
||||
if (errors && typeof errors === 'object') {
|
||||
return Object.values(errors).flatMap((e) => collectErrorMessages(e, depth + 1))
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
|
||||
mode,
|
||||
initialValues,
|
||||
|
|
@ -188,6 +204,20 @@ const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
|
|||
initialValues={formInitialValues}
|
||||
validationSchema={productValidationSchema}
|
||||
onSubmit={(values) => {
|
||||
// Normalize feature names before sending to backend: the mandatory
|
||||
// 'quantity' feature must always go out lowercase (case-insensitive
|
||||
// matching means 'Quantity'/'QUANTITY' are treated the same), and
|
||||
// names are trimmed like the backend does.
|
||||
const normalizedValues: ProductFormData = {
|
||||
...values,
|
||||
variants: values.variants.map((v) => ({
|
||||
...v,
|
||||
attributes: v.attributes.map((a) => ({
|
||||
...a,
|
||||
featureName: isQuantityFeature(a) ? 'quantity' : (a.featureName ?? '').trim() || null,
|
||||
})),
|
||||
})),
|
||||
}
|
||||
const images = variantImages.map((imgs) =>
|
||||
imgs.map((img) => ({ url: img.imgUrl, mimeType: img.mimeType }))
|
||||
)
|
||||
|
|
@ -203,7 +233,7 @@ const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
|
|||
})
|
||||
})
|
||||
}
|
||||
onSubmit(values, images, deletedKeys)
|
||||
onSubmit(normalizedValues, images, deletedKeys)
|
||||
}}
|
||||
enableReinitialize
|
||||
>
|
||||
|
|
@ -323,13 +353,24 @@ const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
|
|||
<MyText style={tw`text-gray-600 text-xs ml-0.5`}>Add</MyText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{variant.attributes.map((attr, aIndex) => (
|
||||
{variant.attributes.map((attr, aIndex) => {
|
||||
const quantityCount = variant.attributes.filter(isQuantityFeature).length
|
||||
// Quantity features are locked (non-editable), but if there
|
||||
// are duplicates (e.g. 'quantity' + 'Quantity') the user must
|
||||
// be able to delete the extras to fix the form.
|
||||
const canDelete = variant.attributes.length > 1 && (!isQuantityFeature(attr) || quantityCount > 1)
|
||||
return (
|
||||
<View key={aIndex} style={tw`flex-row items-center mb-2 gap-2`}>
|
||||
<View style={tw`flex-1`}>
|
||||
<MyTextInput
|
||||
placeholder="Name"
|
||||
value={attr.featureName ?? ''}
|
||||
onChangeText={handleChange(`variants.${vIndex}.attributes.${aIndex}.featureName`)}
|
||||
onChangeText={(text) =>
|
||||
setFieldValue(
|
||||
`variants.${vIndex}.attributes.${aIndex}.featureName`,
|
||||
text.trim().toLowerCase() === 'quantity' ? 'quantity' : text
|
||||
)
|
||||
}
|
||||
editable={!isQuantityFeature(attr)}
|
||||
style={isQuantityFeature(attr) ? tw`text-gray-400` : undefined}
|
||||
/>
|
||||
|
|
@ -341,13 +382,14 @@ const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
|
|||
onChangeText={handleChange(`variants.${vIndex}.attributes.${aIndex}.featureValue`)}
|
||||
/>
|
||||
</View>
|
||||
{variant.attributes.length > 1 && !isQuantityFeature(attr) && (
|
||||
{canDelete && (
|
||||
<TouchableOpacity onPress={() => removeAttr(aIndex)}>
|
||||
<MaterialIcons name="close" size={18} color="#EF4444" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
<View style={tw`flex-row items-center self-start`}>
|
||||
<TouchableOpacity
|
||||
onPress={() => pushAttr(defaultAttribute())}
|
||||
|
|
@ -534,18 +576,10 @@ const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
|
|||
onPress={async () => {
|
||||
const validationErrors = await validateForm()
|
||||
if (Object.keys(validationErrors).length > 0) {
|
||||
const variantsError = validationErrors.variants
|
||||
const firstVariantErrors = Array.isArray(variantsError)
|
||||
? (variantsError[0] as Record<string, unknown> | undefined) || {}
|
||||
: {}
|
||||
const message = (firstVariantErrors.price as string | undefined)
|
||||
|| (firstVariantErrors.marketPrice as string | undefined)
|
||||
|| (firstVariantErrors.flashPrice as string | undefined)
|
||||
|| (firstVariantErrors.attributes as string | undefined)
|
||||
|| validationErrors.name
|
||||
|| validationErrors.storeId
|
||||
|| variantsError
|
||||
Alert.alert('Check your form', String(message || 'Please fix the highlighted fields'))
|
||||
const variantsMessages = collectErrorMessages(validationErrors.variants)
|
||||
const allMessages = collectErrorMessages(validationErrors)
|
||||
const message = variantsMessages[0] || allMessages[0] || 'Please fix the highlighted fields'
|
||||
Alert.alert('Check your form', String(message))
|
||||
return
|
||||
}
|
||||
handleSubmit()
|
||||
|
|
@ -554,7 +588,12 @@ const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
|
|||
style={tw`px-4 py-3 rounded-lg shadow-lg items-center mt-4 ${isLoading ? 'bg-gray-400' : 'bg-blue-500'}`}
|
||||
>
|
||||
<MyText style={tw`text-white text-lg font-bold`}>
|
||||
{isLoading ? 'Creating...' : 'Create Product'}
|
||||
{(() => {
|
||||
if (mode === 'edit') {
|
||||
return isLoading ? 'Saving...' : 'Save Changes'
|
||||
}
|
||||
return isLoading ? 'Creating...' : 'Create Product'
|
||||
})()}
|
||||
</MyText>
|
||||
</TouchableOpacity>
|
||||
</ScrollView>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue