252 lines
7.5 KiB
TypeScript
252 lines
7.5 KiB
TypeScript
import {
|
|
Box,
|
|
Button,
|
|
Card,
|
|
FileInput,
|
|
Group,
|
|
Stack,
|
|
Text,
|
|
TextInput,
|
|
Textarea,
|
|
} from '@mantine/core'
|
|
import { notifications } from '@mantine/notifications'
|
|
import {
|
|
IconDownload,
|
|
IconFileTypeDocx,
|
|
IconFileTypePdf,
|
|
IconFileTypeXls,
|
|
IconPhoto,
|
|
IconSearch,
|
|
IconTrash,
|
|
} from '@tabler/icons-react'
|
|
import { useState } from 'react'
|
|
import classes from './FileUploadForm.module.css'
|
|
|
|
// type TFileProfile = {
|
|
// label: string
|
|
// type: string
|
|
// value: string
|
|
// children?: TFileProfile[]
|
|
// }
|
|
|
|
interface FileData {
|
|
id: number
|
|
name: string
|
|
url: string
|
|
type: string
|
|
description?: string
|
|
created_at: string
|
|
}
|
|
|
|
type FileUploadFormProps = {
|
|
data: FileData[];
|
|
handleSubmit: (e: React.FormEvent, fileName: string, description: string, currentUser: string) => Promise<boolean | void>;
|
|
handleFileChange: (file: File | null) => void;
|
|
removeFile: (id: number) => Promise<void>;
|
|
isLoading: boolean;
|
|
currentUser: string;
|
|
};
|
|
|
|
const FileUploadForm = ({
|
|
data,
|
|
handleSubmit,
|
|
handleFileChange,
|
|
removeFile,
|
|
isLoading,
|
|
currentUser,
|
|
}: FileUploadFormProps) => {
|
|
const [selectedFile, setSelectedFile] = useState<File | null>(null)
|
|
const [fileName, setFileName] = useState('')
|
|
const [description, setDescription] = useState('')
|
|
const [isUploading, setIsUploading] = useState(false)
|
|
const [searchTerm, setSearchTerm] = useState('')
|
|
|
|
const handleFileSelect = (file: File | null) => {
|
|
setSelectedFile(file)
|
|
handleFileChange(file)
|
|
if (file) {
|
|
// Set default name as file name without extension
|
|
setFileName(file.name.split('.')[0])
|
|
}
|
|
}
|
|
|
|
const handleFormSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
setIsUploading(true)
|
|
|
|
try {
|
|
await handleSubmit(e, fileName, description, currentUser)
|
|
notifications.show({
|
|
title: 'Thành công',
|
|
message: 'Tải file lên thành công',
|
|
color: 'green',
|
|
})
|
|
setFileName('')
|
|
setDescription('')
|
|
setSelectedFile(null)
|
|
} catch (error) {
|
|
console.error('Error submitting form:', error)
|
|
notifications.show({
|
|
title: 'Lỗi',
|
|
message: 'Không thể tải file lên',
|
|
color: 'red',
|
|
})
|
|
} finally {
|
|
setIsUploading(false)
|
|
}
|
|
}
|
|
|
|
const getFileIcon = (type: string) => {
|
|
switch (type) {
|
|
case 'document':
|
|
return <IconFileTypeDocx size={16} />
|
|
case 'image':
|
|
return <IconPhoto size={16} />
|
|
case 'spreadsheet':
|
|
return <IconFileTypeXls size={16} />
|
|
default:
|
|
return <IconFileTypePdf size={16} />
|
|
}
|
|
}
|
|
|
|
const filteredFiles = data.filter(
|
|
(file) =>
|
|
file.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
(file.description &&
|
|
file.description.toLowerCase().includes(searchTerm.toLowerCase())),
|
|
)
|
|
|
|
return (
|
|
<>
|
|
{isLoading && (
|
|
<div className={classes.loadingOverlay}>
|
|
<div className={classes.loadingSpinner} />
|
|
</div>
|
|
)}
|
|
|
|
<form onSubmit={handleFormSubmit}>
|
|
<Box>
|
|
<Text className={classes.sectionTitle}>Tài liệu</Text>
|
|
|
|
<Box className={classes.fileInputGroup}>
|
|
<FileInput
|
|
label="Chọn file"
|
|
placeholder="Chọn file để tải lên"
|
|
accept="image/png,image/jpeg,image/jpg,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.oasis.opendocument.spreadsheet"
|
|
onChange={handleFileSelect}
|
|
value={selectedFile}
|
|
className={classes.fileInput}
|
|
/>
|
|
|
|
<TextInput
|
|
label="Tên file"
|
|
placeholder="Nhập tên file"
|
|
value={fileName}
|
|
onChange={(e) => setFileName(e.target.value)}
|
|
className={classes.fileNameInput}
|
|
required
|
|
/>
|
|
|
|
<Textarea
|
|
label="Mô tả"
|
|
placeholder="Nhập mô tả cho file"
|
|
value={description}
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
className={classes.descriptionInput}
|
|
minRows={3}
|
|
/>
|
|
|
|
<Button
|
|
type="submit"
|
|
color="blue"
|
|
className={classes.saveButton}
|
|
disabled={isLoading || isUploading || !selectedFile || !fileName}
|
|
loading={isLoading || isUploading}
|
|
>
|
|
{isLoading || isUploading ? 'Đang xử lý...' : 'Lưu thay đổi'}
|
|
</Button>
|
|
</Box>
|
|
|
|
<Box className={classes.fileListContainer}>
|
|
<TextInput
|
|
placeholder="Tìm kiếm theo tên hoặc mô tả..."
|
|
leftSection={<IconSearch size={14} />}
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
className={classes.searchInput}
|
|
/>
|
|
|
|
<Stack className={classes.fileList} gap="xs">
|
|
{filteredFiles.map((file) => (
|
|
<Card
|
|
key={file.id}
|
|
shadow="xs"
|
|
padding="xs"
|
|
radius="sm"
|
|
withBorder
|
|
>
|
|
<Group justify="space-between" gap="xs" wrap="nowrap">
|
|
<Group gap="xs" className={classes.cardContent}>
|
|
{getFileIcon(file.type)}
|
|
<Box style={{ minWidth: 0 }}>
|
|
<Text size="xs" fw={500} className={classes.cardTitle}>
|
|
{file.name}
|
|
</Text>
|
|
{file.description && (
|
|
<Text
|
|
size="xs"
|
|
c="dimmed"
|
|
className={classes.cardDescription}
|
|
>
|
|
{file.description}
|
|
</Text>
|
|
)}
|
|
<Text size="xs" c="dimmed">
|
|
Uploaded:{' '}
|
|
{new Date(file.created_at).toLocaleDateString()}
|
|
</Text>
|
|
</Box>
|
|
</Group>
|
|
<Group gap="xs" wrap="nowrap">
|
|
<Button
|
|
size="xs"
|
|
variant="light"
|
|
color="blue"
|
|
component="a"
|
|
href={`${import.meta.env.VITE_BACKEND_URL}${
|
|
import.meta.env.VITE_BACKEND_URL?.includes(
|
|
'localhost',
|
|
)
|
|
? ''
|
|
: 'image/'
|
|
}${file.url.slice(1)}`}
|
|
target="_blank"
|
|
>
|
|
<Group gap={2}>
|
|
<IconDownload size={12} />
|
|
</Group>
|
|
</Button>
|
|
<Button
|
|
size="xs"
|
|
variant="light"
|
|
color="red"
|
|
onClick={() => removeFile(file.id)}
|
|
>
|
|
<Group gap={2}>
|
|
<IconTrash size={12} />
|
|
</Group>
|
|
</Button>
|
|
</Group>
|
|
</Group>
|
|
</Card>
|
|
))}
|
|
</Stack>
|
|
</Box>
|
|
</Box>
|
|
</form>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default FileUploadForm
|