494 lines
21 KiB
JavaScript
494 lines
21 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import { useParams, useNavigate } from 'react-router-dom';
|
|
import { Card, Button, Typography, Spin, Alert, Space } from 'antd';
|
|
import { NotifAlert } from '../../../components/Global/ToastNotif';
|
|
import { ArrowLeftOutlined, FilePdfOutlined, FileImageOutlined, DownloadOutlined } from '@ant-design/icons';
|
|
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
|
|
import { getBrandById } from '../../../api/master-brand';
|
|
import {
|
|
downloadFile,
|
|
getFile,
|
|
getFileUrl,
|
|
getFolderFromFileType,
|
|
} from '../../../api/file-uploads';
|
|
|
|
const { Title } = Typography;
|
|
|
|
const ViewFilePage = () => {
|
|
const params = useParams();
|
|
const { id, fileType, fileName } = params;
|
|
const navigate = useNavigate();
|
|
const { setBreadcrumbItems } = useBreadcrumb();
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState(null);
|
|
const [brandData, setBrandData] = useState(null);
|
|
const [actualFileName, setActualFileName] = useState('');
|
|
const [pdfBlobUrl, setPdfBlobUrl] = useState(null);
|
|
const [pdfLoading, setPdfLoading] = useState(false);
|
|
|
|
// Debug: Log URL parameters and location
|
|
const isFromEdit = window.location.pathname.includes('/edit/');
|
|
console.log('ViewFilePage URL Parameters:', {
|
|
id,
|
|
fileType,
|
|
fileName,
|
|
allParams: params,
|
|
windowLocation: window.location.pathname,
|
|
urlParts: window.location.pathname.split('/'),
|
|
isFromEdit
|
|
});
|
|
|
|
let fallbackId = id;
|
|
let fallbackFileType = fileType;
|
|
let fallbackFileName = fileName;
|
|
|
|
if (!fileName || !fileType || !id) {
|
|
|
|
const urlParts = window.location.pathname.split('/');
|
|
// console.log('URL Parts from pathname:', urlParts);
|
|
|
|
const viewIndex = urlParts.indexOf('view');
|
|
const editIndex = urlParts.indexOf('edit');
|
|
const actionIndex = viewIndex !== -1 ? viewIndex : editIndex;
|
|
|
|
if (actionIndex !== -1 && urlParts.length > actionIndex + 4) {
|
|
fallbackId = urlParts[actionIndex + 1];
|
|
fallbackFileType = urlParts[actionIndex + 3];
|
|
fallbackFileName = decodeURIComponent(urlParts[actionIndex + 4]);
|
|
|
|
console.log('Fallback extraction:', {
|
|
fallbackId,
|
|
fallbackFileType,
|
|
fallbackFileName,
|
|
actionType: viewIndex !== -1 ? 'view' : 'edit'
|
|
});
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
setPdfBlobUrl(null);
|
|
setPdfLoading(false);
|
|
setError(null);
|
|
|
|
const fetchData = async () => {
|
|
const token = localStorage.getItem('token');
|
|
if (!token) {
|
|
navigate('/signin');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const actualId = fallbackId || id;
|
|
const actualFileName = fallbackFileName || fileName;
|
|
|
|
const brandResponse = await getBrandById(actualId);
|
|
if (brandResponse && brandResponse.statusCode === 200) {
|
|
setBrandData(brandResponse.data);
|
|
}
|
|
|
|
const decodedFileName = decodeURIComponent(actualFileName);
|
|
setActualFileName(decodedFileName);
|
|
|
|
const fileExtension = decodedFileName.split('.').pop().toLowerCase();
|
|
if (fileExtension === 'pdf') {
|
|
setPdfLoading(true);
|
|
const folder = getFolderFromFileType('pdf');
|
|
try {
|
|
const blobData = await getFile(folder, decodedFileName);
|
|
console.log('PDF blob data received:', blobData);
|
|
const blobUrl = window.URL.createObjectURL(blobData);
|
|
setPdfBlobUrl(blobUrl);
|
|
console.log('PDF blob URL created successfully:', blobUrl);
|
|
} catch (pdfError) {
|
|
console.error('Error loading PDF:', pdfError);
|
|
setError('Failed to load PDF file: ' + (pdfError.message || pdfError));
|
|
setPdfBlobUrl(null);
|
|
} finally {
|
|
setPdfLoading(false);
|
|
}
|
|
}
|
|
|
|
setLoading(false);
|
|
} catch (error) {
|
|
console.error('Error fetching data:', error);
|
|
setError('Failed to load data');
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchData();
|
|
|
|
return () => {
|
|
if (pdfBlobUrl) {
|
|
window.URL.revokeObjectURL(pdfBlobUrl);
|
|
}
|
|
};
|
|
}, [id, fileName, fileType, navigate]);
|
|
|
|
useEffect(() => {
|
|
if (brandData) {
|
|
const breadcrumbItems = [
|
|
{ title: <strong style={{ fontSize: '14px' }}>• Master</strong> },
|
|
{
|
|
title: <strong style={{ fontSize: '14px' }} onClick={() => navigate('/master/brand-device')}>Brand Device</strong>
|
|
}
|
|
];
|
|
|
|
if (isFromEdit) {
|
|
breadcrumbItems.push({
|
|
title: <strong style={{ fontSize: '14px' }} onClick={() => navigate(`/master/brand-device/edit/${fallbackId || id}`)}>Edit Brand Device</strong>
|
|
});
|
|
} else {
|
|
breadcrumbItems.push({
|
|
title: <strong style={{ fontSize: '14px' }} onClick={() => navigate(`/master/brand-device/view/${fallbackId || id}`)}>View Brand Device</strong>
|
|
});
|
|
}
|
|
|
|
breadcrumbItems.push({ title: <strong style={{ fontSize: '14px' }}>View Document</strong> });
|
|
|
|
setBreadcrumbItems(breadcrumbItems);
|
|
}
|
|
}, [brandData, id, isFromEdit, fallbackId, navigate, setBreadcrumbItems]);
|
|
|
|
const handleBack = () => {
|
|
if (isFromEdit) {
|
|
const savedPhase = localStorage.getItem(`brand_device_edit_${fallbackId || id}_last_phase`);
|
|
|
|
if (savedPhase) {
|
|
localStorage.removeItem(`brand_device_edit_${fallbackId || id}_last_phase`);
|
|
}
|
|
|
|
const targetPhase = savedPhase ? parseInt(savedPhase) : 1;
|
|
|
|
console.log('ViewFilePage handleBack - Edit mode:', {
|
|
savedPhase,
|
|
targetPhase,
|
|
id: fallbackId || id
|
|
});
|
|
|
|
navigate(`/master/brand-device/edit/${fallbackId || id}`, {
|
|
state: { phase: targetPhase, fromFileViewer: true },
|
|
replace: true
|
|
});
|
|
} else {
|
|
navigate(`/master/brand-device/view/${fallbackId || id}`, {
|
|
state: { phase: 1 },
|
|
replace: true
|
|
});
|
|
}
|
|
};
|
|
|
|
const renderContent = () => {
|
|
if (error) {
|
|
return (
|
|
<Alert
|
|
message="Error Loading File"
|
|
description={error}
|
|
type="error"
|
|
showIcon
|
|
style={{ margin: '20px 0' }}
|
|
/>
|
|
);
|
|
}
|
|
|
|
const displayFileName = actualFileName || 'Loading...';
|
|
const fileExtension = displayFileName.split('.').pop().toLowerCase();
|
|
const isImage = ['jpg', 'jpeg', 'png', 'gif'].includes(fileExtension);
|
|
const isPdf = fileExtension === 'pdf';
|
|
|
|
// const fileUrl = loading ? null : getFileUrl(getFolderFromFileType(fallbackFileType || fileType), actualFileName);
|
|
|
|
// Show placeholder when loading
|
|
if (loading) {
|
|
return (
|
|
<div style={{ textAlign: 'center', padding: '50px' }}>
|
|
{isImage ? (
|
|
<div style={{
|
|
width: '100%',
|
|
height: '300px',
|
|
backgroundColor: '#f5f5f5',
|
|
border: '1px solid #d9d9d9',
|
|
borderRadius: '8px',
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
color: '#999'
|
|
}}>
|
|
<div>
|
|
<FileImageOutlined style={{ fontSize: '48px', marginBottom: '16px' }} />
|
|
<div>Loading image...</div>
|
|
</div>
|
|
</div>
|
|
) : isPdf ? (
|
|
<div style={{
|
|
width: '100%',
|
|
height: '400px',
|
|
backgroundColor: '#f5f5f5',
|
|
border: '1px solid #d9d9d9',
|
|
borderRadius: '8px',
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
color: '#999'
|
|
}}>
|
|
<div>
|
|
<FilePdfOutlined style={{ fontSize: '48px', marginBottom: '16px' }} />
|
|
<div>Loading PDF...</div>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div style={{
|
|
width: '100%',
|
|
height: '200px',
|
|
backgroundColor: '#f5f5f5',
|
|
border: '1px solid #d9d9d9',
|
|
borderRadius: '8px',
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
color: '#999'
|
|
}}>
|
|
<div>
|
|
<FilePdfOutlined style={{ fontSize: '48px', marginBottom: '16px' }} />
|
|
<div>Loading file...</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (isImage) {
|
|
return (
|
|
<div style={{ textAlign: 'center', padding: '20px' }}>
|
|
<img
|
|
src={getFileUrl(getFolderFromFileType(fallbackFileType || fileType), actualFileName)}
|
|
alt={actualFileName}
|
|
style={{
|
|
maxWidth: '100%',
|
|
maxHeight: '70vh',
|
|
objectFit: 'contain',
|
|
border: '1px solid #d9d9d9',
|
|
borderRadius: '8px'
|
|
}}
|
|
onError={() => setError('Failed to load image')}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (isPdf) {
|
|
const displayUrl = pdfBlobUrl || getFileUrl(getFolderFromFileType(fallbackFileType || fileType), actualFileName);
|
|
|
|
return (
|
|
<div style={{ height: '75vh', width: '100%', border: '1px solid #d9d9d9', borderRadius: '8px', overflow: 'hidden' }}>
|
|
{pdfBlobUrl ? (
|
|
<iframe
|
|
src={pdfBlobUrl}
|
|
title={actualFileName}
|
|
style={{
|
|
width: '100%',
|
|
height: '100%',
|
|
border: 'none',
|
|
borderRadius: '8px'
|
|
}}
|
|
onError={() => {
|
|
setError('Failed to load PDF. Please try downloading the file.');
|
|
}}
|
|
/>
|
|
) : pdfLoading ? (
|
|
<div style={{
|
|
height: '100%',
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
flexDirection: 'column',
|
|
gap: '16px',
|
|
backgroundColor: '#f5f5f5'
|
|
}}>
|
|
<Spin size="large" />
|
|
<div style={{ fontSize: '16px', color: '#666', textAlign: 'center' }}>
|
|
<div style={{ marginBottom: '8px', fontWeight: 'bold' }}>Memuat PDF...</div>
|
|
<div>Silakan tunggu sebentar</div>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<div style={{
|
|
height: '100%',
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
flexDirection: 'column',
|
|
gap: '16px',
|
|
backgroundColor: '#f5f5f5'
|
|
}}>
|
|
<FilePdfOutlined style={{ fontSize: '48px', color: '#ff4d4f' }} />
|
|
<div style={{ fontSize: '16px', color: '#666', textAlign: 'center' }}>
|
|
<div style={{ marginBottom: '8px', fontWeight: 'bold' }}>PDF tidak dapat dimuat</div>
|
|
<div>Silakan download file untuk melihat kontennya</div>
|
|
</div>
|
|
<div style={{ display: 'flex', gap: '12px' }}>
|
|
<Button
|
|
type="primary"
|
|
onClick={() => {
|
|
const folder = getFolderFromFileType(fallbackFileType || fileType);
|
|
downloadFile(folder, actualFileName);
|
|
}}
|
|
icon={<DownloadOutlined />}
|
|
>
|
|
Download PDF
|
|
</Button>
|
|
<Button
|
|
onClick={() => {
|
|
// Retry loading PDF
|
|
setPdfLoading(true);
|
|
const folder = getFolderFromFileType('pdf');
|
|
getFile(folder, actualFileName)
|
|
.then(blobData => {
|
|
console.log('Retry PDF blob data:', blobData);
|
|
const blobUrl = window.URL.createObjectURL(blobData);
|
|
setPdfBlobUrl(blobUrl);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error retrying PDF load:', error);
|
|
setError('Failed to load PDF file: ' + (error.message || error));
|
|
setPdfBlobUrl(null);
|
|
})
|
|
.finally(() => {
|
|
setPdfLoading(false);
|
|
});
|
|
}}
|
|
>
|
|
Coba Lagi
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div style={{ textAlign: 'center', padding: '50px' }}>
|
|
<FilePdfOutlined style={{ fontSize: '48px', color: '#ff4d4f', marginBottom: '16px' }} />
|
|
<div style={{ fontSize: '16px', marginBottom: '8px' }}>Preview tidak tersedia untuk jenis file ini</div>
|
|
<div style={{ color: '#666', marginBottom: '16px' }}>{actualFileName}</div>
|
|
<div style={{ marginTop: '16px' }}>
|
|
<Button type="primary" href={getFileUrl(getFolderFromFileType(fallbackFileType || fileType), actualFileName)} target="_blank" rel="noopener noreferrer">
|
|
Buka di Tab Baru
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const getFileIcon = () => {
|
|
const displayFileName = actualFileName || 'Loading...';
|
|
const fileExtension = displayFileName.split('.').pop().toLowerCase();
|
|
const isImage = ['jpg', 'jpeg', 'png', 'gif'].includes(fileExtension);
|
|
const isPdf = fileExtension === 'pdf';
|
|
|
|
if (isImage) return <FileImageOutlined style={{ color: '#1890ff', fontSize: '20px' }} />;
|
|
if (isPdf) return <FilePdfOutlined style={{ color: '#ff4d4f', fontSize: '20px' }} />;
|
|
return <FilePdfOutlined style={{ color: '#ff4d4f', fontSize: '20px' }} />;
|
|
};
|
|
|
|
const getFileTypeColor = () => {
|
|
const displayFileName = actualFileName || 'Loading...';
|
|
const fileExtension = displayFileName.split('.').pop().toLowerCase();
|
|
const isImage = ['jpg', 'jpeg', 'png', 'gif'].includes(fileExtension);
|
|
const isPdf = fileExtension === 'pdf';
|
|
|
|
if (isImage) return '#1890ff';
|
|
if (isPdf) return '#ff4d4f';
|
|
return '#ff4d4f';
|
|
};
|
|
|
|
return (
|
|
<div style={{ padding: '24px', minHeight: '100vh', backgroundColor: '#f5f5f5' }}>
|
|
<Card>
|
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '24px' }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
|
{getFileIcon()}
|
|
<div>
|
|
<Title level={4} style={{ margin: 0 }}>
|
|
{actualFileName || 'Loading...'}
|
|
</Title>
|
|
{brandData ? (
|
|
<div style={{ color: '#666', fontSize: '14px' }}>
|
|
Brand: {brandData.brand_name} | ID: {brandData.brand_id}
|
|
</div>
|
|
) : (
|
|
<div style={{ color: '#666', fontSize: '14px' }}>
|
|
Loading brand information...
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<Space>
|
|
<Button
|
|
icon={<ArrowLeftOutlined />}
|
|
onClick={handleBack}
|
|
>
|
|
Kembali
|
|
</Button>
|
|
<Button
|
|
type="primary"
|
|
onClick={() => {
|
|
const folder = getFolderFromFileType(fallbackFileType || fileType);
|
|
downloadFile(folder, actualFileName);
|
|
}}
|
|
disabled={loading}
|
|
>
|
|
Download File
|
|
</Button>
|
|
</Space>
|
|
</div>
|
|
|
|
{/* File type indicator */}
|
|
<div style={{ marginBottom: '16px' }}>
|
|
<div style={{
|
|
display: 'inline-block',
|
|
padding: '4px 12px',
|
|
backgroundColor: getFileTypeColor() + '15',
|
|
border: `1px solid ${getFileTypeColor()}30`,
|
|
borderRadius: '16px',
|
|
fontSize: '12px',
|
|
fontWeight: 'bold',
|
|
color: getFileTypeColor()
|
|
}}>
|
|
{(fallbackFileType || fileType || 'FILE')?.toUpperCase()}
|
|
</div>
|
|
</div>
|
|
|
|
<div style={{ position: 'relative' }}>
|
|
{/* Overlay with blur effect during loading */}
|
|
{loading && (
|
|
<div style={{
|
|
position: 'absolute',
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
backgroundColor: 'rgba(255, 255, 255, 0.6)',
|
|
backdropFilter: 'blur(0.8px)',
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
zIndex: 5,
|
|
borderRadius: '8px'
|
|
}}>
|
|
<Spin size="large" />
|
|
</div>
|
|
)}
|
|
|
|
<div style={{ filter: loading ? 'blur(0.5px)' : 'none', transition: 'filter 0.3s ease' }}>
|
|
{renderContent()}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ViewFilePage; |