import React, { useState } from 'react'; import { View, ScrollView, TouchableOpacity, Alert } from 'react-native'; import { Formik, FormikHelpers } from 'formik'; import * as Yup from 'yup'; import { MyText, tw, MyTextInput, MyTouchableOpacity, ImageUploader, BottomDropdown } from 'common-ui'; import { DropdownOption } from 'common-ui/src/components/bottom-dropdown'; import { trpc } from '../src/trpc-client'; import usePickImage from 'common-ui/src/components/use-pick-image'; import MaterialIcons from '@expo/vector-icons/MaterialIcons'; export interface BannerFormData { name: string; imageUrl: string; description: string; productIds: number[]; redirectUrl: string; // serialNum removed - will be assigned automatically by backend } interface BannerFormProps { initialValues: BannerFormData; onSubmit: (values: BannerFormData, imageUrl?: string) => Promise | void; onCancel: () => void; submitButtonText?: string; isSubmitting?: boolean; existingImageUrl?: string; mode?: 'create' | 'edit'; } const validationSchema = Yup.object().shape({ name: Yup.string().trim().required('Banner name is required').max(255), description: Yup.string().max(500), productIds: Yup.array() .of(Yup.number()) .optional(), redirectUrl: Yup.string() .url('Please enter a valid URL') .optional(), // serialNum validation removed - assigned automatically by backend }); export default function BannerForm({ initialValues, onSubmit, onCancel, submitButtonText = 'Create Banner', isSubmitting = false, existingImageUrl, mode = 'create', }: BannerFormProps) { const [selectedImages, setSelectedImages] = useState<{ blob: Blob; mimeType: string }[]>([]); const [displayImages, setDisplayImages] = useState<{ uri?: string }[]>([]); const generateUploadUrls = trpc.common.generateUploadUrls.useMutation(); // Fetch products for dropdown const { data: productsData } = trpc.common.product.getAllProductsSummary.useQuery({}); const products = productsData?.products || []; // Create product options for dropdown const productOptions: DropdownOption[] = products.map(product => ({ label: `${product.name} (${product.price})`, value: product.id, })); const handleImagePick = usePickImage({ setFile: async (assets: any) => { if (!assets || (Array.isArray(assets) && assets.length === 0)) { setSelectedImages([]); setDisplayImages([]); return; } const files = Array.isArray(assets) ? assets : [assets]; const blobPromises = files.map(async (asset) => { const response = await fetch(asset.uri); const blob = await response.blob(); return { blob, mimeType: asset.mimeType || 'image/jpeg' }; }); const blobArray = await Promise.all(blobPromises); setSelectedImages(blobArray); setDisplayImages(files.map(asset => ({ uri: asset.uri }))); }, multiple: false, // Single image for banners }); const handleRemoveImage = (uri: string) => { const index = displayImages.findIndex(img => img.uri === uri); if (index !== -1) { const newDisplay = displayImages.filter((_, i) => i !== index); const newFiles = selectedImages.filter((_, i) => i !== index); setDisplayImages(newDisplay); setSelectedImages(newFiles); } }; const handleFormikSubmit = async (values: BannerFormData) => { try { let imageUrl: string | undefined; if (selectedImages.length > 0) { // Generate upload URLs const mimeTypes = selectedImages.map(s => s.mimeType); const { uploadUrls } = await generateUploadUrls.mutateAsync({ contextString: 'store', // Using 'store' for now mimeTypes, }); // Upload image const uploadUrl = uploadUrls[0]; const { blob, mimeType } = selectedImages[0]; const uploadResponse = await fetch(uploadUrl, { method: 'PUT', body: blob, headers: { 'Content-Type': mimeType, }, }); if (!uploadResponse.ok) { throw new Error(`Upload failed with status ${uploadResponse.status}`); } imageUrl = uploadUrl; } // Call onSubmit with form values and imageUrl await onSubmit(values, imageUrl); } catch (error) { console.error('Upload error:', error); Alert.alert('Error', 'Failed to upload image'); } }; return ( {({ handleChange, handleBlur, handleSubmit, values, errors, touched, isValid, dirty, setFieldValue, }) => ( {errors.name && touched.name && ( {errors.name} )} Banner Image { // Handle removing existing image in edit mode }} allowMultiple={false} /> {errors.description && touched.description && ( {errors.description} )} { const selectedValues = Array.isArray(value) ? value : [value]; setFieldValue('productIds', selectedValues.map(v => Number(v))); }} placeholder="Select products for banner (optional)" multiple={true} /> {errors.redirectUrl && touched.redirectUrl && ( {errors.redirectUrl} )} {/* Action Buttons */} Cancel handleSubmit()} disabled={isSubmitting || !isValid || !dirty} style={tw`flex-1 rounded-lg py-4 items-center ${ isSubmitting || !isValid || !dirty ? 'bg-blue-400' : 'bg-blue-600' }`} > {isSubmitting ? 'Saving...' : submitButtonText} )} ); }