import React, { useState, useEffect, useCallback, forwardRef, useImperativeHandle, useMemo } from 'react'; import { View, TouchableOpacity } from 'react-native'; import { Formik, FieldArray } from 'formik'; import * as Yup from 'yup'; import { MyTextInput, BottomDropdown, MyText, useTheme, DatePicker, tw, useFocusCallback, Checkbox, ImageUploaderNeo, ImageUploaderNeoItem, ImageUploaderNeoPayload } from 'common-ui'; import MaterialIcons from '@expo/vector-icons/MaterialIcons'; import { trpc } from '../trpc-client'; interface ProductFormData { name: string; shortDescription: string; longDescription: string; unitId: number; storeId: number; price: string; marketPrice: string; isSuspended: boolean; isFlashAvailable: boolean; flashPrice: string; deals: Deal[]; tagIds: number[]; productQuantity: number; } interface Deal { quantity: string; price: string; validTill: Date | null; } export interface ProductFormRef { clearImages: () => void; } interface ProductFormProps { mode: 'create' | 'edit'; initialValues: ProductFormData; onSubmit: (values: ProductFormData, images: ImageUploaderNeoPayload[], imagesToDelete: string[]) => void; isLoading: boolean; existingImages?: ImageUploaderNeoItem[]; existingImageKeys?: string[]; } const unitOptions = [ { label: 'Kg', value: 1 }, { label: 'Litre', value: 2 }, { label: 'Dozen', value: 3 }, { label: 'Unit Piece', value: 4 }, ]; const ProductForm = forwardRef(({ mode, initialValues, onSubmit, isLoading, existingImages = [], existingImageKeys = [], }, ref) => { const { theme } = useTheme(); const [images, setImages] = useState(existingImages); // Sync images state when existingImages prop changes (e.g., when async query data arrives) useEffect(() => { setImages(existingImages); }, [existingImages]); const { data: storesData } = trpc.common.getStoresSummary.useQuery(); const storeOptions = storesData?.stores.map(store => ({ label: store.name, value: store.id, })) || []; const { data: tagsData } = trpc.admin.product.getProductTags.useQuery(); const tagOptions = tagsData?.tags.map(tag => ({ label: tag.tagName, value: tag.id.toString(), })) || []; // Build signed URL -> S3 key mapping for existing images const signedUrlToKey = useMemo(() => { const map: Record = {}; existingImages.forEach((img, i) => { if (existingImageKeys[i]) { map[img.imgUrl] = existingImageKeys[i]; } }); return map; }, [existingImages, existingImageKeys]); return ( { // New images have mimeType set, existing images have mimeType === null const newImages = images.filter(img => img.mimeType !== null); const deletedImageKeys = existingImages .filter(existing => !images.some(current => current.imgUrl === existing.imgUrl)) .map(deleted => signedUrlToKey[deleted.imgUrl]) .filter(Boolean); onSubmit( values, newImages.map(img => ({ url: img.imgUrl, mimeType: img.mimeType })), deletedImageKeys, ); }} enableReinitialize > {({ handleChange, handleSubmit, values, setFieldValue, resetForm }) => { const clearForm = useCallback(() => { setImages([]); resetForm(); }, [resetForm]); useFocusCallback(clearForm); useImperativeHandle(ref, () => ({ clearImages: clearForm, }), [clearForm]); const submit = () => handleSubmit(); return ( setImages(prev => [...prev, ...payloads.map(p => ({ imgUrl: p.url, mimeType: p.mimeType }))])} onImageRemove={(payload) => setImages(prev => prev.filter(img => img.imgUrl !== payload.url))} allowMultiple={true} /> setFieldValue('unitId', value)} placeholder="Select unit" style={{ marginBottom: 16 }} /> setFieldValue('productQuantity', text)} style={{ marginBottom: 16 }} /> setFieldValue('storeId', value)} placeholder="Select store" style={{ marginBottom: 16 }} /> id.toString())} options={tagOptions} onValueChange={(value) => setFieldValue('tagIds', (value as string[]).map(id => parseInt(id)))} multiple={true} placeholder="Select tags" style={{ marginBottom: 16 }} /> setFieldValue('isSuspended', !values.isSuspended)} style={tw`mr-3`} /> Suspend Product { setFieldValue('isFlashAvailable', !values.isFlashAvailable); if (values.isFlashAvailable) setFieldValue('flashPrice', ''); }} style={tw`mr-3`} /> Flash Available {values.isFlashAvailable && ( )} {isLoading ? (mode === 'create' ? 'Creating...' : 'Updating...') : (mode === 'create' ? 'Create Product' : 'Update Product')} ); }} ); }); ProductForm.displayName = 'ProductForm'; export default ProductForm;