From 4fab5df3002839eacba997bb146f5b49a627f98a Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Date: Fri, 12 Dec 2025 12:44:16 +0700 Subject: [PATCH] repair: error code brand-device --- .../brandDevice/component/ErrorCodeForm.jsx | 279 ++++++++++++++++ .../component/ErrorCodeSimpleForm.jsx | 108 ------ .../brandDevice/component/ListErrorCode.jsx | 307 ++++++++++++++++++ .../master/brandDevice/hooks/errorCode.js | 4 +- 4 files changed, 587 insertions(+), 111 deletions(-) create mode 100644 src/pages/master/brandDevice/component/ErrorCodeForm.jsx delete mode 100644 src/pages/master/brandDevice/component/ErrorCodeSimpleForm.jsx create mode 100644 src/pages/master/brandDevice/component/ListErrorCode.jsx diff --git a/src/pages/master/brandDevice/component/ErrorCodeForm.jsx b/src/pages/master/brandDevice/component/ErrorCodeForm.jsx new file mode 100644 index 0000000..666e3b1 --- /dev/null +++ b/src/pages/master/brandDevice/component/ErrorCodeForm.jsx @@ -0,0 +1,279 @@ +import React, { useState, useEffect } from 'react'; +import { Form, Input, Switch, Typography, ConfigProvider, Card, Button } from 'antd'; +import { FileOutlined, EyeOutlined, DeleteOutlined } from '@ant-design/icons'; +import FileUploadHandler from './FileUploadHandler'; +import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif'; +import { getFileUrl, getFolderFromFileType } from '../../../../api/file-uploads'; + +const { Text } = Typography; + +const ErrorCodeForm = ({ + errorCodeForm, + isErrorCodeFormReadOnly = false, + errorCodeIcon, + onErrorCodeIconUpload, + onErrorCodeIconRemove, + isEdit = false, +}) => { + const [statusValue, setStatusValue] = useState(true); + const [currentIcon, setCurrentIcon] = useState(null); + + useEffect(() => { + if (errorCodeIcon && typeof errorCodeIcon === 'object' && Object.keys(errorCodeIcon).length > 0) { + setCurrentIcon(errorCodeIcon); + } else { + setCurrentIcon(null); + } + }, [errorCodeIcon]); + + const handleIconRemove = () => { + setCurrentIcon(null); + onErrorCodeIconRemove(); + }; + + const renderIconUpload = () => { + if (currentIcon) { + const displayFileName = currentIcon.name || currentIcon.uploadPath?.split('/').pop() || currentIcon.url?.split('/').pop() || 'Icon File'; + + return ( + +
+
+ +
+ +
+
+ {displayFileName} +
+
+ {currentIcon.size ? `${(currentIcon.size / 1024).toFixed(1)} KB` : 'Icon uploaded'} +
+
+ +
+
+
+
+ ); + } else { + return ( + { + setCurrentIcon(fileData); + onErrorCodeIconUpload(fileData); + }} + onFileRemove={handleIconRemove} + buttonText="Upload Icon" + buttonStyle={{ + width: '100%', + borderColor: '#23A55A', + color: '#23A55A', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + gap: '8px' + }} + uploadText="Upload error code icon" + disabled={isErrorCodeFormReadOnly} + /> + ); + } + }; + + return ( + +
+ +
+ { + setStatusValue(checked); + }} + /> + + {statusValue ? 'Active' : 'Inactive'} + +
+
+ + + + + + + + + + + + + + +
+ e.target.value} + getValueProps={(value) => ({ value: value || '#000000' })} + > + + + + + {renderIconUpload()} + +
+ +
+
+
+ ); +}; + +export default ErrorCodeForm; diff --git a/src/pages/master/brandDevice/component/ErrorCodeSimpleForm.jsx b/src/pages/master/brandDevice/component/ErrorCodeSimpleForm.jsx deleted file mode 100644 index c87e27c..0000000 --- a/src/pages/master/brandDevice/component/ErrorCodeSimpleForm.jsx +++ /dev/null @@ -1,108 +0,0 @@ -import React from 'react'; -import { Form, Input, Switch, Typography, ConfigProvider } from 'antd'; -import FileUploadHandler from './FileUploadHandler'; -import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif'; - -const { Text } = Typography; - -const ErrorCodeSimpleForm = ({ - errorCodeForm, - isErrorCodeFormReadOnly = false, - errorCodeIcon, - onErrorCodeIconUpload, - onErrorCodeIconRemove, - onAddErrorCode, - isEdit = false, // Add isEdit prop to check if we're in edit mode -}) => { - const statusValue = Form.useWatch('status', errorCodeForm); - - const handleIconRemove = () => { - onErrorCodeIconRemove(); - }; - - return ( - <> - {/* Status Switch */} - -
- - - - {statusValue ? 'Active' : 'Inactive'} -
-
- - {/* Error Code */} - - - - - {/* Error Name */} - - - - - {/* Error Description */} - - - - - {/* Color and Icon */} - -
- - - - - - - -
- -
- - ); -}; - -export default ErrorCodeSimpleForm; diff --git a/src/pages/master/brandDevice/component/ListErrorCode.jsx b/src/pages/master/brandDevice/component/ListErrorCode.jsx new file mode 100644 index 0000000..a1f1d01 --- /dev/null +++ b/src/pages/master/brandDevice/component/ListErrorCode.jsx @@ -0,0 +1,307 @@ +import React, { useState, useEffect, useMemo } from 'react'; +import { Card, Input, Button, Row, Col, Empty } from 'antd'; +import { PlusOutlined, SearchOutlined, DeleteOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons'; +import { getErrorCodesByBrandId, deleteErrorCode } from '../../../../api/master-brand'; +import { NotifAlert, NotifOk, NotifConfirmDialog } from '../../../../components/Global/ToastNotif'; + +const ListErrorCode = ({ + brandId, + selectedErrorCode, + onErrorCodeSelect, + onAddNew, + tempErrorCodes = [], + trigerFilter, + searchText, + onSearchChange, + onSearch, + onSearchClear +}) => { + const [errorCodes, setErrorCodes] = useState([]); + const [loading, setLoading] = useState(false); + const [pagination, setPagination] = useState({ + current_page: 1, + current_limit: 15, + total_limit: 0, + total_page: 0, + }); + const [currentPage, setCurrentPage] = useState(1); + const pageSize = 15; // Fixed limit 15 items per page + + const queryParams = useMemo(() => { + const params = new URLSearchParams(); + params.set('page', currentPage.toString()); + params.set('limit', pageSize.toString()); + if (searchText) { + params.set('criteria', searchText); + } + return params; + }, [searchText, currentPage, pageSize]); + + const fetchErrorCodes = async () => { + if (!brandId) { + setErrorCodes([]); + return; + } + + setLoading(true); + try { + const response = await getErrorCodesByBrandId(brandId, queryParams); + + if (response && response.statusCode === 200) { + const apiErrorData = response.data || []; + const allErrorCodes = [ + ...apiErrorData.map(ec => ({ + ...ec, + tempId: `existing_${ec.error_code_id}`, + status: 'existing' + })), + ...tempErrorCodes.filter(ec => ec.status !== 'deleted') + ]; + + setErrorCodes(allErrorCodes); + + if (response.paging) { + setPagination({ + current_page: response.paging.current_page || 1, + current_limit: response.paging.current_limit || 15, + total_limit: response.paging.total_limit || 0, + total_page: response.paging.total_page || 0, + }); + } + } else { + setErrorCodes([]); + } + } catch (error) { + setErrorCodes([]); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchErrorCodes(); + }, [brandId, queryParams, tempErrorCodes, trigerFilter]); + + const handlePrevious = () => { + if (pagination.current_page > 1) { + setCurrentPage(pagination.current_page - 1); + } + }; + + const handleNext = () => { + if (pagination.current_page < pagination.total_page) { + setCurrentPage(pagination.current_page + 1); + } + }; + + const handleSearch = () => { + setCurrentPage(1); + if (onSearch) { + onSearch(); + } + }; + + const handleSearchClear = () => { + setCurrentPage(1); + if (onSearchClear) { + onSearchClear(); + } + }; + + const handleDelete = async (item, e) => { + e.stopPropagation(); + + if (item.status === 'existing' && item.error_code_id) { + NotifConfirmDialog({ + icon: 'warning', + title: 'Hapus Error Code', + message: `Apakah Anda yakin ingin menghapus error code ${item.error_code}?`, + onConfirm: () => performDelete(item), + onCancel: () => {}, + confirmButtonText: 'Hapus' + }); + } + }; + + const performDelete = async (item) => { + try { + // Additional validation + if (!item.error_code_id || item.error_code_id === 'undefined') { + NotifAlert({ + icon: 'error', + title: 'Error', + message: 'Error code ID tidak valid' + }); + return; + } + + if (!item.brand_id || item.brand_id === 'undefined') { + NotifAlert({ + icon: 'error', + title: 'Error', + message: 'Brand ID tidak valid' + }); + return; + } + + const response = await deleteErrorCode(item.brand_id, item.error_code_id); + + if (response && response.statusCode === 200) { + NotifOk({ + icon: 'success', + title: 'Berhasil', + message: 'Error code berhasil dihapus' + }); + fetchErrorCodes(); + } else { + NotifAlert({ + icon: 'error', + title: 'Gagal', + message: 'Gagal menghapus error code' + }); + } + } catch (error) { + NotifAlert({ + icon: 'error', + title: 'Error', + message: 'Terjadi kesalahan saat menghapus error code' + }); + } + }; + + return ( + + { + const value = e.target.value; + if (onSearchChange) { + onSearchChange(value); + } + }} + onSearch={handleSearch} + allowClear + enterButton={ + + } + size="default" + style={{ + marginBottom: 12, + height: '32px', + width: '100%', + maxWidth: '300px' + }} + /> + +
+ {errorCodes.length === 0 ? ( + + ) : ( +
+ {errorCodes.map((item) => ( +
onErrorCodeSelect(item)} + > +
+
+
+ {item.error_code} +
+
+ {item.error_code_name} +
+
+ {item.status === 'existing' && ( +
+
+ ))} +
+ )} +
+ + {pagination.total_limit > 0 && ( + + + + Menampilkan {pagination.current_limit} data halaman{' '} + {pagination.current_page} dari total {pagination.total_limit} data + + + +
+ + + {pagination.current_page} / {pagination.total_page} + + +
+ +
+ )} +
+ ); +}; + +export default ListErrorCode; \ No newline at end of file diff --git a/src/pages/master/brandDevice/hooks/errorCode.js b/src/pages/master/brandDevice/hooks/errorCode.js index 0d748d5..7a82ee5 100644 --- a/src/pages/master/brandDevice/hooks/errorCode.js +++ b/src/pages/master/brandDevice/hooks/errorCode.js @@ -194,7 +194,6 @@ export const useErrorCodeLogic = (errorCodeForm, fileList) => { }; const handleSolutionStatusChange = (fieldId, status) => { - // Only update local state - form is already updated by Form.Item setSolutionStatuses(prev => ({ ...prev, [fieldId]: status @@ -213,8 +212,7 @@ export const useErrorCodeLogic = (errorCodeForm, fileList) => { newSolutionTypes[fieldId] = solution.type_solution || 'text'; newSolutionStatuses[fieldId] = solution.is_active !== false; newSolutionData[fieldId] = { - ...solution, - brand_code_solution_id: solution.brand_code_solution_id + ...solution }; setTimeout(() => {