From 49ba00d886b346e9fa8510a438fa5dda3c0aa96d Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Date: Fri, 12 Dec 2025 23:02:09 +0700 Subject: [PATCH] repair: view brand device, add: read only --- .../master/brandDevice/AddBrandDevice.jsx | 136 +-- .../master/brandDevice/EditBrandDevice.jsx | 94 +- .../master/brandDevice/ViewBrandDevice.jsx | 1047 +++++++++++------ src/pages/master/brandDevice/ViewFilePage.jsx | 12 +- .../brandDevice/component/BrandForm.jsx | 14 +- .../component/CustomSparepartCard.jsx | 24 +- .../brandDevice/component/ErrorCodeForm.jsx | 7 +- .../component/FileUploadHandler.jsx | 6 +- .../brandDevice/component/ListBrandDevice.jsx | 4 +- .../brandDevice/component/ListErrorCode.jsx | 23 +- .../brandDevice/component/SolutionField.jsx | 15 +- .../brandDevice/component/SparepartSelect.jsx | 4 +- 12 files changed, 868 insertions(+), 518 deletions(-) diff --git a/src/pages/master/brandDevice/AddBrandDevice.jsx b/src/pages/master/brandDevice/AddBrandDevice.jsx index 53d50b2..c43345d 100644 --- a/src/pages/master/brandDevice/AddBrandDevice.jsx +++ b/src/pages/master/brandDevice/AddBrandDevice.jsx @@ -982,7 +982,7 @@ const AddBrandDevice = () => { borderTop: '1px solid #f0f0f0', marginTop: '12px' }}> - {/* Cancel Button - Only show when editing existing error code */} + {editingErrorCodeKey && ( )} - {/* Save Button - Always show on right */} +
- {currentStep === 1 && ( - - )} + {currentStep === 1 && ( + + )} +
+
+ {currentStep === 0 && ( + + )} + {currentStep === 1 && ( + + )} +
-
- {currentStep === 0 && ( - - )} - {currentStep === 1 && ( - - )} -
- ); diff --git a/src/pages/master/brandDevice/EditBrandDevice.jsx b/src/pages/master/brandDevice/EditBrandDevice.jsx index d5190e4..c836527 100644 --- a/src/pages/master/brandDevice/EditBrandDevice.jsx +++ b/src/pages/master/brandDevice/EditBrandDevice.jsx @@ -328,13 +328,13 @@ const EditBrandDevice = () => { } }, [currentStep, id]); - // Auto refresh error codes when returning from add/edit error code + useEffect(() => { if (location.state?.refreshErrorCodes) { - // Trigger refresh of error codes list + setTrigerFilter(prev => !prev); - // Clear the state to prevent infinite refresh + const state = { ...location.state }; delete state.refreshErrorCodes; navigate(location.pathname + location.search, { @@ -345,7 +345,7 @@ const EditBrandDevice = () => { }, [location, navigate]); const addErrorCode = (newErrorCode) => { - // Generate unique tempId with timestamp and random number + const uniqueId = `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const errorCodeWithId = { ...newErrorCode, @@ -566,13 +566,13 @@ const EditBrandDevice = () => { message: editingErrorCodeKey ? 'Error code berhasil diupdate!' : 'Error code berhasil ditambahkan!', }); - // Clear temp error codes after successful save to prevent duplication + setTempErrorCodes(prev => prev.filter(ec => { - // Keep existing error codes that weren't just saved + if (ec.status === 'existing' || ec.tempId.startsWith('existing_')) { return true; } - // Remove the newly saved error code to prevent duplication + return ec.tempId !== editingErrorCodeKey; })); @@ -1182,7 +1182,7 @@ const EditBrandDevice = () => { borderTop: '1px solid #f0f0f0', marginTop: '12px' }}> - {/* Cancel Button - Only show when editing existing error code */} + {editingErrorCodeKey && ( )} - {/* Save Button - Always show on right */} +
- )} +
+
+ {currentStep === 1 && ( + + )} +
+
+ {currentStep === 0 && ( + + )} + {currentStep === 1 && ( + + )} +
-
- {currentStep === 0 && ( - - )} - {currentStep === 1 && ( - - )} -
-
); diff --git a/src/pages/master/brandDevice/ViewBrandDevice.jsx b/src/pages/master/brandDevice/ViewBrandDevice.jsx index f0a7940..997af93 100644 --- a/src/pages/master/brandDevice/ViewBrandDevice.jsx +++ b/src/pages/master/brandDevice/ViewBrandDevice.jsx @@ -1,414 +1,765 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useCallback, useMemo } from 'react'; import { useNavigate, useParams, useLocation } from 'react-router-dom'; -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 { + Divider, + Typography, + Button, + Steps, + Form, + Row, + Col, + Card, + Spin, + Tag, + Space, + ConfigProvider, + Empty +} from 'antd'; +import { ArrowLeftOutlined } from '@ant-design/icons'; import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb'; -import { NotifOk, NotifAlert } from '../../../components/Global/ToastNotif'; -import { getBrandById } from '../../../api/master-brand'; +import { NotifAlert } from '../../../components/Global/ToastNotif'; +import { getBrandById, getErrorCodesByBrandId } from '../../../api/master-brand'; +import { getFileUrl, getFolderFromFileType } from '../../../api/file-uploads'; +import { SendRequest } from '../../../components/Global/ApiRequest'; +import ListErrorCode from './component/ListErrorCode'; +import BrandForm from './component/BrandForm'; +import ErrorCodeForm from './component/ErrorCodeForm'; +import SolutionForm from './component/SolutionForm'; +import SparepartSelect from './component/SparepartSelect'; const { Title, Text } = Typography; const { Step } = Steps; -const { Panel } = Collapse; const ViewBrandDevice = () => { const navigate = useNavigate(); const { id } = useParams(); const location = useLocation(); const { setBreadcrumbItems } = useBreadcrumb(); + const [brandForm] = Form.useForm(); + const [errorCodeForm] = Form.useForm(); + const [solutionForm] = Form.useForm(); + const [brandData, setBrandData] = useState(null); + const [errorCodes, setErrorCodes] = useState([]); const [loading, setLoading] = useState(true); const [currentStep, setCurrentStep] = useState(0); - const [activeErrorKeys, setActiveErrorKeys] = useState([]); + const [selectedErrorCode, setSelectedErrorCode] = useState(null); + const [selectedSparepartIds, setSelectedSparepartIds] = useState([]); + const [errorCodeIcon, setErrorCodeIcon] = useState(null); + const [trigerFilter, setTrigerFilter] = useState(false); + const [searchText, setSearchText] = useState(''); + + + const [solutionFields, setSolutionFields] = useState([0]); + const [solutionTypes, setSolutionTypes] = useState({ 0: 'text' }); + const [solutionStatuses, setSolutionStatuses] = useState({ 0: true }); + const [currentSolutionData, setCurrentSolutionData] = useState([]); + + + const [brandInfo, setBrandInfo] = useState({}); + + const resetSolutionFields = () => { + if (solutionForm && solutionForm.resetFields) { + solutionForm.resetFields(); + solutionForm.setFieldsValue({ + solution_items: { + 0: { + name: '', + type: 'text', + text: '', + status: true + } + } + }); + } + setCurrentSolutionData([]); + }; + + const getSolutionData = () => { + if (!solutionForm) return []; + try { + const values = solutionForm.getFieldsValue(true); + + let solutions = []; + + if (values.solution_items) { + if (Array.isArray(values.solution_items)) { + solutions = values.solution_items.filter(Boolean); + } else if (typeof values.solution_items === 'object') { + solutions = Object.values(values.solution_items).filter(Boolean); + } + } + + return solutions; + } catch (error) { + return []; + } + }; + + + useEffect(() => { + const savedPhase = location.state?.phase || localStorage.getItem(`brand_device_${id}_last_phase`); + if (savedPhase) { + setCurrentStep(parseInt(savedPhase)); + localStorage.removeItem(`brand_device_${id}_last_phase`); + } + }, [location.state, id]); + + + useEffect(() => { + setBreadcrumbItems([ + { + title: • Master + }, + { + title: ( + navigate('/master/brand-device')} + > + Brand Device + + ), + }, + { + title: ( + + View Brand Device + + ), + }, + ]); + }, [setBreadcrumbItems, navigate]); useEffect(() => { const fetchBrandData = async () => { const token = localStorage.getItem('token'); - if (token) { - const savedPhase = location.state?.phase || localStorage.getItem(`brand_device_${id}_last_phase`); - if (savedPhase) { - setCurrentStep(parseInt(savedPhase)); - localStorage.removeItem(`brand_device_${id}_last_phase`); - } + if (!token) { + navigate('/signin'); + return; + } - setBreadcrumbItems([ - { title: • Master }, - { - title: navigate('/master/brand-device')}>Brand Device - }, - { title: View Brand Device } - ]); + try { + setLoading(true); + const response = await getBrandById(id); - try { - setLoading(true); - const response = await getBrandById(id); + if (response && response.statusCode === 200) { + const brandData = response.data; - if (response && response.statusCode === 200) { - setBrandData(response.data); - } else { - NotifAlert({ - icon: 'error', - title: 'Error', - message: response?.message || 'Failed to fetch brand device data', - }); + const brandInfoData = { + brand_code: brandData.brand_code, + brand_name: brandData.brand_name, + brand_type: brandData.brand_type || '', + brand_manufacture: brandData.brand_manufacture || '', + brand_model: brandData.brand_model || '', + is_active: brandData.is_active + }; + + setBrandInfo(brandInfoData); + setBrandData(brandData); + brandForm.setFieldsValue(brandInfoData); + + if (brandData.brand_id) { + try { + const errorCodesResponse = await getErrorCodesByBrandId(id || brandData.brand_id); + if (errorCodesResponse && errorCodesResponse.statusCode === 200) { + const apiErrorData = errorCodesResponse.data || []; + const existingCodes = apiErrorData.map(ec => ({ + ...ec, + tempId: `existing_${ec.error_code_id}`, + status: 'existing', + solution: ec.solution || [], + spareparts: ec.spareparts || [] + })); + setErrorCodes(existingCodes); + } + } catch (error) { + } } - } catch (error) { + } else { NotifAlert({ icon: 'error', title: 'Error', - message: error.message || 'Failed to fetch brand device data', + message: response?.message || 'Failed to fetch brand device data', }); - } finally { - setLoading(false); } - } else { - navigate('/signin'); + } catch (error) { + NotifAlert({ + icon: 'error', + title: 'Error', + message: error.message || 'Failed to fetch brand device data', + }); + } finally { + setLoading(false); } }; fetchBrandData(); - }, [id, setBreadcrumbItems, navigate, location.state]); + }, [id, navigate, brandForm]); - const handleFileView = (fileName, fileType) => { - localStorage.setItem(`brand_device_${id}_last_phase`, currentStep.toString()); + useEffect(() => { + if (currentStep === 1 && id) { + setTrigerFilter(prev => !prev); + } + }, [currentStep, id]); - let actualFileName = fileName; - if (fileName && fileName.includes('/')) { - const parts = fileName.split('/'); - actualFileName = parts[parts.length - 1]; + + useEffect(() => { + if (currentStep === 1 && errorCodes.length > 0 && !selectedErrorCode) { + handleErrorCodeSelect(errorCodes[0]); + } + }, [currentStep, errorCodes]); + + const setSolutionsForExistingRecord = (solutions, targetForm) => { + + if (!targetForm || !solutions || solutions.length === 0) { + return; } - const encodedFileName = encodeURIComponent(actualFileName); - const fileTypeParam = fileType === 'image' ? 'image' : 'pdf'; - const navigationPath = `/master/brand-device/view/${id}/files/${fileTypeParam}/${encodedFileName}`; - navigate(navigationPath); + targetForm.resetFields(); + + const solutionItems = {}; + const newSolutionFields = []; + const newSolutionTypes = {}; + const newSolutionStatuses = {}; + + solutions.forEach((solution, index) => { + const fieldKey = index; + newSolutionFields.push(fieldKey); + + const isFileType = solution.type_solution && solution.type_solution !== 'text'; + newSolutionTypes[fieldKey] = isFileType ? 'file' : 'text'; + newSolutionStatuses[fieldKey] = solution.is_active; + + let fileObject = null; + if (isFileType && (solution.path_solution || solution.path_document)) { + fileObject = { + uploadPath: solution.path_solution || solution.path_document, + path_solution: solution.path_solution || solution.path_document, + name: solution.file_upload_name || (solution.path_solution || solution.path_document).split('/').pop() || 'File', + type_solution: solution.type_solution, + isExisting: true, + size: 0, + url: solution.path_solution || solution.path_document + }; + } + + solutionItems[fieldKey] = { + brand_code_solution_id: solution.brand_code_solution_id, + name: solution.solution_name || '', + type: isFileType ? 'file' : 'text', + text: solution.text_solution || '', + status: solution.is_active, + file: fileObject, + fileUpload: fileObject, + path_solution: solution.path_solution || solution.path_document || null, + fileName: solution.file_upload_name || null + }; + }); + + setSolutionFields(newSolutionFields); + setSolutionTypes(newSolutionTypes); + setSolutionStatuses(newSolutionStatuses); + + targetForm.resetFields(); + + setTimeout(() => { + targetForm.setFieldsValue({ + solution_items: solutionItems + }); + + setTimeout(() => { + Object.keys(solutionItems).forEach(key => { + const solution = solutionItems[key]; + targetForm.setFieldValue(['solution_items', key, 'name'], solution.name); + targetForm.setFieldValue(['solution_items', key, 'type'], solution.type); + targetForm.setFieldValue(['solution_items', key, 'text'], solution.text); + targetForm.setFieldValue(['solution_items', key, 'file'], solution.file); + targetForm.setFieldValue(['solution_items', key, 'fileUpload'], solution.fileUpload); + targetForm.setFieldValue(['solution_items', key, 'status'], solution.status); + targetForm.setFieldValue(['solution_items', key, 'path_solution'], solution.path_solution); + targetForm.setFieldValue(['solution_items', key, 'fileName'], solution.fileName); + }); + + const finalValues = targetForm.getFieldsValue(); + }, 100); + }, 100); + }; + + const handleErrorCodeSelect = async (errorCode) => { + + setSelectedErrorCode(errorCode); + + try { + + const directResponse = await SendRequest({ + method: 'get', + prefix: `error-code/${errorCode.error_code_id}`, + }); + + const apiResponse = directResponse.data; + + if (apiResponse && apiResponse.statusCode === 200 && apiResponse.data) { + const fullErrorCodeData = { + ...apiResponse.data, + tempId: `existing_${apiResponse.data.error_code_id}` + }; + + const formValues = { + error_code: fullErrorCodeData.error_code, + error_code_name: fullErrorCodeData.error_code_name, + error_code_description: fullErrorCodeData.error_code_description || '', + error_code_color: fullErrorCodeData.error_code_color && fullErrorCodeData.error_code_color !== '' ? fullErrorCodeData.error_code_color : '#000000', + status: fullErrorCodeData.is_active, + }; + + errorCodeForm.setFieldsValue(formValues); + + if (fullErrorCodeData.path_icon && fullErrorCodeData.path_icon !== '') { + const iconData = { + name: fullErrorCodeData.path_icon.split('/').pop(), + uploadPath: fullErrorCodeData.path_icon, + }; + setErrorCodeIcon(iconData); + } else { + setErrorCodeIcon(null); + } + + if (apiResponse.data.solution && apiResponse.data.solution.length > 0) { + setCurrentSolutionData(apiResponse.data.solution); + setSolutionsForExistingRecord(apiResponse.data.solution, solutionForm); + } + + if (apiResponse.data.spareparts && apiResponse.data.spareparts.length > 0) { + setSelectedSparepartIds(apiResponse.data.spareparts.map(sp => sp.sparepart_id)); + } else { + setSelectedSparepartIds([]); + } + } else { + const basicErrorCodeData = { + ...errorCode, + tempId: `existing_${errorCode.error_code_id}` + }; + + const formValues = { + error_code: basicErrorCodeData.error_code, + error_code_name: basicErrorCodeData.error_code_name, + error_code_description: basicErrorCodeData.error_code_description || '', + error_code_color: basicErrorCodeData.error_code_color && basicErrorCodeData.error_code_color !== '' ? basicErrorCodeData.error_code_color : '#000000', + status: basicErrorCodeData.is_active, + }; + + errorCodeForm.setFieldsValue(formValues); + + if (basicErrorCodeData.path_icon && basicErrorCodeData.path_icon !== '') { + const iconData = { + name: basicErrorCodeData.path_icon.split('/').pop(), + uploadPath: basicErrorCodeData.path_icon, + }; + setErrorCodeIcon(iconData); + } else { + setErrorCodeIcon(null); + } + + resetSolutionFields(); + setSelectedSparepartIds([]); + } + } catch (error) { + const basicErrorCodeData = { + ...errorCode, + tempId: `existing_${errorCode.error_code_id}` + }; + + const formValues = { + error_code: basicErrorCodeData.error_code, + error_code_name: basicErrorCodeData.error_code_name, + error_code_description: basicErrorCodeData.error_code_description || '', + error_code_color: basicErrorCodeData.error_code_color && basicErrorCodeData.error_code_color !== '' ? basicErrorCodeData.error_code_color : '#000000', + status: basicErrorCodeData.is_active, + }; + + errorCodeForm.setFieldsValue(formValues); + + if (basicErrorCodeData.path_icon && basicErrorCodeData.path_icon !== '') { + const iconData = { + name: basicErrorCodeData.path_icon.split('/').pop(), + uploadPath: basicErrorCodeData.path_icon, + }; + setErrorCodeIcon(iconData); + } else { + setErrorCodeIcon(null); + } + + resetSolutionFields(); + setSelectedSparepartIds([]); + } + }; + + const handleBrandFormValuesChange = useCallback((changedValues, allValues) => { + setBrandInfo(allValues); + }, [setBrandInfo]); + + const handleSearch = () => { + setTrigerFilter((prev) => !prev); + }; + + const handleSearchClear = () => { + setSearchText(''); + setTrigerFilter((prev) => !prev); + }; + + const handleFileView = (fileName) => { + try { + let fileUrl = ''; + let actualFileName = ''; + + const filePath = fileName || ''; + if (filePath) { + actualFileName = filePath.split('/').pop(); + } + + if (actualFileName) { + const fileExtension = actualFileName.split('.').pop()?.toLowerCase(); + const folder = getFolderFromFileType(fileExtension); + fileUrl = getFileUrl(folder, actualFileName); + } + + if (!fileUrl && filePath) { + fileUrl = filePath.startsWith('http') ? filePath : `${import.meta.env.VITE_API_SERVER}/${filePath}`; + } + + if (fileUrl && actualFileName) { + const fileExtension = actualFileName.split('.').pop()?.toLowerCase(); + const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']; + const pdfExtensions = ['pdf']; + + if (imageExtensions.includes(fileExtension) || pdfExtensions.includes(fileExtension)) { + const viewerUrl = `/image-viewer/${encodeURIComponent(actualFileName)}`; + window.open(viewerUrl, '_blank', 'noopener,noreferrer'); + } else { + window.open(fileUrl, '_blank', 'noopener,noreferrer'); + } + } else { + NotifAlert({ + icon: 'error', + title: 'Error', + message: 'File URL not found' + }); + } + } catch (error) { + NotifAlert({ + icon: 'error', + title: 'Error', + message: 'Failed to open file preview' + }); + } + }; + + const handleNextStep = () => { + setCurrentStep(1); }; const renderStepContent = () => { if (currentStep === 0) { return ( -
-
- Status -
-
-
-
+ {loading && ( +
+ top: 0, + left: 0, + right: 0, + bottom: 0, + backgroundColor: 'rgba(255, 255, 255, 0.7)', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + zIndex: 10, + borderRadius: '8px', + }} + > +
- {(brandData || {}).is_active ? 'Running' : 'Offline'} -
- -
- Brand Code -
-
-
- {brandData?.brand_code || (loading ? 'Loading...' : '-')} -
-
- - - -
- Brand Name -
-
-
- {brandData?.brand_name || (loading ? 'Loading...' : '-')} -
-
- - -
- Manufacture -
-
-
- {brandData?.brand_manufacture || (loading ? 'Loading...' : '-')} -
-
- -
- - - -
- Brand Type -
-
-
- {brandData?.brand_type || (loading ? 'Loading...' : '-')} -
-
- - -
- Model -
-
-
- {brandData?.brand_model || (loading ? 'Loading...' : '-')} -
-
- -
+ )} +
); } if (currentStep === 1) { - const errorCodes = brandData?.error_code || []; - return ( -
- - Error Codes ({errorCodes.length}) - - - {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 + + + { + setSearchText(value); + if (value === '') { + setTrigerFilter((prev) => !prev); } - /> - ) - )} -
+ }} + onSearch={handleSearch} + onSearchClear={handleSearchClear} + isReadOnly={true} + /> + + + +
+ {selectedErrorCode ? ( + + + Error Code Form + + } + style={{ + width: '100%', + boxShadow: '0 2px 8px rgba(0,0,0,0.06)', + borderRadius: '12px' + }} + styles={{ + body: { padding: '16px 24px 12px 24px' }, + header: { + padding: '16px 24px', + borderBottom: '1px solid #f0f0f0', + backgroundColor: '#fafafa' + } + }} + > +
+
+
+
+

+ Error Code Details +

+
+ { }} + onErrorCodeIconRemove={() => { }} + isEdit={true} + /> +
+ + + +
+
+
+

+ Solution +

+
+ { }} + onRemoveSolutionField={() => { }} + onSolutionTypeChange={() => { }} + onSolutionStatusChange={() => { }} + onSolutionFileUpload={() => { }} + onFileView={(fileData) => { + if (fileData && (fileData.url || fileData.uploadPath)) { + window.open(fileData.url || fileData.uploadPath, '_blank'); + } + }} + isReadOnly={true} + solutionData={currentSolutionData} + /> +
+ + +
+
+
+

+ Sparepart Selection +

+
+
+ { }} + isReadOnly={true} + /> +
+
+ +
+
+
+ ) : ( +
+ +
+ )} +
+ + ); } - return null; }; return ( - + - - - View Brand Device - - - - - - - - - -
- {loading && ( -
- -
- )} - -
- {renderStepContent()} + {renderStepContent()} + +
+
+ {currentStep === 1 && ( + + )} +
+
+ {currentStep === 0 && ( + + )} + {currentStep === 1 && ( + + )}
- - -
- {currentStep > 0 && ( - - )} - {currentStep < 1 && ( - - )} -
- + ); }; diff --git a/src/pages/master/brandDevice/ViewFilePage.jsx b/src/pages/master/brandDevice/ViewFilePage.jsx index 16de7a7..b46b56b 100644 --- a/src/pages/master/brandDevice/ViewFilePage.jsx +++ b/src/pages/master/brandDevice/ViewFilePage.jsx @@ -6,10 +6,10 @@ import { ArrowLeftOutlined, FilePdfOutlined, FileImageOutlined, DownloadOutlined import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb'; import { getBrandById } from '../../../api/master-brand'; import { - downloadFile, - getFile, - getFileUrl, - getFolderFromFileType, + downloadFile, + getFile, + getFileUrl, + getFolderFromFileType, } from '../../../api/file-uploads'; const { Title } = Typography; @@ -412,7 +412,7 @@ const ViewFilePage = () => {
- {/* File type indicator */} +
{
- {/* Overlay with blur effect during loading */} + {loading && (
{ const isActive = Form.useWatch('is_active', form) ?? true; @@ -38,6 +39,7 @@ const BrandForm = ({ @@ -61,18 +63,18 @@ const BrandForm = ({ - + - + @@ -80,12 +82,12 @@ const BrandForm = ({ - + - + diff --git a/src/pages/master/brandDevice/component/CustomSparepartCard.jsx b/src/pages/master/brandDevice/component/CustomSparepartCard.jsx index 5327cee..1dea0a5 100644 --- a/src/pages/master/brandDevice/component/CustomSparepartCard.jsx +++ b/src/pages/master/brandDevice/component/CustomSparepartCard.jsx @@ -140,7 +140,7 @@ const CustomSparepartCard = ({ onClick={handleCardClick} >
- {/* Image Section */} +
- {/* Selection Indicator */} + {isSelected && (
- {/* Content Section */} +
- {/* Stock and Quantity Information */} +
- {/* Stock Status Tag */} +
- {/* Quantity */} +
- {/* Preview Modal */} +
- {/* Item Type Tag */} + {sparepart.sparepart_item_type && (
@@ -383,7 +383,7 @@ const CustomSparepartCard = ({
)} - {/* Stock Status and Quantity */} +
- {/* Sparepart Name */} + {sparepart.sparepart_name || 'Unnamed'} - {/* Basic Information */} +
@@ -480,7 +480,7 @@ const CustomSparepartCard = ({
- {/* Timeline Information */} + {sparepart.created_at && (
diff --git a/src/pages/master/brandDevice/component/ErrorCodeForm.jsx b/src/pages/master/brandDevice/component/ErrorCodeForm.jsx index 2030332..0dbf604 100644 --- a/src/pages/master/brandDevice/component/ErrorCodeForm.jsx +++ b/src/pages/master/brandDevice/component/ErrorCodeForm.jsx @@ -232,15 +232,16 @@ const ErrorCodeForm = ({ - + @@ -260,7 +261,9 @@ const ErrorCodeForm = ({ height: '40px', border: '1px solid #d9d9d9', borderRadius: 4, + cursor: isErrorCodeFormReadOnly ? 'not-allowed' : 'pointer', }} + disabled={isErrorCodeFormReadOnly} /> diff --git a/src/pages/master/brandDevice/component/FileUploadHandler.jsx b/src/pages/master/brandDevice/component/FileUploadHandler.jsx index b0aad6c..6e5b0e0 100644 --- a/src/pages/master/brandDevice/component/FileUploadHandler.jsx +++ b/src/pages/master/brandDevice/component/FileUploadHandler.jsx @@ -147,7 +147,7 @@ const FileUploadHandler = ({ } } - + if (actualPath) { let fileObject; @@ -394,9 +394,7 @@ const FileUploadHandler = ({ )} - {/* renderExistingFile() is disabled because SolutionField.jsx already handles file card rendering */} - {/* This prevents duplicate card rendering */} - {/* {renderExistingFile()} */} + {showPreview && ( handleDelete(param.brand_id, param.brand_name), - onCancel: () => {}, + onCancel: () => { }, }); }; @@ -156,7 +156,7 @@ const ListBrandDevice = memo(function ListBrandDevice(props) { title: 'Berhasil', message: `Brand ${brand_name} deleted successfully.`, }); - doFilter(); // Refresh data + doFilter(); } else { NotifAlert({ icon: 'error', diff --git a/src/pages/master/brandDevice/component/ListErrorCode.jsx b/src/pages/master/brandDevice/component/ListErrorCode.jsx index a1f1d01..85fe86c 100644 --- a/src/pages/master/brandDevice/component/ListErrorCode.jsx +++ b/src/pages/master/brandDevice/component/ListErrorCode.jsx @@ -14,7 +14,9 @@ const ListErrorCode = ({ searchText, onSearchChange, onSearch, - onSearchClear + onSearchClear, + isReadOnly = false, + errorCodes: propErrorCodes = null }) => { const [errorCodes, setErrorCodes] = useState([]); const [loading, setLoading] = useState(false); @@ -25,7 +27,7 @@ const ListErrorCode = ({ total_page: 0, }); const [currentPage, setCurrentPage] = useState(1); - const pageSize = 15; // Fixed limit 15 items per page + const pageSize = 15; const queryParams = useMemo(() => { const params = new URLSearchParams(); @@ -79,8 +81,15 @@ const ListErrorCode = ({ }; useEffect(() => { - fetchErrorCodes(); - }, [brandId, queryParams, tempErrorCodes, trigerFilter]); + if (isReadOnly && propErrorCodes) { + + setErrorCodes(propErrorCodes); + setLoading(false); + } else { + + fetchErrorCodes(); + } + }, [brandId, queryParams, tempErrorCodes, trigerFilter, isReadOnly, propErrorCodes]); const handlePrevious = () => { if (pagination.current_page > 1) { @@ -117,7 +126,7 @@ const ListErrorCode = ({ title: 'Hapus Error Code', message: `Apakah Anda yakin ingin menghapus error code ${item.error_code}?`, onConfirm: () => performDelete(item), - onCancel: () => {}, + onCancel: () => { }, confirmButtonText: 'Hapus' }); } @@ -125,7 +134,7 @@ const ListErrorCode = ({ const performDelete = async (item) => { try { - // Additional validation + if (!item.error_code_id || item.error_code_id === 'undefined') { NotifAlert({ icon: 'error', @@ -144,7 +153,7 @@ const ListErrorCode = ({ return; } - const response = await deleteErrorCode(item.brand_id, item.error_code_id); + const response = await deleteErrorCode(item.brand_id, item.error_code_id); if (response && response.statusCode === 200) { NotifOk({ diff --git a/src/pages/master/brandDevice/component/SolutionField.jsx b/src/pages/master/brandDevice/component/SolutionField.jsx index d0746b5..4ce54c1 100644 --- a/src/pages/master/brandDevice/component/SolutionField.jsx +++ b/src/pages/master/brandDevice/component/SolutionField.jsx @@ -30,28 +30,15 @@ const SolutionFieldNew = ({ const fileUpload = Form.useWatch(['solution_items', fieldKey, 'fileUpload'], form); const file = Form.useWatch(['solution_items', fieldKey, 'file'], form); - - const getSolutionData = () => { - try { - return form.getFieldValue(['solution_items', fieldKey]) || {}; - } catch (error) { - return {}; - } - }; - const nameValue = Form.useWatch(['solution_items', fieldKey, 'name'], form); - const typeValue = Form.useWatch(['solution_items', fieldKey, 'type'], form); - const textValue = Form.useWatch(['solution_items', fieldKey, 'text'], form); const fileNameValue = Form.useWatch(['solution_items', fieldKey, 'fileName'], form); const statusValue = Form.useWatch(['solution_items', fieldKey, 'status'], form) ?? true; const pathSolution = Form.useWatch(['solution_items', fieldKey, 'path_solution'], form); const [deleteCounter, setDeleteCounter] = useState(0); - - // Reset internal state when form is reset to initial values + React.useEffect(() => { - // Check if form is in initial/reset state (name is empty) if (!nameValue || nameValue === '') { setCurrentFile(null); setIsDeleted(false); diff --git a/src/pages/master/brandDevice/component/SparepartSelect.jsx b/src/pages/master/brandDevice/component/SparepartSelect.jsx index 81c3e78..709dbd4 100644 --- a/src/pages/master/brandDevice/component/SparepartSelect.jsx +++ b/src/pages/master/brandDevice/component/SparepartSelect.jsx @@ -115,7 +115,7 @@ const SparepartSelect = ({ return ( <> - {/* Fixed Search Section */} + {!isReadOnly && (
)} - {/* Scrollable Selected Items Section */} +
{selectedSpareparts.length > 0 ? (