freshyo/apps/admin-web/src/components/VendorSnippetForm.tsx
2026-09-06 23:05:20 +05:30

225 lines
7.8 KiB
TypeScript

import { useEffect, type FC } from 'react'
import { useFormik } from 'formik'
import { p as P, Dropdown, pInput as PInput, Checkbox } from 'web-components'
import ProductsSelector from './ProductsSelector'
import DateInput from './DateInput'
import { trpc } from '@/lib/trpc-client'
import type { VendorSnippetForm as VendorSnippetFormType } from '@/types/vendor-snippets'
interface VendorSnippetFormProps {
snippet?: VendorSnippetFormType | null
onClose: () => void
onSuccess: () => void
showIsPermanentCheckbox?: boolean
}
const VendorSnippetForm: FC<VendorSnippetFormProps> = ({
snippet,
onClose,
onSuccess,
showIsPermanentCheckbox = false,
}) => {
// Fetch slots
const { data: slotsData } = trpc.user.slots.getSlots.useQuery()
const createSnippet = trpc.admin.vendorSnippets.create.useMutation()
const updateSnippet = trpc.admin.vendorSnippets.update.useMutation()
const isEditing = !!snippet
const formik = useFormik({
initialValues: {
snippetCode: snippet?.snippetCode || '',
slotId: snippet?.slotId?.toString() || '',
isPermanent: snippet?.isPermanent || false,
skuIds: snippet?.skuIds?.map(id => id.toString()) || [],
validTill: snippet?.validTill ? new Date(snippet.validTill) : null,
},
validate: (values) => {
const errors: {[key: string]: string} = {}
if (!values.snippetCode.trim()) {
errors.snippetCode = 'Snippet code is required'
}
// Validate snippet code only contains alphanumeric characters and underscore
const snippetCodeRegex = /^[a-zA-Z0-9_]+$/
if (values.snippetCode && !snippetCodeRegex.test(values.snippetCode)) {
errors.snippetCode = 'Snippet code can only contain letters, numbers, and underscores'
}
// Only require slotId if isPermanent is false
if (!values.isPermanent && !values.slotId) {
errors.slotId = 'Slot selection is required'
}
if (values.skuIds.length === 0) {
errors.skuIds = 'At least one product must be selected'
}
return errors
},
onSubmit: async (values) => {
try {
const submitData = {
snippetCode: values.snippetCode,
slotId: values.isPermanent ? undefined : parseInt(values.slotId || '0'),
isPermanent: values.isPermanent,
skuIds: values.skuIds.map(id => parseInt(id)),
validTill: values.validTill ? values.validTill.toISOString() : undefined,
}
if (isEditing && snippet) {
await updateSnippet.mutateAsync({
id: snippet.id,
updates: submitData,
})
window.alert('Success: Vendor snippet updated successfully')
} else {
await createSnippet.mutateAsync(submitData)
window.alert('Success: Vendor snippet created successfully')
}
onSuccess()
onClose()
} catch (error: any) {
window.alert(`Error: ${error.message || 'Failed to save vendor snippet'}`)
}
},
})
// Generate unique snippet code if creating new (only on mount)
useEffect(() => {
if (!isEditing && !formik.values.snippetCode) {
const timestamp = Date.now()
const random = Math.random().toString(36).substring(2, 8)
formik.setFieldValue('snippetCode', `VS_${timestamp}_${random}`)
}
}, [isEditing]) // Removed formik.values.snippetCode from deps
const slotOptions = slotsData?.slots.map(slot => ({
label: new Date(slot.deliveryTime).toLocaleString(),
value: slot.id.toString(),
})) || []
return (
<div className="flex-1 bg-white">
<div className="px-6 py-4 border-b border-gray-200">
<div className="flex flex-row justify-between items-center">
<P className="text-xl font-bold text-gray-800">
{isEditing ? 'Edit Vendor Snippet' : 'Create Vendor Snippet'}
</P>
<button
type="button"
onClick={onClose}
className="p-2 text-gray-500 text-lg"
aria-label="Close"
>
</button>
</div>
</div>
<div className="flex-1 overflow-auto px-6">
<div className="py-6 space-y-6">
{/* Snippet Code */}
<div>
<PInput
topLabel="Snippet Code"
value={formik.values.snippetCode}
onChange={formik.handleChange('snippetCode')}
placeholder="Enter snippet code"
error={!!formik.errors.snippetCode && !!formik.touched.snippetCode}
/>
{formik.errors.snippetCode && formik.touched.snippetCode && (
<P className="text-red-500 text-sm mt-1">{formik.errors.snippetCode}</P>
)}
</div>
{/* Is Permanent Checkbox */}
<div>
<button
type="button"
onClick={() => formik.setFieldValue('isPermanent', !formik.values.isPermanent)}
className="flex flex-row items-center mb-2"
>
<Checkbox
checked={formik.values.isPermanent}
onPress={() => formik.setFieldValue('isPermanent', !formik.values.isPermanent)}
/>
<P className="text-gray-700 font-medium ml-3">Is Permanent?</P>
</button>
<P className="text-sm text-gray-500 block">
Check if this snippet is permanent and not tied to a specific delivery slot
</P>
</div>
{/* Slot Selection - Only show if not permanent */}
{!formik.values.isPermanent && (
<div>
<P className="text-gray-700 font-medium mb-2 block">Delivery Slot</P>
<Dropdown
label="Select Slot"
value={formik.values.slotId}
options={slotOptions}
onValueChange={(value) => formik.setFieldValue('slotId', String(value))}
className="w-full"
/>
{formik.errors.slotId && formik.touched.slotId && (
<P className="text-red-500 text-sm mt-1">{formik.errors.slotId}</P>
)}
</div>
)}
{/* Product Selection */}
<div>
<ProductsSelector
value={formik.values.skuIds.map(id => parseInt(id))}
onChange={(selectedProductIds) => formik.setFieldValue('skuIds', (selectedProductIds as number[]).map(id => id.toString()))}
multiple={true}
label="Select Products"
placeholder="Select products"
labelFormat={(product) => product.label}
/>
{formik.errors.skuIds && formik.touched.skuIds && (
<P className="text-red-500 text-sm mt-1">{formik.errors.skuIds}</P>
)}
</div>
{/* Valid Till Date */}
<div>
<P className="text-gray-700 font-medium mb-2 block">Valid Till (Optional)</P>
<DateInput
value={formik.values.validTill}
setValue={(date) => formik.setFieldValue('validTill', date)}
placeholder="Select expiry date"
showLabel={false}
/>
<P className="text-sm text-gray-500 mt-1 block">
Leave empty for no expiry
</P>
</div>
</div>
</div>
{/* Submit Button */}
<div className="px-6 py-4 border-t border-gray-200">
<button
type="button"
onClick={() => formik.handleSubmit()}
disabled={formik.isSubmitting}
className={`bg-blue-500 py-3 rounded-lg w-full text-white text-center font-semibold disabled:opacity-50 ${formik.isSubmitting ? 'opacity-50' : ''}`}
>
{formik.isSubmitting
? 'Saving...'
: isEditing ? 'Update Snippet' : 'Create Snippet'
}
</button>
</div>
</div>
)
}
export default VendorSnippetForm