freshyo/apps/admin-ui/components/AvailabilityScheduleForm.tsx
2026-03-20 00:39:48 +05:30

237 lines
8 KiB
TypeScript

import React, { useState } from 'react';
import { View, TouchableOpacity, Alert, ScrollView } from 'react-native';
import { useFormik } from 'formik';
import { MyText, tw, MyTextInput, MyTouchableOpacity, DateTimePickerMod } from 'common-ui';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import ProductsSelector from './ProductsSelector';
import { trpc } from '../src/trpc-client';
interface AvailabilityScheduleFormProps {
onClose: () => void;
onSuccess: () => void;
initialProductIds?: number[];
initialGroupIds?: number[];
}
const AvailabilityScheduleForm: React.FC<AvailabilityScheduleFormProps> = ({
onClose,
onSuccess,
initialProductIds,
initialGroupIds,
}) => {
const createSchedule = trpc.admin.productAvailabilitySchedules.create.useMutation();
const { data: groupsData } = trpc.admin.product.getGroups.useQuery();
// Map groups data to match ProductsSelector types (convert price from string to number)
const groups = (groupsData?.groups || []).map(group => ({
...group,
products: group.products.map(product => ({
...product,
price: parseFloat(product.price as unknown as string) || 0,
})),
}));
const formik = useFormik({
initialValues: {
scheduleName: '',
timeDate: null as Date | null,
action: 'in' as 'in' | 'out',
productIds: initialProductIds || ([] as number[]),
groupIds: initialGroupIds || ([] as number[]),
},
validate: (values) => {
const errors: {[key: string]: string} = {};
if (!values.scheduleName.trim()) {
errors.scheduleName = 'Schedule name is required';
}
if (!values.timeDate) {
errors.timeDate = 'Time is required';
}
if (!values.action) {
errors.action = 'Action is required';
}
if (values.productIds.length === 0) {
errors.productIds = 'At least one product must be selected';
}
return errors;
},
onSubmit: async (values) => {
try {
// Convert Date to HH:MM string
const hours = values.timeDate!.getHours().toString().padStart(2, '0');
const minutes = values.timeDate!.getMinutes().toString().padStart(2, '0');
const timeString = `${hours}:${minutes}`;
await createSchedule.mutateAsync({
scheduleName: values.scheduleName,
time: timeString,
action: values.action,
productIds: values.productIds,
groupIds: values.groupIds,
});
Alert.alert('Success', 'Schedule created successfully');
onSuccess();
onClose();
} catch (error: any) {
Alert.alert('Error', error.message || 'Failed to create schedule');
}
},
});
const actionOptions = [
{ label: 'In Stock', value: 'in' },
{ label: 'Out of Stock', value: 'out' },
];
return (
<View style={tw`flex-1 bg-white`}>
{/* Header */}
<View style={tw`flex-row items-center justify-between p-4 border-b border-gray-200 bg-white`}>
<MyText style={tw`text-xl font-bold text-gray-900`}>
Create Availability Schedule
</MyText>
<MyTouchableOpacity onPress={onClose}>
<MaterialIcons name="close" size={24} color="#6B7280" />
</MyTouchableOpacity>
</View>
<ScrollView style={tw`flex-1 p-4`} showsVerticalScrollIndicator={false}>
{/* Schedule Name */}
<View style={tw`mb-4`}>
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
Schedule Name
</MyText>
<MyTextInput
placeholder="Enter schedule name"
value={formik.values.scheduleName}
onChangeText={formik.handleChange('scheduleName')}
onBlur={formik.handleBlur('scheduleName')}
style={tw`border rounded-lg p-3 ${
formik.touched.scheduleName && formik.errors.scheduleName
? 'border-red-500'
: 'border-gray-300'
}`}
/>
{formik.touched.scheduleName && formik.errors.scheduleName && (
<MyText style={tw`text-red-500 text-xs mt-1`}>
{formik.errors.scheduleName}
</MyText>
)}
</View>
{/* Time */}
<View style={tw`mb-4`}>
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
Time
</MyText>
<DateTimePickerMod
value={formik.values.timeDate}
setValue={(date) => formik.setFieldValue('timeDate', date)}
timeOnly={true}
showLabels={false}
/>
{formik.touched.timeDate && formik.errors.timeDate && (
<MyText style={tw`text-red-500 text-xs mt-1`}>
{formik.errors.timeDate}
</MyText>
)}
</View>
{/* Action */}
<View style={tw`mb-4`}>
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
Action
</MyText>
<View style={tw`flex-row gap-3`}>
{actionOptions.map((option) => (
<TouchableOpacity
key={option.value}
onPress={() => formik.setFieldValue('action', option.value)}
style={tw`flex-1 flex-row items-center p-4 rounded-lg border ${
formik.values.action === option.value
? 'bg-blue-50 border-blue-500'
: 'bg-white border-gray-300'
}`}
>
<View
style={tw`w-5 h-5 rounded-full border-2 mr-3 items-center justify-center ${
formik.values.action === option.value
? 'border-blue-500'
: 'border-gray-300'
}`}
>
{formik.values.action === option.value && (
<View style={tw`w-3 h-3 rounded-full bg-blue-500`} />
)}
</View>
<MyText
style={tw`font-medium ${
formik.values.action === option.value
? 'text-blue-700'
: 'text-gray-700'
}`}
>
{option.label}
</MyText>
</TouchableOpacity>
))}
</View>
</View>
{/* Products and Groups */}
<View style={tw`mb-4`}>
<MyText style={tw`text-sm font-medium text-gray-700 mb-2`}>
Products & Groups
</MyText>
<ProductsSelector
value={formik.values.productIds}
onChange={(value) => formik.setFieldValue('productIds', value)}
groups={groups}
selectedGroupIds={formik.values.groupIds}
onGroupChange={(groupIds) => formik.setFieldValue('groupIds', groupIds)}
showGroups={true}
label="Select Products"
placeholder="Select products for this schedule"
/>
{formik.touched.productIds && formik.errors.productIds && (
<MyText style={tw`text-red-500 text-xs mt-1`}>
{formik.errors.productIds}
</MyText>
)}
</View>
{/* Spacer for bottom padding */}
<View style={tw`h-24`} />
</ScrollView>
{/* Footer Buttons */}
<View style={tw`p-4 border-t border-gray-200 bg-white flex-row gap-3`}>
<MyTouchableOpacity
onPress={onClose}
style={tw`flex-1 py-3 px-4 rounded-lg border border-gray-300 items-center`}
>
<MyText style={tw`font-medium text-gray-700`}>Cancel</MyText>
</MyTouchableOpacity>
<MyTouchableOpacity
onPress={() => formik.handleSubmit()}
disabled={formik.isSubmitting}
style={tw`flex-1 py-3 px-4 rounded-lg bg-blue-600 items-center ${
formik.isSubmitting ? 'opacity-50' : ''
}`}
>
<MyText style={tw`font-medium text-white`}>
{formik.isSubmitting ? 'Creating...' : 'Create Schedule'}
</MyText>
</MyTouchableOpacity>
</View>
</View>
);
};
export default AvailabilityScheduleForm;