diff --git a/src/api/file-uploads.jsx b/src/api/file-uploads.jsx index d629846..52bc8bd 100644 --- a/src/api/file-uploads.jsx +++ b/src/api/file-uploads.jsx @@ -5,9 +5,18 @@ const API_BASE_URL = import.meta.env.VITE_API_SERVER; // Get file from uploads directory const getFile = async (folder, filename) => { + const token = localStorage.getItem('token'); + if (!token) { + throw new Error('No authentication token found'); + } + const response = await axios.get(`${API_BASE_URL}/file-uploads/${folder}/${encodeURIComponent(filename)}`, { - responseType: 'blob' + responseType: 'blob', + headers: { + 'Authorization': `Bearer ${token.replace(/"/g, '')}` + } }); + return response.data; }; diff --git a/src/pages/master/brandDevice/ViewBrandDevice.jsx b/src/pages/master/brandDevice/ViewBrandDevice.jsx index e8b369d..9e5096d 100644 --- a/src/pages/master/brandDevice/ViewBrandDevice.jsx +++ b/src/pages/master/brandDevice/ViewBrandDevice.jsx @@ -1,11 +1,10 @@ -import React, { memo, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { useNavigate, useParams, useLocation } from 'react-router-dom'; -import { Typography, Card, Row, Col, Tag, Button, Space, Descriptions, Divider, Table, Steps, Collapse, Switch, Skeleton, Spin, Modal } from 'antd'; -import { ArrowLeftOutlined, EditOutlined, DeleteOutlined, FileTextOutlined, FilePdfOutlined, EyeOutlined } from '@ant-design/icons'; +import { Typography, Card, Row, Col, Tag, Button, Space, Descriptions, Divider, Steps, Collapse, Switch, Spin, Modal, Empty } from 'antd'; +import { ArrowLeftOutlined, FileTextOutlined, FilePdfOutlined, EyeOutlined } from '@ant-design/icons'; import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb'; -import { NotifConfirmDialog, NotifOk, NotifAlert } from '../../../components/Global/ToastNotif'; -import { getBrandById, deleteBrand } from '../../../api/master-brand'; -import TableList from '../../../components/Global/TableList'; +import { NotifOk, NotifAlert } from '../../../components/Global/ToastNotif'; +import { getBrandById } from '../../../api/master-brand'; const { Title, Text } = Typography; const { Step } = Steps; @@ -19,9 +18,8 @@ const ViewBrandDevice = () => { const [brandData, setBrandData] = useState(null); const [loading, setLoading] = useState(true); const [currentStep, setCurrentStep] = useState(0); - const [errorCodesTriger, setErrorCodesTriger] = useState(0); + const [activeErrorKeys, setActiveErrorKeys] = useState([]); - useEffect(() => { const fetchBrandData = async () => { const token = localStorage.getItem('token'); @@ -44,10 +42,8 @@ const ViewBrandDevice = () => { setLoading(true); const response = await getBrandById(id); - if (response && response.statusCode === 200) { setBrandData(response.data); - setErrorCodesTriger(prev => prev + 1); } else { NotifAlert({ icon: 'error', @@ -73,264 +69,21 @@ const ViewBrandDevice = () => { fetchBrandData(); }, [id, setBreadcrumbItems, navigate, location.state]); - // const handleEdit = () => { - // navigate(`/master/brand-device/edit/${id}`); - // }; - - // const handleDelete = () => { - // NotifConfirmDialog({ - // icon: 'question', - // title: 'Konfirmasi Hapus', - // message: `Brand Device "${brandData?.brand_name}" akan dihapus?`, - // onConfirm: async () => { - // try { - // const response = await deleteBrand(id); - - // if (response && response.statusCode === 200) { - // NotifOk({ - // icon: 'success', - // title: 'Berhasil', - // message: response.message || 'Brand Device berhasil dihapus.', - // }); - // navigate('/master/brand-device'); - // } else { - // NotifAlert({ - // icon: 'error', - // title: 'Gagal', - // message: response?.message || 'Gagal menghapus Brand Device', - // }); - // } - // } catch (error) { - // console.error('Delete Brand Device Error:', error); - // NotifAlert({ - // icon: 'error', - // title: 'Error', - // message: error.message || 'Gagal menghapus Brand Device', - // }); - // } - // }, - // onCancel: () => {}, - // }); - // }; - - // Fungsi untuk membuka file viewer di halaman baru const handleFileView = (fileName, fileType) => { - console.log('handleFileView called with:', { fileName, fileType }); - - // Save current phase before navigating to file viewer localStorage.setItem(`brand_device_${id}_last_phase`, currentStep.toString()); - // Extract only the filename without folder prefix let actualFileName = fileName; if (fileName && fileName.includes('/')) { const parts = fileName.split('/'); - actualFileName = parts[parts.length - 1]; // Get the last part (actual filename) + actualFileName = parts[parts.length - 1]; } - console.log('Processed filename:', { original: fileName, actual: actualFileName }); - const encodedFileName = encodeURIComponent(actualFileName); const fileTypeParam = fileType === 'image' ? 'image' : 'pdf'; const navigationPath = `/master/brand-device/view/${id}/files/${fileTypeParam}/${encodedFileName}`; - - console.log('Navigating to:', navigationPath); navigate(navigationPath); }; - - // if (loading) { - // return ( - //
- // - //
- // ); - // } - - if (!brandData && !loading) { - return
Brand Device not found
; - } - - // Error code table columns configuration - const errorCodeColumns = [ - { - title: 'No', - key: 'no', - width: '5%', - align: 'center', - render: (_, __, index) => index + 1, - }, - { - title: 'Error Code', - dataIndex: 'error_code', - key: 'error_code', - width: '15%', - render: (text) => text || '-', - }, - { - title: 'Error Code Name', - dataIndex: 'error_code_name', - key: 'error_code_name', - width: '20%', - render: (text) => text || '-', - }, - { - title: 'Description', - dataIndex: 'error_code_description', - key: 'error_code_description', - width: '25%', - render: (text) => text || '-', - }, - { - title: 'Solutions', - dataIndex: 'solution', - key: 'solution', - width: '20%', - render: (solutions) => ( -
- {solutions && solutions.length > 0 ? ( -
- {solutions.length} solution(s) -
- {solutions.slice(0, 2).map((sol, index) => ( -
- {sol.type_solution === 'text' ? ( - • {sol.solution_name} - ) : ( - • {sol.solution_name} ({sol.type_solution}) - )} -
- ))} - {solutions.length > 2 && ( -
- ...and {solutions.length - 2} more -
- )} -
-
- ) : ( - No solutions - )} -
- ) - }, - { - title: 'Status', - dataIndex: 'is_active', - key: 'is_active', - width: '10%', - align: 'center', - render: (_, { is_active }) => ( - - {is_active ? 'Active' : 'Inactive'} - - ), - }, - { - title: 'Action', - key: 'action', - align: 'center', - width: '5%', - render: (_, record) => ( - - )} - - )} - - - - ))} - - ) : ( - No solutions available - )} - - ), - }); - }} - style={{ color: '#1890ff' }} - /> - ), - }, - ]; - - // Mock data function for error codes - const getErrorCodesData = async () => { - const errorCodes = brandData?.error_code || []; - return { - data: errorCodes, - paging: { - current_page: 1, - current_limit: 10, - total_limit: errorCodes.length, - total_page: 1, - } - }; - }; - const renderStepContent = () => { if (currentStep === 0) { return ( @@ -444,26 +197,140 @@ const ViewBrandDevice = () => { } if (currentStep === 1) { - const errorCodesCount = loading ? 3 : (brandData?.error_code?.length || 0); + const errorCodes = brandData?.error_code || []; return (
- Error Codes ({errorCodesCount}) + Error Codes ({errorCodes.length}) - {errorCodesCount > 0 ? ( - + + {errorCodes.length > 0 ? ( + + {errorCodes.map((errorCode, index) => ( + +
+ {errorCode.error_code} + + - {errorCode.error_code_name} + +
+
+ + {errorCode.is_active ? 'Active' : 'Inactive'} + + + {errorCode.solution?.length || 0} solution(s) + +
+
+ } + > +
+
+ Description: +
+ {errorCode.error_code_description || 'No description'} +
+
+ +
+ Solutions: + {errorCode.solution && errorCode.solution.length > 0 ? ( +
+ {errorCode.solution.map((solution) => ( + + + + + {solution.type_solution === 'pdf' ? ( + + ) : solution.type_solution === 'image' ? ( + + ) : ( + + )} + {solution.solution_name} + + + + + {solution.type_solution ? solution.type_solution.toUpperCase() : 'TEXT'} + + + + +
+ {solution.type_solution === 'text' ? ( + + {solution.text_solution} + + ) : ( +
+ + File: {solution.path_document || solution.path_solution || 'Document'} + + {(solution.path_document || solution.path_solution) && ( + + )} +
+ )} +
+
+ ))} +
+ ) : ( +
+ No solutions available +
+ )} +
+
+ + ))} + ) : ( - !loading && No error codes available + !loading && ( + No error codes available + } + /> + ) )} ); @@ -483,7 +350,7 @@ const ViewBrandDevice = () => { @@ -496,9 +363,7 @@ const ViewBrandDevice = () => { - {/* Content area with blur overlay during loading */}
- {/* Overlay with blur effect during loading - only on content area */} {loading && (
{ )}
- + ); }; diff --git a/src/pages/master/brandDevice/ViewFilePage.jsx b/src/pages/master/brandDevice/ViewFilePage.jsx index f6c5b03..4476450 100644 --- a/src/pages/master/brandDevice/ViewFilePage.jsx +++ b/src/pages/master/brandDevice/ViewFilePage.jsx @@ -94,13 +94,15 @@ const ViewFilePage = () => { setPdfLoading(true); const folder = getFolderFromFileType('pdf'); try { - const response = await getFile(folder, decodedFileName); - const blobUrl = window.URL.createObjectURL(response.data); + 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'); + setError('Failed to load PDF file: ' + (pdfError.message || pdfError)); + setPdfBlobUrl(null); } finally { setPdfLoading(false); } @@ -194,7 +196,7 @@ const ViewFilePage = () => { const isImage = ['jpg', 'jpeg', 'png', 'gif'].includes(fileExtension); const isPdf = fileExtension === 'pdf'; - const fileUrl = loading ? null : getFileUrl(getFolderFromFileType(fallbackFileType || fileType), actualFileName); + // const fileUrl = loading ? null : getFileUrl(getFolderFromFileType(fallbackFileType || fileType), actualFileName); // Show placeholder when loading if (loading) { @@ -260,7 +262,7 @@ const ViewFilePage = () => { return (
{actualFileName} { } if (isPdf) { - const displayUrl = pdfBlobUrl || fileUrl; + const displayUrl = pdfBlobUrl || getFileUrl(getFolderFromFileType(fallbackFileType || fileType), actualFileName); return (
@@ -342,13 +344,15 @@ const ViewFilePage = () => { setPdfLoading(true); const folder = getFolderFromFileType('pdf'); getFile(folder, actualFileName) - .then(response => { - const blobUrl = window.URL.createObjectURL(response.data); + .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'); + setError('Failed to load PDF file: ' + (error.message || error)); + setPdfBlobUrl(null); }) .finally(() => { setPdfLoading(false); @@ -370,7 +374,7 @@ const ViewFilePage = () => {
Preview tidak tersedia untuk jenis file ini
{actualFileName}
-