285 lines
No EOL
11 KiB
TypeScript
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>
|
|
);
|
|
} |