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

285 lines
No EOL
11 KiB
TypeScript

import React, { useState, useEffect, useMemo } 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 BottomDropdown, { DropdownOption } from 'common-ui/src/components/bottom-dropdown';
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[];
}
export default function SlotForm({
onSlotAdded,
initialDeliveryTime,
initialFreezeTime,
initialIsActive = true,
slotId,
initialProductIds = [],
}: SlotFormProps) {
const initialValues = {
deliveryTime: initialDeliveryTime || null,
freezeTime: initialFreezeTime || null,
selectedGroupIds: [] as number[],
selectedProductIds: initialProductIds,
vendorSnippetList: [] as VendorSnippet[],
};
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 products and groups
const { data: productsData } = trpc.common.product.getAllProductsSummary.useQuery({});
const { data: groupsData } = trpc.admin.product.getGroups.useQuery();
const products = productsData?.products || [];
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,
productIds: values.selectedProductIds,
vendorSnippets: values.vendorSnippetList.map(snippet => ({
name: snippet.name,
productIds: snippet.productIds,
validTill: snippet.validTill,
})),
};
console.log({snippetList: values.vendorSnippetList})
values.vendorSnippetList.forEach((snippet, index) => {
console.log({snippet})
});
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 }) => {
// Collect all product IDs from selected groups
const allGroupProductIds = (values?.selectedGroupIds || []).flatMap(groupId => {
const group = (groupsData?.groups || []).find(g => g.id === groupId);
return group?.products.map(p => p.id) || [];
});
// Remove duplicates
const groupProductIds = [...new Set(allGroupProductIds)];
const productOptions: DropdownOption[] = products.map(product => ({
label: `${product.name}${groupProductIds.includes(product.id) ? ' (from group)' : ''}`,
value: product.id.toString(),
disabled: groupProductIds.includes(product.id),
}));
const groupOptions: DropdownOption[] = (groupsData?.groups || []).map(group => ({
label: group.groupName,
value: group.id.toString(),
}));
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`}>
<Text style={tw`text-base mb-2`}>Select Product Groups (Optional)</Text>
<BottomDropdown
label="Select Product Groups"
options={groupOptions}
value={values.selectedGroupIds.map(id => id.toString())}
onValueChange={(value) => {
const selectedValues = Array.isArray(value) ? value : typeof value === 'string' ? [value] : [];
const groupIds = selectedValues.map(v => parseInt(v as string));
setFieldValue('selectedGroupIds', groupIds);
// Collect all products from selected groups
const allGroupProducts = groupIds.flatMap(groupId => {
const group = (groupsData?.groups || []).find(g => g.id === groupId);
return group?.products.map(p => p.id) || [];
});
// Remove duplicates
const uniqueProducts = [...new Set(allGroupProducts)];
setFieldValue('selectedProductIds', uniqueProducts);
}}
placeholder="Select product groups"
multiple={true}
/>
</View>
<View style={tw`mb-4`}>
<Text style={tw`text-base mb-2`}>Select Products (Optional)</Text>
<BottomDropdown
label="Select Products"
options={productOptions}
value={values.selectedProductIds.map(id => id.toString())}
onValueChange={(value) => {
const selectedValues = Array.isArray(value) ? value : typeof value === 'string' ? [value] : [];
setFieldValue('selectedProductIds', selectedValues.map(v => Number(v)));
}}
placeholder="Select products for this slot"
multiple={true}
/>
</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, index) => (
<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`}>
<BottomDropdown
label="Select Groups"
options={groupOptions.filter(option =>
values.selectedGroupIds.includes(Number(option.value))
)}
value={snippet.groupIds?.map(id => id.toString()) || []}
onValueChange={(value) => {
const selectedValues = Array.isArray(value) ? value : [value];
const selectedGroupIds = selectedValues.map(v => parseInt(v as string));
setFieldValue(`vendorSnippetList.${index}.groupIds`, selectedGroupIds);
// Auto-populate products from selected groups
const allSnippetProducts = selectedGroupIds.flatMap(groupId => {
const group = (groupsData?.groups || []).find(g => g.id === groupId);
return group?.products.map(p => p.id) || [];
});
// Remove duplicates
const uniqueSnippetProducts = [...new Set(allSnippetProducts)];
setFieldValue(`vendorSnippetList.${index}.productIds`, uniqueSnippetProducts);
}}
placeholder="Select groups for snippet"
multiple={true}
/>
</View>
<View style={tw`mb-4`}>
<BottomDropdown
label="Select Products"
options={productOptions.filter(option =>
values.selectedProductIds.includes(Number(option.value))
)}
value={snippet.productIds?.map(id => id.toString()) || []}
onValueChange={(value) => {
const selectedValues = Array.isArray(value) ? value : [value];
setFieldValue(`vendorSnippetList.${index}.productIds`, selectedValues.map(v => parseInt(v as string)));
}}
placeholder="Select products for snippet"
multiple={true}
/>
</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>
);
}