Tom Butcher 26132d206c
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
Add Payment and Fulfillment Policy Components with SVG Icons
- Introduced new components for managing Payment Policies and Fulfillment Policies, enhancing the dashboard's functionality for financial management.
- Added corresponding SVG icons for Payment Policy, Fulfillment Policy, and Return Policy to improve visual representation.
- Updated App.css with new styles for file gallery previews, ensuring a cohesive layout and user experience across the dashboard.
- Implemented new object forms and tables for Payment Policies, allowing for better data handling and user interaction.
2026-08-29 00:47:46 +01:00

241 lines
6.4 KiB
JavaScript

import { Card, Flex, Button, Tag, Divider } from 'antd'
import PropTypes from 'prop-types'
import FileIcon from '../../Icons/FileIcon'
import BinIcon from '../../Icons/BinIcon'
import EyeIcon from '../../Icons/EyeIcon'
import DownloadIcon from '../../Icons/DownloadIcon'
import { useContext, useState } from 'react'
import { ApiServerContext } from '../context/ApiServerContext'
import FilePreview from './FilePreview'
import FileGalleryList from './FileGalleryList'
import EyeSlashIcon from '../../Icons/EyeSlashIcon'
import { getModelByName } from '../../../database/ObjectModels'
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import { useNavigate } from 'react-router-dom'
import ElipsisText from './ElipsisText'
import MissingPlaceholder from './MissingPlaceholder'
const FileList = ({
files,
onChange,
multiple = true,
editing = false,
showPreview = true,
showInfo = true,
showDownload = true,
defaultPreviewOpen = false,
minimal = false,
card = true,
gallery = false,
uploadSlot = null,
maxWidth = '100%'
}) => {
const { fetchFileContent, flushFile } = useContext(ApiServerContext)
const navigate = useNavigate()
const [previewOpen, setPreviewOpen] = useState(defaultPreviewOpen)
const infoAction = getModelByName('file').actions.filter(
(action) => action.name == 'info'
)[0]
// Check if there are no items in the list
const hasNoItems = multiple
? !files || !Array.isArray(files) || files.length === 0
: !files
const handleRemove = (fileToRemove) => {
flushFile(fileToRemove._id)
if (multiple) {
const currentFiles = Array.isArray(files) ? files : []
const updatedFiles = currentFiles.filter((file) => {
const fileUid = file._id || file.id
const removeUid = fileToRemove._id || fileToRemove.id
return fileUid !== removeUid
})
onChange(updatedFiles)
} else {
onChange(null)
}
}
const handleDownload = async (file) => {
await fetchFileContent(file, true)
}
const filesToRender = multiple
? Array.isArray(files)
? files
: []
: files
? [files]
: []
const rootStyle = {
minWidth: 0,
maxWidth,
...(card
? { width: '100%' }
: { width: 'fit-content', alignSelf: 'flex-start' })
}
if (gallery && !minimal) {
if (hasNoItems && !(gallery && (editing || uploadSlot))) {
return <MissingPlaceholder message='No listing images.' />
}
return (
<div style={rootStyle}>
<FileGalleryList
files={filesToRender}
editing={editing}
uploadSlot={uploadSlot}
onRemove={handleRemove}
/>
</div>
)
}
if (hasNoItems && !(gallery && (editing || uploadSlot))) {
return null
}
const renderFileContent = (file) => (
<Flex
vertical
gap='10px'
style={{
maxWidth: '100%',
minWidth: 0,
width: card ? '100%' : 'fit-content'
}}
>
<Flex
justify={card ? 'space-between' : 'start'}
align='center'
gap='small'
wrap={false}
style={{
maxWidth: '100%',
minWidth: 0,
width: card ? '100%' : 'fit-content'
}}
>
<Flex
gap={'small'}
align='center'
style={{ minWidth: 0, flex: '1 1 auto', overflow: 'hidden' }}
>
<FileIcon
style={{ margin: 0, fontSize: card == true ? '24px' : '16px' }}
/>
<ElipsisText style={{ marginTop: '1px', minWidth: 0 }}>
{file.name || file.filename || 'Unknown file'}
</ElipsisText>
{file.extension && <Tag style={{ margin: 0 }}>{file.extension}</Tag>}
</Flex>
{!minimal && (
<Flex gap={'small'} align='center'>
{showDownload && (
<Button
icon={<DownloadIcon />}
size='small'
type='text'
onClick={() => handleDownload(file)}
/>
)}
{showPreview && (
<Button
icon={previewOpen ? <EyeSlashIcon /> : <EyeIcon />}
size='small'
type='text'
onClick={() => {
if (previewOpen == true) {
setPreviewOpen(false)
} else {
setPreviewOpen(true)
}
}}
/>
)}
{showInfo && (
<Button
icon={<InfoCircleIcon />}
size='small'
type='text'
onClick={() => {
navigate(infoAction.url(file._id))
}}
/>
)}
{editing && (
<Button
icon={<BinIcon />}
size='small'
type='text'
onClick={() => handleRemove(file)}
/>
)}
</Flex>
)}
</Flex>
{previewOpen && !minimal ? (
<>
<Divider style={{ marginTop: 0, marginBottom: card ? 0 : '4px' }} />
<FilePreview file={file} style={{ width: '100%' }} />
</>
) : null}
</Flex>
)
return (
<div style={rootStyle}>
{filesToRender.map((file, index) => {
const key = file._id || file.id || index
const style = {
marginTop: index > 0 && multiple ? '4px' : undefined
}
if (card) {
return (
<Card
styles={{ body: { padding: '10px' } }}
key={key}
style={style}
>
{renderFileContent(file)}
</Card>
)
} else {
return (
<div
key={key}
style={{
...style
}}
>
{index > 0 && <Divider />}
{renderFileContent(file)}
</div>
)
}
})}
</div>
)
}
FileList.propTypes = {
files: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
onChange: PropTypes.func,
multiple: PropTypes.bool,
editing: PropTypes.bool,
showPreview: PropTypes.string,
showInfo: PropTypes.bool,
showDownload: PropTypes.bool,
defaultPreviewOpen: PropTypes.bool,
card: PropTypes.bool,
gallery: PropTypes.bool,
uploadSlot: PropTypes.node,
minimal: PropTypes.bool,
maxWidth: PropTypes.string
}
export default FileList