Create Vendor

This commit is contained in:
efrilm 2025-09-12 13:52:11 +07:00
parent 8026004630
commit 3a74e32e64
3 changed files with 97 additions and 71 deletions

View File

@ -0,0 +1,52 @@
import { VendorRequest } from '@/types/services/vendor'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'react-toastify'
import { api } from '../api'
export const useVendorsMutation = () => {
const queryClient = useQueryClient()
const createVendor = useMutation({
mutationFn: async (newVendor: VendorRequest) => {
const response = await api.post('/vendors', newVendor)
return response.data
},
onSuccess: () => {
toast.success('Vendor created successfully!')
queryClient.invalidateQueries({ queryKey: ['vendors'] })
},
onError: (error: any) => {
toast.error(error.response?.data?.errors?.[0]?.cause || 'Create failed')
}
})
const updateVendor = useMutation({
mutationFn: async ({ id, payload }: { id: string; payload: VendorRequest }) => {
const response = await api.put(`/vendors/${id}`, payload)
return response.data
},
onSuccess: () => {
toast.success('Vendor updated successfully!')
queryClient.invalidateQueries({ queryKey: ['vendors'] })
},
onError: (error: any) => {
toast.error(error.response?.data?.errors?.[0]?.cause || 'Update failed')
}
})
const deleteVendor = useMutation({
mutationFn: async (id: string) => {
const response = await api.delete(`/vendors/${id}`)
return response.data
},
onSuccess: () => {
toast.success('Vendor deleted successfully!')
queryClient.invalidateQueries({ queryKey: ['vendors'] })
},
onError: (error: any) => {
toast.error(error.response?.data?.errors?.[0]?.cause || 'Delete failed')
}
})
return { createVendor, updateVendor, deleteVendor }
}

View File

@ -18,40 +18,12 @@ import { useForm, Controller } from 'react-hook-form'
// Component Imports // Component Imports
import CustomTextField from '@core/components/mui/TextField' import CustomTextField from '@core/components/mui/TextField'
import { VendorRequest } from '@/types/services/vendor'
// Backend Types import { useVendorsMutation } from '@/services/mutations/vendor'
export interface VendorRequest {
name: string
email?: string
phone_number?: string
address?: string
contact_person?: string
tax_number?: string
payment_terms?: string
notes?: string
is_active: boolean
}
export interface Vendor {
id: string
organization_id: string
name: string
email?: string
phone_number?: string
address?: string
contact_person?: string
tax_number?: string
payment_terms?: string
notes?: string
is_active: boolean
created_at: string
updated_at: string
}
type Props = { type Props = {
open: boolean open: boolean
handleClose: () => void handleClose: () => void
onSubmit?: (vendorRequest: VendorRequest) => Promise<void>
} }
type FormValidateType = { type FormValidateType = {
@ -81,12 +53,14 @@ const initialData: FormValidateType = {
const AddVendorDrawer = (props: Props) => { const AddVendorDrawer = (props: Props) => {
// Props // Props
const { open, handleClose, onSubmit } = props const { open, handleClose } = props
// States // States
const [showMore, setShowMore] = useState(false) const [showMore, setShowMore] = useState(false)
const [isSubmitting, setIsSubmitting] = useState(false) const [isSubmitting, setIsSubmitting] = useState(false)
const { createVendor, updateVendor } = useVendorsMutation()
// Hooks // Hooks
const { const {
control, control,
@ -114,29 +88,12 @@ const AddVendorDrawer = (props: Props) => {
is_active: data.is_active is_active: data.is_active
} }
// Call the onSubmit prop if provided (for API call) createVendor.mutate(vendorRequest, {
if (onSubmit) { onSuccess: () => {
await onSubmit(vendorRequest) handleReset()
} else { handleClose()
// Fallback: Create local vendor object for local state update
const newVendor: Vendor = {
id: `temp-${Date.now()}`, // Temporary ID
organization_id: 'current-org', // Should be provided by context
name: vendorRequest.name,
email: vendorRequest.email,
phone_number: vendorRequest.phone_number,
address: vendorRequest.address,
contact_person: vendorRequest.contact_person,
tax_number: vendorRequest.tax_number,
payment_terms: vendorRequest.payment_terms,
notes: vendorRequest.notes,
is_active: vendorRequest.is_active,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString()
} }
} })
handleReset()
} catch (error) { } catch (error) {
console.error('Error submitting vendor:', error) console.error('Error submitting vendor:', error)
// Handle error (show toast, etc.) // Handle error (show toast, etc.)
@ -214,12 +171,13 @@ const AddVendorDrawer = (props: Props) => {
{/* Email */} {/* Email */}
<div> <div>
<Typography variant='body2' className='mb-2'> <Typography variant='body2' className='mb-2'>
Email Email <span className='text-red-500'>*</span>
</Typography> </Typography>
<Controller <Controller
name='email' name='email'
control={control} control={control}
rules={{ rules={{
required: 'Email wajib diisi',
pattern: { pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i, value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
message: 'Format email tidak valid' message: 'Format email tidak valid'
@ -241,24 +199,35 @@ const AddVendorDrawer = (props: Props) => {
{/* Nomor Telepon */} {/* Nomor Telepon */}
<div> <div>
<Typography variant='body2' className='mb-2'> <Typography variant='body2' className='mb-2'>
Nomor Telepon Nomor Telepon <span className='text-red-500'>*</span>
</Typography> </Typography>
<Controller <Controller
name='phone_number' name='phone_number'
control={control} control={control}
render={({ field }) => <CustomTextField {...field} fullWidth placeholder='Nomor telepon vendor' />} rules={{ required: 'Telepon wajib diisi' }}
render={({ field }) => (
<CustomTextField {...field} fullWidth placeholder='Harus Diawali 62' error={!!errors.phone_number} />
)}
/> />
</div> </div>
{/* Contact Person */} {/* Contact Person */}
<div> <div>
<Typography variant='body2' className='mb-2'> <Typography variant='body2' className='mb-2'>
Contact Person Contact Person <span className='text-red-500'>*</span>
</Typography> </Typography>
<Controller <Controller
name='contact_person' name='contact_person'
control={control} control={control}
render={({ field }) => <CustomTextField {...field} fullWidth placeholder='Nama contact person' />} rules={{ required: 'Contact Person wajib diisi' }}
render={({ field }) => (
<CustomTextField
{...field}
fullWidth
placeholder='Nama contact person'
error={!!errors.contact_person}
/>
)}
/> />
</div> </div>
@ -278,12 +247,15 @@ const AddVendorDrawer = (props: Props) => {
{/* Tampilkan selengkapnya */} {/* Tampilkan selengkapnya */}
{!showMore && ( {!showMore && (
<div className='flex items-center gap-3 cursor-pointer' onClick={() => setShowMore(true)}> <Button
<i className='tabler-plus text-blue-500' /> variant='text'
<Typography variant='body1' color='primary'> color='primary'
Tampilkan selengkapnya size='small'
</Typography> sx={{ textTransform: 'none', fontSize: '14px', p: 0, textAlign: 'left', width: '200px' }}
</div> onClick={() => setShowMore(true)}
>
+ Tampilkan selengkapnya
</Button>
)} )}
{/* Konten tambahan */} {/* Konten tambahan */}
@ -358,12 +330,15 @@ const AddVendorDrawer = (props: Props) => {
</div> </div>
{/* Sembunyikan */} {/* Sembunyikan */}
<div className='flex items-center gap-3 cursor-pointer' onClick={() => setShowMore(false)}> <Button
<i className='tabler-minus text-blue-500' /> variant='text'
<Typography variant='body1' color='primary'> color='primary'
Sembunyikan size='small'
</Typography> sx={{ textTransform: 'none', fontSize: '14px', p: 0, textAlign: 'left', width: '200px' }}
</div> onClick={() => setShowMore(false)}
>
- Sembunyikan
</Button>
</> </>
)} )}
</div> </div>

View File

@ -235,7 +235,6 @@ const VendorListTable = () => {
return ( return (
<> <>
<Card> <Card>
<CardHeader title='Filter' className='pbe-4' />
{/* <TableFilters setData={setFilteredData} tableData={data} /> */} {/* <TableFilters setData={setFilteredData} tableData={data} /> */}
<div className='flex justify-between flex-col items-start md:flex-row md:items-center p-6 border-bs gap-4'> <div className='flex justify-between flex-col items-start md:flex-row md:items-center p-6 border-bs gap-4'>
<CustomTextField <CustomTextField