freshyo/apps/admin-ui/components/SlotForm.tsx
2026-02-01 21:01:05 +05:30

230 lines
No EOL
8.3 KiB
TypeScript

import React from 'react';
import { View, Text, TouchableOpacity, Alert } from 'react-native';
import { Formik, FieldArray } from 'formik';
import DateTimePickerMod from 'common-ui/src/components/date-time-picker';
import { tw, MyTextInput } from 'common-ui';
import { trpc } from '../src/trpc-client';
import ProductsSelector from '../components/ProductsSelector';
interface VendorSnippet {
name: string;
groupIds: number[];
productIds: number[];
validTill?: string;
}
interface SlotFormProps {
onSlotAdded?: () => void;
initialDeliveryTime?: Date | null;
initialFreezeTime?: Date | null;
initialIsActive?: boolean;
slotId?: number;
initialProductIds?: number[];
initialGroupIds?: number[];
}
export default function SlotForm({
onSlotAdded,
initialDeliveryTime,
initialFreezeTime,
initialIsActive = true,
slotId,
initialProductIds = [],
initialGroupIds = [],
}: SlotFormProps) {
const { data: slotData } = trpc.admin.slots.getSlotById.useQuery(
{ id: slotId! },
{ enabled: !!slotId }
);
const vendorSnippetsFromSlot = (slotData?.slot?.vendorSnippets || []).map((snippet: any) => ({
name: snippet.name || '',
groupIds: snippet.groupIds || [],
productIds: snippet.productIds || [],
validTill: snippet.validTill || undefined,
})) as VendorSnippet[];
const initialValues = {
deliveryTime: initialDeliveryTime || (slotData?.slot?.deliveryTime ? new Date(slotData.slot.deliveryTime) : null),
freezeTime: initialFreezeTime || (slotData?.slot?.freezeTime ? new Date(slotData.slot.freezeTime) : null),
selectedGroupIds: initialGroupIds.length > 0 ? initialGroupIds : (slotData?.slot?.groupIds || []),
selectedProductIds: initialProductIds.length > 0 ? initialProductIds : (slotData?.slot?.products?.map((p: any) => p.id) || []),
vendorSnippetList: vendorSnippetsFromSlot,
};
const { mutate: createSlot, isPending: isCreating } = trpc.admin.slots.createSlot.useMutation();
const { mutate: updateSlot, isPending: isUpdating } = trpc.admin.slots.updateSlot.useMutation();
const isEditMode = !!slotId;
const isPending = isCreating || isUpdating;
// Fetch groups
const { data: groupsData } = trpc.admin.product.getGroups.useQuery();
const handleFormSubmit = (values: typeof initialValues) => {
if (!values.deliveryTime || !values.freezeTime) {
Alert.alert('Error', 'Please fill all fields');
return;
}
const slotData = {
deliveryTime: values.deliveryTime.toISOString(),
freezeTime: values.freezeTime.toISOString(),
isActive: initialIsActive,
groupIds: values.selectedGroupIds,
productIds: values.selectedProductIds,
vendorSnippets: values.vendorSnippetList.map((snippet: VendorSnippet) => ({
name: snippet.name,
productIds: snippet.productIds,
validTill: snippet.validTill,
})),
};
if (isEditMode && slotId) {
updateSlot(
{ id: slotId, ...slotData },
{
onSuccess: () => {
Alert.alert('Success', 'Slot updated successfully!');
onSlotAdded?.();
},
onError: (error: any) => {
console.log({msg: JSON.stringify(error.message)})
Alert.alert('Error', error.message || 'Failed to update slot');
},
}
);
} else {
createSlot(
slotData,
{
onSuccess: () => {
Alert.alert('Success', 'Slot created successfully!');
// Reset form
// Formik will handle reset
onSlotAdded?.();
},
onError: (error: any) => {
Alert.alert('Error', error.message || 'Failed to create slot');
},
}
);
}
};
return (
<Formik
initialValues={initialValues}
onSubmit={handleFormSubmit}
>
{({ handleSubmit, values, setFieldValue }) => {
// Map groups data to match ProductsSelector types (convert price from string to number)
const mappedGroups = (groupsData?.groups || []).map(group => ({
...group,
products: group.products.map(product => ({
...product,
price: parseFloat(product.price as unknown as string) || 0,
})),
}));
return (
<View style={tw`mb-4`}>
<Text style={tw`text-xl font-bold mb-6 text-center`}>
{isEditMode ? 'Edit Slot' : 'Create New Slot'}
</Text>
<View style={tw`mb-4`}>
<Text style={tw`text-lg font-semibold mb-2`}>Delivery Date & Time</Text>
<DateTimePickerMod value={values.deliveryTime} setValue={(value) => setFieldValue('deliveryTime', value)} />
</View>
<View style={tw`mb-4`}>
<Text style={tw`text-lg font-semibold mb-2`}>Freeze Date & Time</Text>
<DateTimePickerMod value={values.freezeTime} setValue={(value) => setFieldValue('freezeTime', value)} />
</View>
<View style={tw`mb-4`}>
<ProductsSelector
value={values.selectedProductIds}
onChange={(newProductIds) => setFieldValue('selectedProductIds', newProductIds)}
groups={mappedGroups}
selectedGroupIds={values.selectedGroupIds}
onGroupChange={(newGroupIds) => setFieldValue('selectedGroupIds', newGroupIds)}
label="Select Products"
placeholder="Select products for this slot"
/>
</View>
{/* Vendor Snippets */}
<FieldArray name="vendorSnippetList">
{({ push, remove }) => (
<View style={tw`mb-4`}>
<Text style={tw`text-lg font-semibold mb-4`}>Vendor Snippets</Text>
{values.vendorSnippetList.map((snippet: VendorSnippet, index: number) => (
<View key={index} style={tw`bg-gray-50 p-4 rounded-lg mb-4`}>
<View style={tw`mb-4`}>
<MyTextInput
topLabel="Snippet Name *"
placeholder="Enter snippet name"
value={snippet.name}
onChangeText={(text) => setFieldValue(`vendorSnippetList.${index}.name`, text)}
/>
</View>
<View style={tw`mb-4`}>
<ProductsSelector
value={snippet.productIds || []}
onChange={(newProductIds) => setFieldValue(`vendorSnippetList.${index}.productIds`, newProductIds)}
groups={mappedGroups.filter(group =>
values.selectedGroupIds.includes(group.id)
).map(group => ({
...group,
products: group.products.filter(p => values.selectedProductIds.includes(p.id))
}))}
selectedGroupIds={snippet.groupIds || []}
onGroupChange={(newGroupIds) => setFieldValue(`vendorSnippetList.${index}.groupIds`, newGroupIds)}
label="Select Products"
placeholder="Select products for snippet"
isDisabled={(product) => !values.selectedProductIds.includes(product.id)}
/>
</View>
<TouchableOpacity
onPress={() => remove(index)}
style={tw`bg-red-500 px-4 py-2 rounded-lg self-end`}
>
<Text style={tw`text-white font-medium`}>Remove Snippet</Text>
</TouchableOpacity>
</View>
))}
<TouchableOpacity
onPress={() => push({ name: '', groupIds: [], productIds: [], validTill: '' })}
style={tw`bg-blue-500 px-4 py-3 rounded-lg items-center`}
>
<Text style={tw`text-white font-medium`}>Add Vendor Snippet</Text>
</TouchableOpacity>
</View>
)}
</FieldArray>
<TouchableOpacity
onPress={() => handleSubmit()}
disabled={isPending}
style={tw`${isPending ? 'bg-pink2' : 'bg-pink1'} p-3 rounded-lg items-center mt-6 pb-4`}
>
<Text style={tw`text-white text-base font-bold`}>
{isPending ? (isEditMode ? 'Updating...' : 'Creating...') : (isEditMode ? 'Update Slot' : 'Create Slot')}
</Text>
</TouchableOpacity>
</View>
)}}
</Formik>
);
}