freshyo/apps/admin-ui/components/ProductGroupForm.tsx
2026-01-24 00:13:15 +05:30

151 lines
No EOL
4.7 KiB
TypeScript

import React from 'react';
import { View, TouchableOpacity, Alert, ScrollView } from 'react-native';
import { useFormik } from 'formik';
import { MyText, tw, MyTextInput, MyTouchableOpacity, theme, BottomDropdown } from 'common-ui';
import { trpc } from '../src/trpc-client';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
interface ProductGroup {
id: number;
groupName: string;
description: string | null;
createdAt: string;
products: any[];
productCount: number;
}
interface ProductGroupFormProps {
group?: ProductGroup | null;
onClose: () => void;
onSuccess: () => void;
}
const ProductGroupForm: React.FC<ProductGroupFormProps> = ({
group,
onClose,
onSuccess,
}) => {
// Fetch products
const { data: productsData } = trpc.common.product.getAllProductsSummary.useQuery({});
const createGroup = trpc.admin.product.createGroup.useMutation();
const updateGroup = trpc.admin.product.updateGroup.useMutation();
const isEditing = !!group;
const products = productsData?.products || [];
const productOptions = products.map(product => ({
label: `${product.name}${product.shortDescription ? ` - ${product.shortDescription}` : ''}`,
value: product.id,
}));
const formik = useFormik({
initialValues: {
group_name: group?.groupName || '',
description: group?.description || '',
product_ids: group?.products?.map(p => p.id) || [],
},
validate: (values) => {
const errors: {[key: string]: string} = {};
if (!values.group_name.trim()) {
errors.group_name = 'Group name is required';
}
return errors;
},
onSubmit: async (values) => {
try {
if (isEditing) {
await updateGroup.mutateAsync({
id: group.id,
group_name: values.group_name,
description: values.description,
product_ids: values.product_ids,
});
} else {
await createGroup.mutateAsync({
group_name: values.group_name,
description: values.description,
product_ids: values.product_ids,
});
}
onSuccess();
} catch (error: any) {
Alert.alert('Error', error.message || 'Failed to save group');
}
},
});
return (
<View style={tw`flex-1 bg-white`}>
{/* Header */}
<View style={tw`flex-row items-center justify-between p-6 border-b border-gray-200`}>
<MyText style={tw`text-xl font-bold text-gray-900`}>
{isEditing ? 'Edit Product Group' : 'Create Product Group'}
</MyText>
<TouchableOpacity onPress={onClose} style={tw`p-2`}>
<MaterialIcons name="close" size={24} color="#6B7280" />
</TouchableOpacity>
</View>
<ScrollView style={tw`flex-1`} contentContainerStyle={tw`px-6 py-4`}>
{/* Group Name */}
<MyTextInput
topLabel="Group Name *"
placeholder="Enter group name"
value={formik.values.group_name}
onChangeText={(text) => formik.setFieldValue('group_name', text)}
style={{ marginBottom: 16 }}
/>
{formik.errors.group_name && (
<MyText style={tw`text-red-500 text-sm mt-1 mb-4`}>{formik.errors.group_name}</MyText>
)}
{/* Description */}
<MyTextInput
topLabel="Description"
placeholder="Enter description (optional)"
multiline
numberOfLines={3}
value={formik.values.description}
onChangeText={(text) => formik.setFieldValue('description', text)}
style={{ marginBottom: 16 }}
/>
{/* Products Selection */}
<BottomDropdown
label="Products"
value={formik.values.product_ids}
options={productOptions}
onValueChange={(value) => formik.setFieldValue('product_ids', value as number[])}
multiple={true}
placeholder="Select products"
style={{ marginBottom: 16 }}
/>
{/* Actions */}
<View style={tw`flex-row gap-4`}>
<MyTouchableOpacity
onPress={onClose}
style={tw`flex-1 bg-gray-200 rounded-lg py-4 items-center`}
>
<MyText style={tw`text-gray-700 font-medium`}>Cancel</MyText>
</MyTouchableOpacity>
<MyTouchableOpacity
onPress={() => formik.handleSubmit()}
disabled={createGroup.isPending || updateGroup.isPending}
style={tw`flex-1 bg-brand600 rounded-lg py-4 items-center`}
>
<MyText style={tw`text-white font-medium`}>
{createGroup.isPending || updateGroup.isPending ? 'Saving...' : 'Save'}
</MyText>
</MyTouchableOpacity>
</View>
</ScrollView>
</View>
);
};
export default ProductGroupForm;