374 lines
11 KiB
JavaScript
374 lines
11 KiB
JavaScript
import React, { useState, useEffect } from 'react'
|
|
import { useLocation } from 'react-router-dom'
|
|
import axios from 'axios'
|
|
import {
|
|
Descriptions,
|
|
Spin,
|
|
Space,
|
|
Button,
|
|
message,
|
|
Typography,
|
|
Flex,
|
|
Form,
|
|
Input,
|
|
Collapse
|
|
} from 'antd'
|
|
import {
|
|
LoadingOutlined,
|
|
ExportOutlined,
|
|
CaretRightOutlined
|
|
} from '@ant-design/icons'
|
|
import IdText from '../../common/IdText'
|
|
import CountrySelect from '../../common/CountrySelect'
|
|
import CountryDisplay from '../../common/CountryDisplay'
|
|
import TimeDisplay from '../../common/TimeDisplay'
|
|
import ReloadIcon from '../../../Icons/ReloadIcon'
|
|
import EditIcon from '../../../Icons/EditIcon.jsx'
|
|
import XMarkIcon from '../../../Icons/XMarkIcon.jsx'
|
|
import CheckIcon from '../../../Icons/CheckIcon.jsx'
|
|
import useCollapseState from '../../hooks/useCollapseState'
|
|
|
|
import config from '../../../../config.js'
|
|
|
|
const { Title, Link } = Typography
|
|
|
|
const VendorInfo = () => {
|
|
const [vendorData, setVendorData] = useState(null)
|
|
const [loading, setLoading] = useState(false)
|
|
const [error, setError] = useState(null)
|
|
const location = useLocation()
|
|
const [messageApi, contextHolder] = message.useMessage()
|
|
const vendorId = new URLSearchParams(location.search).get('vendorId')
|
|
const [isEditing, setIsEditing] = useState(false)
|
|
const [form] = Form.useForm()
|
|
const [fetchLoading, setFetchLoading] = useState(true)
|
|
const [collapseState, updateCollapseState] = useCollapseState('VendorInfo', {
|
|
info: true
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (vendorId) {
|
|
fetchVendorDetails()
|
|
}
|
|
}, [vendorId])
|
|
|
|
useEffect(() => {
|
|
if (vendorData) {
|
|
form.setFieldsValue({
|
|
name: vendorData.name || '',
|
|
website: vendorData.website || '',
|
|
contact: vendorData.contact || '',
|
|
country: vendorData.country || '',
|
|
phone: vendorData.phone || '',
|
|
email: vendorData.email || ''
|
|
})
|
|
}
|
|
}, [vendorData, form])
|
|
|
|
const fetchVendorDetails = async () => {
|
|
try {
|
|
setFetchLoading(true)
|
|
const response = await axios.get(
|
|
`${config.backendUrl}/vendors/${vendorId}`,
|
|
{
|
|
headers: {
|
|
Accept: 'application/json'
|
|
},
|
|
withCredentials: true
|
|
}
|
|
)
|
|
setVendorData(response.data)
|
|
setError(null)
|
|
} catch (err) {
|
|
setError('Failed to fetch vendor details')
|
|
messageApi.error('Failed to fetch vendor details')
|
|
} finally {
|
|
setFetchLoading(false)
|
|
}
|
|
}
|
|
|
|
const startEditing = () => {
|
|
updateCollapseState('info', true)
|
|
setIsEditing(true)
|
|
}
|
|
|
|
const cancelEditing = () => {
|
|
form.setFieldsValue({
|
|
name: vendorData?.name || '',
|
|
website: vendorData?.website || '',
|
|
contact: vendorData?.contact || '',
|
|
country: vendorData?.country || '',
|
|
phone: vendorData?.phone || '',
|
|
email: vendorData?.email || ''
|
|
})
|
|
setIsEditing(false)
|
|
}
|
|
|
|
const updateInfo = async () => {
|
|
try {
|
|
const values = await form.validateFields()
|
|
setLoading(true)
|
|
|
|
await axios.put(`${config.backendUrl}/vendors/${vendorId}`, values, {
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
withCredentials: true
|
|
})
|
|
|
|
setVendorData({ ...vendorData, ...values })
|
|
setIsEditing(false)
|
|
messageApi.success('Vendor information updated successfully')
|
|
} catch (err) {
|
|
if (err.errorFields) {
|
|
return
|
|
}
|
|
console.error('Failed to update vendor information:', err)
|
|
messageApi.error('Failed to update vendor information')
|
|
} finally {
|
|
fetchVendorDetails()
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
if (fetchLoading) {
|
|
return (
|
|
<div style={{ textAlign: 'center', padding: '20px' }}>
|
|
<Spin indicator={<LoadingOutlined style={{ fontSize: 24 }} spin />} />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (error || !vendorData) {
|
|
return (
|
|
<Space
|
|
direction='vertical'
|
|
style={{ width: '100%', textAlign: 'center' }}
|
|
>
|
|
<p>{error || 'Vendor not found'}</p>
|
|
<Button icon={<ReloadIcon />} onClick={fetchVendorDetails}>
|
|
Retry
|
|
</Button>
|
|
</Space>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div style={{ height: '100%', minHeight: 0, overflowY: 'auto' }}>
|
|
{contextHolder}
|
|
<Flex vertical gap={'large'}>
|
|
<Collapse
|
|
ghost
|
|
collapsible='icon'
|
|
activeKey={collapseState.info ? ['1'] : []}
|
|
onChange={(keys) => updateCollapseState('info', keys.length > 0)}
|
|
expandIcon={({ isActive }) => (
|
|
<CaretRightOutlined
|
|
rotate={isActive ? 90 : 0}
|
|
style={{ paddingTop: '9px' }}
|
|
/>
|
|
)}
|
|
className='no-h-padding-collapse no-t-padding-collapse'
|
|
>
|
|
<Collapse.Panel
|
|
header={
|
|
<Flex
|
|
align='center'
|
|
justify='space-between'
|
|
style={{ width: '100%' }}
|
|
>
|
|
<Title level={5} style={{ margin: 0 }}>
|
|
Vendor Information
|
|
</Title>
|
|
<Space>
|
|
{isEditing ? (
|
|
<>
|
|
<Button
|
|
icon={<CheckIcon />}
|
|
type='primary'
|
|
onClick={updateInfo}
|
|
loading={loading}
|
|
/>
|
|
<Button
|
|
icon={<XMarkIcon />}
|
|
onClick={cancelEditing}
|
|
disabled={loading}
|
|
/>
|
|
</>
|
|
) : (
|
|
<Button icon={<EditIcon />} onClick={startEditing} />
|
|
)}
|
|
</Space>
|
|
</Flex>
|
|
}
|
|
key='1'
|
|
>
|
|
<Form form={form} layout='vertical'>
|
|
<Descriptions
|
|
bordered
|
|
column={{
|
|
xs: 1,
|
|
sm: 1,
|
|
md: 1,
|
|
lg: 2,
|
|
xl: 2,
|
|
xxl: 2
|
|
}}
|
|
>
|
|
<Descriptions.Item label='ID'>
|
|
<IdText id={vendorData._id} type='vendor' />
|
|
</Descriptions.Item>
|
|
<Descriptions.Item label='Created At'>
|
|
<TimeDisplay
|
|
dateTime={vendorData.createdAt}
|
|
showSince={true}
|
|
/>
|
|
</Descriptions.Item>
|
|
|
|
<Descriptions.Item label='Name'>
|
|
{isEditing ? (
|
|
<Form.Item
|
|
name='name'
|
|
rules={[
|
|
{
|
|
required: true,
|
|
message: 'Please enter a vendor name'
|
|
},
|
|
{
|
|
max: 100,
|
|
message: 'Name cannot exceed 100 characters'
|
|
}
|
|
]}
|
|
style={{ margin: 0 }}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
) : (
|
|
vendorData.name
|
|
)}
|
|
</Descriptions.Item>
|
|
|
|
<Descriptions.Item label='Updated At'>
|
|
<TimeDisplay
|
|
dateTime={vendorData.updatedAt}
|
|
showSince={true}
|
|
/>
|
|
</Descriptions.Item>
|
|
|
|
<Descriptions.Item label='Website'>
|
|
{isEditing ? (
|
|
<Form.Item
|
|
name='website'
|
|
rules={[
|
|
{ type: 'url', message: 'Please enter a valid URL' },
|
|
{
|
|
max: 200,
|
|
message: 'Website URL cannot exceed 200 characters'
|
|
}
|
|
]}
|
|
style={{ margin: 0 }}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
) : vendorData.website ? (
|
|
<Link
|
|
href={vendorData.website}
|
|
target='_blank'
|
|
rel='noopener noreferrer'
|
|
>
|
|
{new URL(vendorData.website).hostname + ' '}
|
|
<ExportOutlined />
|
|
</Link>
|
|
) : (
|
|
'n/a'
|
|
)}
|
|
</Descriptions.Item>
|
|
|
|
<Descriptions.Item label='Country'>
|
|
{isEditing ? (
|
|
<Form.Item name='country' style={{ margin: 0 }}>
|
|
<CountrySelect countryCode={vendorData.country} />
|
|
</Form.Item>
|
|
) : vendorData.country ? (
|
|
<CountryDisplay countryCode={vendorData.country} />
|
|
) : (
|
|
'n/a'
|
|
)}
|
|
</Descriptions.Item>
|
|
|
|
<Descriptions.Item label='Contact'>
|
|
{isEditing ? (
|
|
<Form.Item
|
|
name='contact'
|
|
rules={[
|
|
{
|
|
max: 200,
|
|
message: 'Contact info cannot exceed 200 characters'
|
|
}
|
|
]}
|
|
style={{ margin: 0 }}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
) : vendorData.contact ? (
|
|
vendorData.contact
|
|
) : (
|
|
'n/a'
|
|
)}
|
|
</Descriptions.Item>
|
|
|
|
<Descriptions.Item label='Phone'>
|
|
{isEditing ? (
|
|
<Form.Item
|
|
name='phone'
|
|
rules={[
|
|
{
|
|
type: 'phone',
|
|
message: 'Please enter a valid phone number'
|
|
}
|
|
]}
|
|
style={{ margin: 0 }}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
) : vendorData.phone ? (
|
|
vendorData.phone
|
|
) : (
|
|
'n/a'
|
|
)}
|
|
</Descriptions.Item>
|
|
|
|
<Descriptions.Item label='Email'>
|
|
{isEditing ? (
|
|
<Form.Item
|
|
name='email'
|
|
rules={[
|
|
{
|
|
type: 'email',
|
|
message: 'Please enter a valid email'
|
|
}
|
|
]}
|
|
style={{ margin: 0 }}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
) : vendorData.email ? (
|
|
<Link href={`mailto:${vendorData.email}`}>
|
|
{vendorData.email + ' '}
|
|
<ExportOutlined />
|
|
</Link>
|
|
) : (
|
|
'n/a'
|
|
)}
|
|
</Descriptions.Item>
|
|
</Descriptions>
|
|
</Form>
|
|
</Collapse.Panel>
|
|
</Collapse>
|
|
</Flex>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default VendorInfo
|