fix show file document detail, update document
This commit is contained in:
parent
0c5f56d30c
commit
e026b70e66
|
|
@ -94,6 +94,7 @@ class DocumentController extends Controller
|
|||
'uri.*' => 'nullable|url',
|
||||
'files' => 'nullable|array',
|
||||
'files.*' => 'file|mimes:doc,docx,xls,xlsx,pdf|max:20480',
|
||||
'existing_files' => 'nullable|array',
|
||||
'is_active' => 'required|boolean',
|
||||
]);
|
||||
|
||||
|
|
@ -104,14 +105,17 @@ class DocumentController extends Controller
|
|||
|
||||
if ($request->type === "file") {
|
||||
$existingFiles = explode(',', $document->uri);
|
||||
foreach ($existingFiles as $file) {
|
||||
$selectedExistingFiles = $request->existing_files ?? [];
|
||||
|
||||
$filesToDelete = array_diff($existingFiles, $selectedExistingFiles);
|
||||
foreach ($filesToDelete as $file) {
|
||||
$filePath = str_replace('storage/', 'public/', $file);
|
||||
if (Storage::exists($filePath)) {
|
||||
Storage::delete($filePath);
|
||||
}
|
||||
}
|
||||
|
||||
$uploadedFiles = [];
|
||||
$uploadedFiles = $selectedExistingFiles;
|
||||
if ($request->hasFile('files')) {
|
||||
foreach ($request->file('files') as $file) {
|
||||
$path = $file->store('uploads', 'public');
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use App\Http\Middleware\CheckPermission;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Modules\Admin\app\Http\Controllers\AdminController;
|
||||
use Modules\Admin\app\Http\Controllers\BannerController;
|
||||
use Modules\Admin\app\Http\Controllers\CategoryController;
|
||||
|
|
@ -224,6 +225,23 @@ Route::middleware('api')
|
|||
Route::post('/update', [DocumentController::class, 'update'])->middleware('check.permission:admin');
|
||||
Route::get('/delete', [DocumentController::class, 'delete'])->middleware('check.permission:admin');
|
||||
});
|
||||
|
||||
Route::get('/download-file/{filename}', function ($filename) {
|
||||
$path = "uploads/{$filename}";
|
||||
|
||||
if (!Storage::disk('public')->exists($path)) {
|
||||
return response()->json(['error' => 'File not found'], 404);
|
||||
}
|
||||
|
||||
$headers = [
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Methods' => 'GET',
|
||||
'Access-Control-Allow-Headers' => 'Content-Type',
|
||||
'Content-Disposition' => 'inline; filename="' . $filename . '"',
|
||||
];
|
||||
|
||||
return response()->file(storage_path("app/public/{$path}"), $headers);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -34,6 +34,7 @@
|
|||
"react": "^18.2.0",
|
||||
"react-doc-viewer": "^0.1.14",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-file-viewer": "^1.2.1",
|
||||
"react-redux": "^8.1.3",
|
||||
"react-router-dom": "^6.19.0",
|
||||
"reactstrap": "^9.2.2",
|
||||
|
|
|
|||
|
|
@ -116,3 +116,6 @@ export const listDocument = API_URL + 'v1/admin/document/all'
|
|||
export const createDocument = API_URL + 'v1/admin/document/create'
|
||||
export const updateDocument = API_URL + 'v1/admin/document/update'
|
||||
export const deleteDocument = API_URL + 'v1/admin/document/delete'
|
||||
|
||||
// Download File
|
||||
export const downloadFile = API_URL + 'v1/admin/download-file'
|
||||
|
|
|
|||
|
|
@ -3,18 +3,19 @@ import { get } from '@/rtk/helpers/apiService'
|
|||
import { deleteDocument, listDocument } from '@/api/Admin'
|
||||
import { Xdelete } from '@/rtk/helpers/CRUD'
|
||||
|
||||
import { Anchor, Box, Button, Dialog, Group, Loader, Text } from '@mantine/core'
|
||||
import {
|
||||
Anchor,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
Group,
|
||||
Loader,
|
||||
Text,
|
||||
} from '@mantine/core'
|
||||
import { useDisclosure } from '@mantine/hooks'
|
||||
import { notifications } from '@mantine/notifications'
|
||||
import {
|
||||
IconEdit,
|
||||
IconFileInfo,
|
||||
IconFileTypeDoc,
|
||||
IconFileTypePdf,
|
||||
IconFileTypeXls,
|
||||
IconFileUnknown,
|
||||
IconX,
|
||||
} from '@tabler/icons-react'
|
||||
import { IconEdit, IconX } from '@tabler/icons-react'
|
||||
import DataTableAll from '@/components/DataTable/DataTable'
|
||||
import ModalAddEditDocument from './ModalAddEditDocument'
|
||||
import ModalFileDocument from './ModalFileDocument'
|
||||
|
|
@ -91,35 +92,6 @@ const Document = () => {
|
|||
}
|
||||
}
|
||||
|
||||
const getFileTypeIcon = (url: string) => {
|
||||
if (!url) return null
|
||||
|
||||
try {
|
||||
const parsedUrl = new URL(url)
|
||||
const hostname = parsedUrl.hostname
|
||||
const pathname = parsedUrl.pathname.toLowerCase()
|
||||
|
||||
if (
|
||||
hostname.includes('docs.google.com') ||
|
||||
hostname.includes('drive.google.com')
|
||||
) {
|
||||
if (pathname.includes('/document/')) {
|
||||
return <IconFileTypeDoc style={{ color: '#1e62c1' }} />
|
||||
}
|
||||
if (pathname.includes('/spreadsheets/')) {
|
||||
return <IconFileTypeXls style={{ color: '#0e864b' }} />
|
||||
}
|
||||
return <IconFileTypePdf style={{ color: '#ff1b0e' }} />
|
||||
}
|
||||
|
||||
return <IconFileUnknown style={{ color: '#000' }} />
|
||||
} catch (error) {
|
||||
console.error('Invalid URL:', url)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'id',
|
||||
|
|
@ -131,7 +103,7 @@ const Document = () => {
|
|||
},
|
||||
{
|
||||
name: 'title',
|
||||
size: '40%',
|
||||
size: '30%',
|
||||
header: 'Title',
|
||||
render: (row: TDocument) => {
|
||||
return <Text ta="start">{row?.title}</Text>
|
||||
|
|
@ -139,24 +111,30 @@ const Document = () => {
|
|||
},
|
||||
{
|
||||
name: 'uri',
|
||||
size: '50%',
|
||||
size: '60%',
|
||||
header: 'URI',
|
||||
render: (row: TDocument) => {
|
||||
return (
|
||||
<Box>
|
||||
{row.type === 'file' ? (
|
||||
<Text
|
||||
style={{ cursor: 'pointer' }}
|
||||
ta="start"
|
||||
onClick={() => {
|
||||
setSelectDataRow(row)
|
||||
openModalFile()
|
||||
}}
|
||||
>
|
||||
<IconFileInfo style={{ color: '#ffa500' }} />
|
||||
</Text>
|
||||
<Group gap={5}>
|
||||
{row?.uri &&
|
||||
row?.uri.split(',')?.map((uriItem) => (
|
||||
<Badge
|
||||
style={{ cursor: 'pointer' }}
|
||||
tt="initial"
|
||||
onClick={() => {
|
||||
setSelectDataRow({ ...row, uri: uriItem })
|
||||
openModalFile()
|
||||
}}
|
||||
color="orange"
|
||||
>
|
||||
{uriItem.replace('storage/uploads/', '')}
|
||||
</Badge>
|
||||
))}
|
||||
</Group>
|
||||
) : (
|
||||
<Group>
|
||||
<Group gap={5}>
|
||||
{row?.uri &&
|
||||
row?.uri.split(',')?.map((uriItem) => (
|
||||
<Anchor
|
||||
|
|
@ -165,7 +143,9 @@ const Document = () => {
|
|||
target="_blank"
|
||||
title={uriItem}
|
||||
>
|
||||
{getFileTypeIcon(uriItem)}
|
||||
<Badge style={{ cursor: 'pointer' }} tt="initial">
|
||||
{uriItem}
|
||||
</Badge>
|
||||
</Anchor>
|
||||
))}
|
||||
</Group>
|
||||
|
|
@ -225,6 +205,18 @@ const Document = () => {
|
|||
</div>
|
||||
|
||||
<Box mt={'md'}>
|
||||
<Group wrap="wrap" mb={'md'} px={16}>
|
||||
<Text fw={500}>Note: </Text>
|
||||
|
||||
<Badge style={{ cursor: 'pointer' }} tt="initial">
|
||||
Links
|
||||
</Badge>
|
||||
|
||||
<Badge style={{ cursor: 'pointer' }} tt="initial" color="orange">
|
||||
Files
|
||||
</Badge>
|
||||
</Group>
|
||||
|
||||
{loader ? (
|
||||
<Box ta={'center'}>
|
||||
<Loader size={40} mt={'15%'} />
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
Checkbox,
|
||||
FileInput,
|
||||
TagsInput,
|
||||
Group,
|
||||
} from '@mantine/core'
|
||||
|
||||
import { create, update } from '@/rtk/helpers/CRUD'
|
||||
|
|
@ -117,7 +118,7 @@ const ModalAddEditDocument = ({
|
|||
const header = await getHeaderInfo()
|
||||
|
||||
if (data.type) {
|
||||
if (data.files.length < 1) {
|
||||
if (data.files.length < 1 && data.uri.length < 1) {
|
||||
notifications.show({
|
||||
title: 'Error',
|
||||
message: 'Upload at least 1 file',
|
||||
|
|
@ -132,11 +133,15 @@ const ModalAddEditDocument = ({
|
|||
|
||||
tmpFormData.append('title', data.title)
|
||||
tmpFormData.append('type', data.type ? 'file' : 'link')
|
||||
tmpFormData.append('is_active', data.is_active)
|
||||
tmpFormData.append('is_active', data.is_active ? '1' : '0')
|
||||
for (let i = 0; i < data.files.length; i++) {
|
||||
tmpFormData.append('files[]', data.files[i])
|
||||
}
|
||||
|
||||
data.uri.forEach((fileUri: string) => {
|
||||
tmpFormData.append('existing_files[]', fileUri)
|
||||
})
|
||||
|
||||
formdata = tmpFormData
|
||||
} else {
|
||||
const { files, ...rest } = data
|
||||
|
|
@ -230,6 +235,32 @@ const ModalAddEditDocument = ({
|
|||
disabled={loadingSubmit}
|
||||
required
|
||||
/>
|
||||
|
||||
{selectDataRow?.uri && form.values.uri.length > 0 && (
|
||||
<Box>
|
||||
<Text fw={500}>Existing Files:</Text>
|
||||
{form.values.uri.map((fileUri: string, index) => (
|
||||
<Group key={index} justify="space-between" mb="sm">
|
||||
<Text size="sm">
|
||||
{fileUri.replace('storage/uploads/', '')}
|
||||
</Text>
|
||||
<Button
|
||||
color="red"
|
||||
size="xs"
|
||||
ml="md"
|
||||
onClick={() =>
|
||||
form.setFieldValue(
|
||||
'uri',
|
||||
form.values.uri.filter((_, i) => i !== index),
|
||||
)
|
||||
}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</Group>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
) : (
|
||||
<Box>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import { Modal, Text, Box } from '@mantine/core'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { getDownloadFile } from '@/rtk/helpers/apiService'
|
||||
import { Modal, Text, Box, Loader, Paper, Button, Group } from '@mantine/core'
|
||||
import FileViewer from 'react-file-viewer'
|
||||
import { IconDownload } from '@tabler/icons-react'
|
||||
import { downloadFile } from '@/api/Admin'
|
||||
|
||||
type MProps = {
|
||||
opened: boolean
|
||||
|
|
@ -7,17 +12,48 @@ type MProps = {
|
|||
setSelectDataRow: any
|
||||
}
|
||||
|
||||
interface TDocumentFile {
|
||||
uri: string
|
||||
fileType: string
|
||||
}
|
||||
|
||||
const ModalFileDocument = ({
|
||||
opened,
|
||||
close,
|
||||
selectDataRow,
|
||||
setSelectDataRow,
|
||||
}: MProps) => {
|
||||
// const supportedFileTypes = ['pdf', 'xls', 'xlsx', 'docx', 'doc']
|
||||
// const getFileType = (fileName: string) => {
|
||||
// const extension = fileName.split('.').pop()?.toLowerCase()
|
||||
// return supportedFileTypes.includes(extension!) ? extension : 'default'
|
||||
// }
|
||||
const [fileDoc, setFileDoc] = useState<TDocumentFile>()
|
||||
const [loader, setLoader] = useState<boolean>(false)
|
||||
|
||||
useEffect(() => {
|
||||
getFile()
|
||||
}, [])
|
||||
|
||||
const getFile = async () => {
|
||||
try {
|
||||
setLoader(true)
|
||||
const params = {}
|
||||
const fileUri = selectDataRow?.uri.replace('storage/uploads/', '')
|
||||
const res = await getDownloadFile(`${downloadFile}/${fileUri}`, params)
|
||||
|
||||
setFileDoc({
|
||||
uri: URL.createObjectURL(res.data),
|
||||
fileType: getFileType(selectDataRow?.uri) || 'default',
|
||||
})
|
||||
} catch (error: any) {
|
||||
console.log(error)
|
||||
} finally {
|
||||
setLoader(false)
|
||||
}
|
||||
}
|
||||
|
||||
const supportedFileTypes = ['pdf', 'xlsx', 'xls', 'docx', 'doc']
|
||||
const getFileType = (fileName: string) => {
|
||||
const extension = fileName.split('.').pop()?.toLowerCase()
|
||||
|
||||
return supportedFileTypes.includes(extension!) ? extension : 'default'
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
|
|
@ -26,34 +62,48 @@ const ModalFileDocument = ({
|
|||
close()
|
||||
setSelectDataRow({})
|
||||
}}
|
||||
size="xl"
|
||||
size="65%"
|
||||
title={
|
||||
<Text fw={700} fz={'lg'}>
|
||||
{selectDataRow?.title}
|
||||
File Detail: {selectDataRow?.title}
|
||||
</Text>
|
||||
}
|
||||
>
|
||||
<Box>
|
||||
{selectDataRow?.uri?.split(',').map((uriItem: string) => {
|
||||
let url = import.meta.env.VITE_BACKEND_URL + uriItem
|
||||
const extension = url.split('.').pop()?.toLowerCase()
|
||||
|
||||
if (extension === 'doc' || extension === 'docx') {
|
||||
const docUrl = encodeURIComponent(uriItem)
|
||||
url = `https://view.officeapps.live.com/op/embed.aspx?src=${docUrl}`
|
||||
<Group justify="flex-end" mb={'md'}>
|
||||
<a
|
||||
href={`${import.meta.env.VITE_BACKEND_URL}${selectDataRow.uri}`}
|
||||
download="Document Download"
|
||||
target={
|
||||
getFileType(selectDataRow?.uri) === 'pdf' ? '_blank' : '_self'
|
||||
}
|
||||
>
|
||||
<Button
|
||||
leftSection={<IconDownload size={18} />}
|
||||
color={
|
||||
getFileType(selectDataRow?.uri) === 'pdf'
|
||||
? 'red'
|
||||
: getFileType(selectDataRow?.uri) === 'xlsx' ||
|
||||
getFileType(selectDataRow?.uri) === 'xls'
|
||||
? 'green'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
Download .{getFileType(selectDataRow?.uri)}
|
||||
</Button>
|
||||
</a>
|
||||
</Group>
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<iframe
|
||||
key={uriItem}
|
||||
src={url}
|
||||
style={{ width: '100%', height: '800px', border: 'none' }}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
<Paper withBorder>
|
||||
{loader ? (
|
||||
<Box ta={'center'} my={20}>
|
||||
<Loader size={40} />
|
||||
</Box>
|
||||
) : (
|
||||
<Box w="100%">
|
||||
<FileViewer fileType={fileDoc?.fileType} filePath={fileDoc?.uri} />
|
||||
</Box>
|
||||
)}
|
||||
</Paper>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue