Create Vendor
This commit is contained in:
parent
8026004630
commit
3a74e32e64
52
src/services/mutations/vendor.ts
Normal file
52
src/services/mutations/vendor.ts
Normal 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 }
|
||||
}
|
||||
115
src/views/apps/vendor/list/AddVendorDrawer.tsx
vendored
115
src/views/apps/vendor/list/AddVendorDrawer.tsx
vendored
@ -18,40 +18,12 @@ import { useForm, Controller } from 'react-hook-form'
|
||||
|
||||
// Component Imports
|
||||
import CustomTextField from '@core/components/mui/TextField'
|
||||
|
||||
// Backend Types
|
||||
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
|
||||
}
|
||||
import { VendorRequest } from '@/types/services/vendor'
|
||||
import { useVendorsMutation } from '@/services/mutations/vendor'
|
||||
|
||||
type Props = {
|
||||
open: boolean
|
||||
handleClose: () => void
|
||||
onSubmit?: (vendorRequest: VendorRequest) => Promise<void>
|
||||
}
|
||||
|
||||
type FormValidateType = {
|
||||
@ -81,12 +53,14 @@ const initialData: FormValidateType = {
|
||||
|
||||
const AddVendorDrawer = (props: Props) => {
|
||||
// Props
|
||||
const { open, handleClose, onSubmit } = props
|
||||
const { open, handleClose } = props
|
||||
|
||||
// States
|
||||
const [showMore, setShowMore] = useState(false)
|
||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||
|
||||
const { createVendor, updateVendor } = useVendorsMutation()
|
||||
|
||||
// Hooks
|
||||
const {
|
||||
control,
|
||||
@ -114,29 +88,12 @@ const AddVendorDrawer = (props: Props) => {
|
||||
is_active: data.is_active
|
||||
}
|
||||
|
||||
// Call the onSubmit prop if provided (for API call)
|
||||
if (onSubmit) {
|
||||
await onSubmit(vendorRequest)
|
||||
} else {
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
createVendor.mutate(vendorRequest, {
|
||||
onSuccess: () => {
|
||||
handleReset()
|
||||
handleClose()
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('Error submitting vendor:', error)
|
||||
// Handle error (show toast, etc.)
|
||||
@ -214,12 +171,13 @@ const AddVendorDrawer = (props: Props) => {
|
||||
{/* Email */}
|
||||
<div>
|
||||
<Typography variant='body2' className='mb-2'>
|
||||
Email
|
||||
Email <span className='text-red-500'>*</span>
|
||||
</Typography>
|
||||
<Controller
|
||||
name='email'
|
||||
control={control}
|
||||
rules={{
|
||||
required: 'Email wajib diisi',
|
||||
pattern: {
|
||||
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
|
||||
message: 'Format email tidak valid'
|
||||
@ -241,24 +199,35 @@ const AddVendorDrawer = (props: Props) => {
|
||||
{/* Nomor Telepon */}
|
||||
<div>
|
||||
<Typography variant='body2' className='mb-2'>
|
||||
Nomor Telepon
|
||||
Nomor Telepon <span className='text-red-500'>*</span>
|
||||
</Typography>
|
||||
<Controller
|
||||
name='phone_number'
|
||||
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>
|
||||
|
||||
{/* Contact Person */}
|
||||
<div>
|
||||
<Typography variant='body2' className='mb-2'>
|
||||
Contact Person
|
||||
Contact Person <span className='text-red-500'>*</span>
|
||||
</Typography>
|
||||
<Controller
|
||||
name='contact_person'
|
||||
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>
|
||||
|
||||
@ -278,12 +247,15 @@ const AddVendorDrawer = (props: Props) => {
|
||||
|
||||
{/* Tampilkan selengkapnya */}
|
||||
{!showMore && (
|
||||
<div className='flex items-center gap-3 cursor-pointer' onClick={() => setShowMore(true)}>
|
||||
<i className='tabler-plus text-blue-500' />
|
||||
<Typography variant='body1' color='primary'>
|
||||
Tampilkan selengkapnya
|
||||
</Typography>
|
||||
</div>
|
||||
<Button
|
||||
variant='text'
|
||||
color='primary'
|
||||
size='small'
|
||||
sx={{ textTransform: 'none', fontSize: '14px', p: 0, textAlign: 'left', width: '200px' }}
|
||||
onClick={() => setShowMore(true)}
|
||||
>
|
||||
+ Tampilkan selengkapnya
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Konten tambahan */}
|
||||
@ -358,12 +330,15 @@ const AddVendorDrawer = (props: Props) => {
|
||||
</div>
|
||||
|
||||
{/* Sembunyikan */}
|
||||
<div className='flex items-center gap-3 cursor-pointer' onClick={() => setShowMore(false)}>
|
||||
<i className='tabler-minus text-blue-500' />
|
||||
<Typography variant='body1' color='primary'>
|
||||
Sembunyikan
|
||||
</Typography>
|
||||
</div>
|
||||
<Button
|
||||
variant='text'
|
||||
color='primary'
|
||||
size='small'
|
||||
sx={{ textTransform: 'none', fontSize: '14px', p: 0, textAlign: 'left', width: '200px' }}
|
||||
onClick={() => setShowMore(false)}
|
||||
>
|
||||
- Sembunyikan
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -235,7 +235,6 @@ const VendorListTable = () => {
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<CardHeader title='Filter' className='pbe-4' />
|
||||
{/* <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'>
|
||||
<CustomTextField
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user