Tom Butcher fb9454d8e0 Refactor Stock Transfers and Management Components
- Adjusted width of Stock Transfers modal for better UI consistency.
- Enhanced New Stock Transfer form with default transfer name generation and improved validation handling.
- Added Product Categories to Management Sidebar and updated routing for better navigation.
- Implemented delete functionality in Host Info component.
- Improved User Info layout for mobile responsiveness and added ObjectProperty component for better data display.
- Updated FileUpload and FileList components to support minimal display mode.
- Enhanced ObjectTable with subscription filtering and improved rendering logic.
- Added new properties and filters to Stock Transfer and Product models for better data management.
2026-06-14 23:51:45 +01:00

216 lines
5.8 KiB
JavaScript

import { Upload, Button, Flex, Typography, Space, Progress, Card } from 'antd'
import PropTypes from 'prop-types'
import { ApiServerContext } from '../context/ApiServerContext'
import UploadIcon from '../../Icons/UploadIcon'
import {
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState
} from 'react'
import ObjectSelect from './ObjectSelect'
import FileList from './FileList'
import PlusIcon from '../../Icons/PlusIcon'
import { LoadingOutlined } from '@ant-design/icons'
import FileIcon from '../../Icons/FileIcon'
const { Text } = Typography
const getFileIdentity = (value, multiple) => {
if (multiple) {
return Array.isArray(value)
? value.map((file) => file?._id || file).join(',')
: ''
}
return value?._id || value || ''
}
const FileUpload = ({
value,
onChange,
minimal = false,
multiple = true,
defaultPreviewOpen = false,
showPreview = true,
showInfo
}) => {
const { uploadFile } = useContext(ApiServerContext)
const [uploading, setUploading] = useState(false)
const [uploadProgress, setUploadProgress] = useState(0)
const onChangeRef = useRef(onChange)
useEffect(() => {
onChangeRef.current = onChange
}, [onChange])
// Track current files using useState
const [currentFiles, setCurrentFiles] = useState(() => {
if (multiple) {
return Array.isArray(value) ? value : []
} else {
return value || null
}
})
// Update currentFiles when value prop changes
useEffect(() => {
setCurrentFiles((prev) => {
if (
getFileIdentity(prev, multiple) === getFileIdentity(value, multiple)
) {
return prev
}
return multiple ? (Array.isArray(value) ? value : []) : value || null
})
}, [value, multiple])
// Track the selected file from ObjectSelect
const [selectedFile, setSelectedFile] = useState(null)
const hasNoItems = useMemo(
() =>
multiple
? !currentFiles ||
!Array.isArray(currentFiles) ||
currentFiles.length === 0
: !currentFiles,
[currentFiles, multiple]
)
const updateCurrentFiles = useCallback((nextFiles) => {
setCurrentFiles(nextFiles)
onChangeRef.current?.(nextFiles)
}, [])
const handleFileUpload = async (file) => {
try {
setUploading(true)
const uploadedFile = await uploadFile(file, {}, (progress) => {
setUploadProgress(progress)
})
setUploading(false)
if (uploadedFile) {
if (multiple) {
// For multiple files, add to existing array
const newFiles = [...currentFiles, uploadedFile]
updateCurrentFiles(newFiles)
} else {
// For single file, replace the value
updateCurrentFiles(uploadedFile)
}
}
} catch (error) {
console.error('File upload failed:', error)
}
return false // Prevent default upload behavior
}
// Handle adding selected file to the list
const handleAddSelectedFile = () => {
if (selectedFile) {
if (multiple) {
// For multiple files, add to existing array
const newFiles = [...currentFiles, selectedFile]
updateCurrentFiles(newFiles)
} else {
// For single file, replace the value
updateCurrentFiles(selectedFile)
}
// Clear the selection
setSelectedFile(null)
}
}
if (minimal == true) {
return (
<Flex gap={'small'} vertical>
<FileIcon file={currentFiles[0]} style={{ fontSize: '24px' }} />
<Text>{currentFiles[0]?.name}</Text>
</Flex>
)
}
return (
<Flex gap={'small'} vertical>
{hasNoItems && uploading == false ? (
<Flex gap={'small'} align='center' wrap>
<Space.Compact style={{ flexGrow: 1 }}>
<ObjectSelect
type={'file'}
value={selectedFile}
onChange={setSelectedFile}
/>
<Button
icon={<PlusIcon />}
onClick={handleAddSelectedFile}
disabled={!selectedFile}
/>
</Space.Compact>
<Text style={{ whiteSpace: 'nowrap' }} type='secondary'>
or
</Text>
<Upload
beforeUpload={handleFileUpload}
showUploadList={false}
multiple={multiple}
>
<Button style={{ width: '100%' }} icon={<UploadIcon />}>
Upload
</Button>
</Upload>
</Flex>
) : null}
{uploading == true ? (
<Card styles={{ body: { padding: '10px 15px' } }}>
<Flex gap={'small'} align='center'>
<Text>Uploading...</Text>
{uploadProgress > 0 ? (
<>
{uploadProgress >= 0 && uploadProgress < 100 ? (
<>
<Progress
percent={uploadProgress}
showInfo={false}
style={{ width: '100px', flexGrow: 1 }}
status='active'
/>
<Text>{uploadProgress}%</Text>
</>
) : null}
{uploadProgress == 100 ? <LoadingOutlined /> : null}
</>
) : null}
</Flex>
</Card>
) : null}
<FileList
files={currentFiles}
multiple={multiple}
editing={true}
showInfo={showInfo || false}
showPreview={showPreview}
defaultPreviewOpen={defaultPreviewOpen}
onChange={(updatedFiles) => {
updateCurrentFiles(updatedFiles)
}}
/>
</Flex>
)
}
FileUpload.propTypes = {
value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
onChange: PropTypes.func,
multiple: PropTypes.bool,
showPreview: PropTypes.bool,
showInfo: PropTypes.bool,
defaultPreviewOpen: PropTypes.bool,
minimal: PropTypes.bool
}
export default FileUpload