import React, { forwardRef, useState, useEffect, useMemo } from 'react'; import { View, TouchableOpacity, Alert } from 'react-native'; import { Formik } from 'formik'; import * as Yup from 'yup'; import { MyTextInput, BottomDropdown, MyText, tw, ImageUploader } from 'common-ui'; import { trpc } from '../src/trpc-client'; import usePickImage from 'common-ui/src/components/use-pick-image'; export interface StoreFormData { name: string; description: string; imageUrl?: string; owner: number; products: number[]; } export interface StoreFormRef { // Add methods if needed } interface StoreFormProps { mode: 'create' | 'edit'; initialValues: StoreFormData; onSubmit: (values: StoreFormData) => void; isLoading: boolean; storeId?: number; } const validationSchema = Yup.object().shape({ name: Yup.string().required('Name is required'), description: Yup.string(), imageUrl: Yup.string(), owner: Yup.number().required('Owner is required'), products: Yup.array().of(Yup.number()), }); const StoreForm = forwardRef((props, ref) => { const { mode, initialValues, onSubmit, isLoading, storeId } = props; const { data: staffData } = trpc.admin.staffUser.getStaff.useQuery(); const { data: productsData } = trpc.admin.product.getProducts.useQuery(); const [formInitialValues, setFormInitialValues] = useState(initialValues); const [selectedImages, setSelectedImages] = useState<{ blob: Blob; mimeType: string }[]>([]); const [displayImages, setDisplayImages] = useState<{ uri?: string }[]>([]); // For edit mode, pre-select products belonging to this store const initialSelectedProducts = useMemo(() => { if (mode !== 'edit' || !productsData?.products) return []; return productsData.products .filter(p => p.storeId === storeId) .map(p => p.id); }, [mode, productsData?.products, storeId]); useEffect(() => { setFormInitialValues({ ...initialValues, products: initialSelectedProducts, }); }, [initialValues, initialSelectedProducts]); const staffOptions = staffData?.staff.map(staff => ({ label: staff.name, value: staff.id, })) || []; const productOptions = productsData?.products.map(product => ({ label: `${product.name} - ₹${product.price}`, value: product.id, disabled: product.storeId !== null && product.storeId !== storeId, // Disable if belongs to another store })) || []; const generateUploadUrls = trpc.common.generateUploadUrls.useMutation(); 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 stores }); 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); } }; return ( {({ handleChange, handleSubmit, values, setFieldValue, errors, touched }) => { const submit = async () => { 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', mimeTypes, }); // Upload images for (let i = 0; i < uploadUrls.length; i++) { const uploadUrl = uploadUrls[i]; const { blob, mimeType } = selectedImages[i]; 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}`); } } // Extract key from first upload URL // const u = new URL(uploadUrls[0]); // const rawKey = u.pathname.replace(/^\/+/, ""); // imageUrl = decodeURIComponent(rawKey); imageUrl = uploadUrls[0]; } // Submit form with imageUrl onSubmit({ ...values, imageUrl }); } catch (error) { console.error('Upload error:', error); Alert.alert('Error', 'Failed to upload image'); } }; return ( setFieldValue('owner', value)} placeholder="Select owner" error={!!(touched.owner && errors.owner)} style={{ marginBottom: 16 }} /> setFieldValue('products', value)} placeholder="Select products" multiple style={{ marginBottom: 16 }} /> Store Image setFormInitialValues({ ...formInitialValues, imageUrl: undefined })} allowMultiple={false} /> {generateUploadUrls.isPending ? 'Uploading...' : isLoading ? (mode === 'create' ? 'Creating...' : 'Updating...') : (mode === 'create' ? 'Create Store' : 'Update Store')} ); }} ); }); StoreForm.displayName = 'StoreForm'; export default StoreForm;