import React, { useState } from 'react' import { View, Text, TouchableOpacity, Alert, ScrollView } from 'react-native' import { Formik, FieldArray } from 'formik' import { MyTextInput, tw, ImageUploaderNeo, Checkbox, type ImageUploaderNeoItem, type ImageUploaderNeoPayload } from 'common-ui' import { trpc } from '../src/trpc-client' import ProductsSelector from '../components/ProductsSelector' import { useUploadToObjectStorage } from '../hooks/useUploadToObjectStore' import MaterialIcons from '@expo/vector-icons/MaterialIcons' interface OfferItem { productId: number quantity: string product?: { id: number; name: string; images: string[]; unitShortNotation: string } } interface OfferFormProps { onOfferAdded?: () => void offerId?: number initialName?: string initialShortDescription?: string initialLongDescription?: string initialPrice?: string initialMarketPrice?: string initialImages?: string[] initialImageKeys?: string[] initialIsSuspended?: boolean initialIsFlashEnabled?: boolean initialItems?: OfferItem[] } export default function OfferForm({ onOfferAdded, offerId, initialName = '', initialShortDescription = '', initialLongDescription = '', initialPrice = '', initialMarketPrice = '', initialImages = [], initialImageKeys = [], initialIsSuspended = false, initialIsFlashEnabled = false, initialItems = [], }: OfferFormProps) { const isEditMode = !!offerId const { mutate: createOffer, isPending: isCreating } = trpc.admin.offers.create.useMutation() const { mutate: updateOffer, isPending: isUpdating } = trpc.admin.offers.update.useMutation() const { upload, isUploading } = useUploadToObjectStorage() const isPending = isCreating || isUpdating || isUploading const [images, setImages] = useState( initialImages.map((url) => ({ imgUrl: url, mimeType: 'image/jpeg' })) ) const initialValues = { name: initialName, shortDescription: initialShortDescription, longDescription: initialLongDescription, price: initialPrice, marketPrice: initialMarketPrice, isFlashEnabled: initialIsFlashEnabled, items: initialItems.length > 0 ? initialItems.map(item => ({ productId: item.productId, quantity: item.quantity })) : [{ productId: 0, quantity: '1' }], } const handleSubmit = async (values: typeof initialValues) => { if (!values.name.trim()) { Alert.alert('Error', 'Offer name is required') return } if (!values.price || Number(values.price) <= 0) { Alert.alert('Error', 'Valid price is required') return } const validItems = values.items.filter(item => item.productId > 0) if (validItems.length === 0) { Alert.alert('Error', 'At least one product is required') return } let imageUrls: string[] = [] // Build map of signed URL → raw S3 key for existing images const signedUrlToKey: Record = {} initialImages.forEach((url, i) => { if (initialImageKeys[i]) { signedUrlToKey[url] = initialImageKeys[i] } }) // Existing images that are still present → use their raw S3 keys const keptImageKeys = images .filter(img => img.imgUrl.startsWith('http')) .map(img => signedUrlToKey[img.imgUrl]) .filter(Boolean) // New images (local file:// URIs) → upload to S3 const newImages = images.filter(img => !img.imgUrl.startsWith('http')) if (newImages.length > 0) { try { const blobs = await Promise.all( newImages.map(async (img) => { const response = await fetch(img.imgUrl) const blob = await response.blob() return { blob, mimeType: img.mimeType || 'image/jpeg' } }) ) const result = await upload({ images: blobs, contextString: 'product_info' as any }) imageUrls = [...keptImageKeys, ...result.keys] } catch (error: any) { Alert.alert('Error', error.message || 'Failed to upload images') return } } else { imageUrls = keptImageKeys } const offerData = { name: values.name.trim(), shortDescription: values.shortDescription.trim() || undefined, longDescription: values.longDescription.trim() || undefined, price: values.price, marketPrice: values.marketPrice || undefined, images: imageUrls, isFlashEnabled: values.isFlashEnabled, items: validItems.map(item => ({ productId: item.productId, quantity: item.quantity, })), } if (isEditMode && offerId) { updateOffer( { id: offerId, ...offerData } as any, { onSuccess: () => { Alert.alert('Success', 'Offer updated successfully!') onOfferAdded?.() }, onError: (error: any) => { Alert.alert('Error', error.message || 'Failed to update offer') }, } ) } else { createOffer( offerData as any, { onSuccess: () => { Alert.alert('Success', 'Offer created successfully!') onOfferAdded?.() }, onError: (error: any) => { Alert.alert('Error', error.message || 'Failed to create offer') }, } ) } } return ( {isEditMode ? 'Edit Offer' : 'Create New Offer'} {({ handleSubmit, values, setFieldValue }) => ( setFieldValue('name', text)} /> setFieldValue('shortDescription', text)} multiline /> setFieldValue('longDescription', text)} multiline /> Offer Images setImages(prev => [...prev, ...newImages.map((img: ImageUploaderNeoPayload) => ({ imgUrl: img.url, mimeType: img.mimeType, }))])} onImageRemove={(img) => setImages(prev => prev.filter(i => i.imgUrl !== img.url))} /> setFieldValue('price', text)} keyboardType="numeric" /> setFieldValue('marketPrice', text)} keyboardType="numeric" /> setFieldValue('isFlashEnabled', !values.isFlashEnabled)} /> Flash Enabled Products in Offer {({ push, remove }) => ( {values.items.map((item, index) => ( Product {index + 1} {values.items.length > 1 && ( remove(index)}> )} setFieldValue(`items.${index}.productId`, Array.isArray(value) ? value[0] : value)} multiple={false} placeholder="Select a product" /> setFieldValue(`items.${index}.quantity`, text)} keyboardType="numeric" /> ))} push({ productId: 0, quantity: '1' })} style={tw`bg-blue-500 px-4 py-3 rounded-xl items-center mb-4`} > Add Product )} handleSubmit()} disabled={isPending} style={tw`${isPending ? 'bg-pink-300' : 'bg-pink-500'} p-4 rounded-xl items-center mt-4`} > {isPending ? (isEditMode ? 'Updating...' : 'Creating...') : (isEditMode ? 'Update Offer' : 'Create Offer')} )} ) }