ManagementSystem/FRONTEND/src/pages/AllProfiles/AllProfiles.tsx

336 lines
10 KiB
TypeScript

import { getFiles, uploadFiles } from '@/api/Admin'
import { get } from '@/rtk/helpers/apiService'
import { getAccessToken } from '@/rtk/localStorage'
import {
Box,
Button,
Card,
Collapse,
Group,
Modal,
Stack,
Text,
TextInput,
Title,
} from '@mantine/core'
import { notifications } from '@mantine/notifications'
import {
IconChevronDown,
IconDownload,
IconFileTypeDocx,
IconFileTypePdf,
IconFolder,
IconListCheck,
IconPhoto,
IconSearch,
IconTrash,
} from '@tabler/icons-react'
import axios from 'axios'
import { useEffect, useState } from 'react'
import FileUploadForm from '../Profile/components/FileUploadForm'
import classes from './AllProfiles.module.css'
interface FileData {
id: number
name: string
url: string
type: string
description?: string
created_at: string
}
interface GroupedFiles {
[key: string]: FileData[]
}
const AllProfiles = () => {
const [groupedFiles, setGroupedFiles] = useState<GroupedFiles>({})
const [currentUser, setCurrentUser] = useState<string>('')
const [openedProfile, setOpenedProfile] = useState(false)
const [selectedFile, setSelectedFile] = useState<File | null>(null)
const [isLoading, setIsLoading] = useState(false)
const [expandedFolders, setExpandedFolders] = useState<{
[key: string]: boolean
}>({})
const [searchTerms, setSearchTerms] = useState<{ [key: string]: string }>({})
const toggleFolder = (userName: string) => {
setExpandedFolders((prev) => ({
...prev,
[userName]: !prev[userName],
}))
}
const getFileIcon = (type: string) => {
switch (type) {
case 'document':
return <IconFileTypeDocx size={16} />
case 'image':
return <IconPhoto size={16} />
default:
return <IconFileTypePdf size={16} />
}
}
const handleSubmit = async (
e: React.FormEvent,
fileName: string,
description: string,
currentUser: string
) => {
e.preventDefault()
setIsLoading(true)
const formData = new FormData()
if (selectedFile) {
formData.append('file', selectedFile)
formData.append('name', fileName)
formData.append('description', description)
formData.append('user_name', currentUser)
try {
const token = await getAccessToken()
const response = await axios.post(uploadFiles, formData, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${token}`,
},
})
if (response.status === 200) {
setSelectedFile(null)
await getAllFiles()
return true
}
return false
} catch (error) {
console.error('Error uploading file:', error)
throw error
} finally {
setIsLoading(false)
}
}
return false
}
const getAllFiles = async () => {
try {
const res = await get(getFiles)
if (res.status === true) {
setGroupedFiles(res.data)
}
} catch (error) {
console.log(error)
}
}
const removeFile = async (id: number) => {
try {
const token = await getAccessToken();
const response = await axios.delete(`${import.meta.env.VITE_BACKEND_URL}api/v1/admin/profile/files/${id}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (response.status === 200) {
notifications.show({
title: 'Thành công',
message: 'Xóa file thành công',
color: 'green',
});
await getAllFiles();
}
} catch (error) {
console.log(error);
notifications.show({
title: 'Lỗi',
message: 'Không thể xóa file',
color: 'red',
});
}
}
useEffect(() => {
getAllFiles()
}, [])
const filterFiles = (files: FileData[], searchTerm: string) => {
return files.filter(
(file) =>
file.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
(file.description &&
file.description.toLowerCase().includes(searchTerm.toLowerCase())),
)
}
return (
<div>
<div className={classes.title}>
<h3>
<Text size="sm">Admin/</Text>
Files Management
</h3>
</div>
<Box ml={'md'}>
<Stack gap="xs">
{Object.entries(groupedFiles).map(([userName, files]) => (
<Card key={userName} shadow="xs" radius="sm" withBorder p="xs">
<Group
justify="space-between"
mb="xs"
gap="xs"
onClick={() => toggleFolder(userName)}
style={{ cursor: 'pointer' }}
>
<Group gap="xs">
<IconFolder size={18} color="var(--mantine-color-yellow-9)" />
<Title order={5}>{userName}</Title>
</Group>
<Group gap="xs">
<Button
size="xs"
variant="subtle"
color="gray"
onClick={() => toggleFolder(userName)}
leftSection={
<IconChevronDown
size={14}
style={{
transform: expandedFolders[userName]
? 'rotate(180deg)'
: 'none',
transition: 'transform 0.2s',
}}
/>
}
>
{expandedFolders[userName] ? 'Collapse' : 'Expand'}
</Button>
<Button
size="xs"
variant="light"
color="blue"
onClick={() => {
setCurrentUser(userName)
setOpenedProfile(true)
}}
>
<Group gap={2}>
<IconListCheck size={14} />
<Text size="xs">Upload Files</Text>
</Group>
</Button>
</Group>
</Group>
<Collapse in={expandedFolders[userName]}>
<Stack gap="xs">
<TextInput
placeholder="Search files by name or description..."
size="xs"
leftSection={<IconSearch size={14} />}
value={searchTerms[userName] || ''}
onChange={(e) =>
setSearchTerms((prev) => ({
...prev,
[userName]: e.target.value,
}))
}
onClick={(e) => e.stopPropagation()}
/>
{filterFiles(files, searchTerms[userName] || '')
.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
.map((file: FileData) => (
<Card
key={file.id}
shadow="xs"
padding="xs"
radius="sm"
withBorder
>
<Group justify="space-between" gap="xs">
<Group gap="xs">
{getFileIcon(file.type)}
<Box>
<Text size="xs" fw={500}>
{file.name}
</Text>
{file.description && (
<Text size="xs" c="dimmed">
{file.description}
</Text>
)}
<Text size="xs" c="dimmed">
Uploaded:{' '}
{new Date(file.created_at).toLocaleDateString()}
</Text>
</Box>
</Group>
<Group gap="xs">
<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} />
<Text size="xs">Download</Text>
</Group>
</Button>
<Button
size="xs"
variant="light"
color="red"
onClick={() => removeFile(file.id)}
>
<Group gap={2}>
<IconTrash size={12} />
<Text size="xs">Delete</Text>
</Group>
</Button>
</Group>
</Group>
</Card>
),
)}
</Stack>
</Collapse>
</Card>
))}
</Stack>
<Modal
size="lg"
opened={openedProfile}
onClose={() => {
setOpenedProfile(false)
setCurrentUser('')
setSelectedFile(null)
}}
>
<Box>
<FileUploadForm
data={groupedFiles[currentUser] || []}
handleSubmit={handleSubmit}
handleFileChange={(file) => file && setSelectedFile(file)}
removeFile={removeFile}
isLoading={isLoading}
currentUser={currentUser}
/>
</Box>
</Modal>
</Box>
</div>
)
}
export default AllProfiles