freshyo/apps/user-ui/components/ComplaintForm.tsx
2026-03-22 16:11:01 +05:30

131 lines
4.4 KiB
TypeScript

import React, { useState } from 'react';
import { View, TextInput, ActivityIndicator, KeyboardAvoidingView, Platform, Alert } from 'react-native';
import { MaterialIcons } from '@expo/vector-icons';
import { MyText, ImageUploader, tw, MyTouchableOpacity } from 'common-ui';
import usePickImage from 'common-ui/src/components/use-pick-image';
import { trpc } from '@/src/trpc-client';
import { useUploadToObjectStorage } from '@/src/hooks/useUploadToObjectStorage';
interface ComplaintFormProps {
open: boolean;
onClose: () => void;
orderId: number;
}
export default function ComplaintForm({ open, onClose, orderId }: ComplaintFormProps) {
const [complaintBody, setComplaintBody] = useState('');
const [complaintImages, setComplaintImages] = useState<{ uri: string; mimeType: string }[]>([]);
const raiseComplaintMutation = trpc.user.complaint.raise.useMutation();
const { upload, isUploading } = useUploadToObjectStorage();
const pickComplaintImage = usePickImage({
setFile: async (assets: any) => {
if (!assets || (Array.isArray(assets) && assets.length === 0)) {
return;
}
const files = Array.isArray(assets) ? assets : [assets];
const newImages = files.map((asset: any) => ({
uri: asset.uri,
mimeType: asset.mimeType || 'image/jpeg',
}));
setComplaintImages(prev => [...prev, ...newImages]);
},
multiple: true,
});
const handleRemoveImage = (image: { uri: string; mimeType: string }) => {
setComplaintImages(prev => prev.filter(img => img.uri !== image.uri));
};
const handleSubmit = async () => {
if (!complaintBody.trim()) {
Alert.alert('Error', 'Please enter complaint details');
return;
}
try {
let imageKeys: string[] = [];
// Upload images if provided
if (complaintImages.length > 0) {
// Fetch blobs from URIs
const imagesWithBlobs = await Promise.all(
complaintImages.map(async (img) => {
const response = await fetch(img.uri);
const blob = await response.blob();
return { blob, mimeType: img.mimeType };
})
);
const result = await upload({
images: imagesWithBlobs,
contextString: 'complaint',
});
imageKeys = result.keys;
}
// Submit complaint
await raiseComplaintMutation.mutateAsync({
orderId,
complaintBody: complaintBody.trim(),
imageKeys,
});
Alert.alert('Success', 'Complaint raised successfully');
setComplaintBody('');
setComplaintImages([]);
onClose();
} catch (error: any) {
Alert.alert('Error', error.message || 'Failed to raise complaint');
}
};
if (!open) return null;
return (
<KeyboardAvoidingView behavior={Platform.OS === 'ios' ? 'padding' : undefined}>
<View style={tw`p-6`}>
<View style={tw`flex-row justify-between items-center mb-4`}>
<MyText style={tw`text-xl font-bold text-gray-900`}>Raise Complaint</MyText>
<MyTouchableOpacity onPress={onClose}>
<MaterialIcons name="close" size={24} color="#9CA3AF" />
</MyTouchableOpacity>
</View>
<MyText style={tw`text-gray-700 font-medium mb-2`}>Describe the issue</MyText>
<TextInput
style={tw`bg-gray-50 border border-gray-200 rounded-xl p-4 min-h-32 text-base text-gray-800 mb-4`}
value={complaintBody}
onChangeText={setComplaintBody}
placeholder="Tell us what went wrong..."
placeholderTextColor="#9CA3AF"
multiline
numberOfLines={4}
textAlignVertical="top"
/>
<ImageUploader
images={complaintImages}
onAddImage={pickComplaintImage}
onRemoveImage={(uri) => setComplaintImages(prev => prev.filter(img => img.uri !== uri))}
allowMultiple={true}
/>
<MyTouchableOpacity
style={tw`bg-yellow-500 py-4 rounded-xl shadow-sm items-center mt-4 ${isUploading || raiseComplaintMutation.isPending ? 'opacity-70' : ''}`}
onPress={handleSubmit}
disabled={isUploading || raiseComplaintMutation.isPending}
>
{isUploading || raiseComplaintMutation.isPending ? (
<ActivityIndicator color="white" />
) : (
<MyText style={tw`text-white font-bold text-lg`}>Submit Complaint</MyText>
)}
</MyTouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}