From 8afff23ffeef24ba58a4b54e68c558d96a038e91 Mon Sep 17 00:00:00 2001 From: Antony Kurniawan Date: Fri, 12 Dec 2025 12:45:46 +0700 Subject: [PATCH] update: brand device --- .../master/brandDevice/AddBrandDevice.jsx | 947 +++++++++++++----- .../master/brandDevice/AddEditErrorCode.jsx | 46 +- .../master/brandDevice/EditBrandDevice.jsx | 793 +++++++++++---- src/pages/master/brandDevice/ViewFilePage.jsx | 9 - .../brandDevice/component/BrandForm.jsx | 9 + .../component/FileUploadHandler.jsx | 32 +- .../brandDevice/component/ListBrandDevice.jsx | 16 +- 7 files changed, 1388 insertions(+), 464 deletions(-) diff --git a/src/pages/master/brandDevice/AddBrandDevice.jsx b/src/pages/master/brandDevice/AddBrandDevice.jsx index d4aced7..73a5124 100644 --- a/src/pages/master/brandDevice/AddBrandDevice.jsx +++ b/src/pages/master/brandDevice/AddBrandDevice.jsx @@ -1,5 +1,5 @@ import { useEffect, useState, useCallback, useMemo } from 'react'; -import { useNavigate, useSearchParams } from 'react-router-dom'; +import { useNavigate, useParams, useSearchParams, useLocation } from 'react-router-dom'; import { Divider, Typography, @@ -14,56 +14,221 @@ import { Space, Input, } from 'antd'; -import { EyeOutlined, EditOutlined, DeleteOutlined, PlusOutlined, SearchOutlined } from '@ant-design/icons'; -import TableList from '../../../components/Global/TableList'; -import { ConfigProvider } from 'antd'; +import { EyeOutlined, EditOutlined, DeleteOutlined, SearchOutlined } from '@ant-design/icons'; import { NotifAlert, NotifOk, NotifConfirmDialog } from '../../../components/Global/ToastNotif'; import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb'; -import { createBrand, createErrorCode, getErrorCodesByBrandId, updateErrorCode, deleteErrorCode } from '../../../api/master-brand'; +import { getBrandById, createBrand, createErrorCode, getErrorCodesByBrandId, updateErrorCode, deleteErrorCode, deleteBrand } from '../../../api/master-brand'; +import { getFileUrl } from '../../../api/file-uploads'; +import { SendRequest } from '../../../components/Global/ApiRequest'; import BrandForm from './component/BrandForm'; -import { useSolutionLogic } from './hooks/solution'; +import ErrorCodeForm from './component/ErrorCodeForm'; +import SolutionForm from './component/SolutionForm'; +import SparepartSelect from './component/SparepartSelect'; +import ListErrorCode from './component/ListErrorCode'; +import FormActions from './component/FormActions'; const { Title } = Typography; const { Step } = Steps; const AddBrandDevice = () => { const navigate = useNavigate(); + const { id } = useParams(); const [searchParams] = useSearchParams(); + const location = useLocation(); const { setBreadcrumbItems } = useBreadcrumb(); const [brandForm] = Form.useForm(); - const [loading, setLoading] = useState(false); - const [confirmLoading, setConfirmLoading] = useState(false); - const [searchValue, setSearchValue] = useState(''); - const [searchText, setSearchText] = useState(''); - const [trigerFilter, setTrigerFilter] = useState(false); - const [currentStep, setCurrentStep] = useState(0); - const [createdBrandId, setCreatedBrandId] = useState(null); - const [brandData, setBrandData] = useState({}); - const [tempErrorCodes, setTempErrorCodes] = useState([]); - - // Error code management states const [errorCodeForm] = Form.useForm(); const [solutionForm] = Form.useForm(); const [errorCodeIcon, setErrorCodeIcon] = useState(null); const [selectedSparepartIds, setSelectedSparepartIds] = useState([]); + const [loading, setLoading] = useState(false); + const tab = searchParams.get('tab'); + const [currentStep, setCurrentStep] = useState(tab === 'error-codes' ? 1 : (location.state?.phase || 0)); const [editingErrorCodeKey, setEditingErrorCodeKey] = useState(null); const [isErrorCodeFormReadOnly, setIsErrorCodeFormReadOnly] = useState(false); + const [searchText, setSearchText] = useState(''); + const [apiErrorCodes, setApiErrorCodes] = useState([]); + const [trigerFilter, setTrigerFilter] = useState(false); + const [searchValue, setSearchValue] = useState(''); + const [brandInfo, setBrandInfo] = useState({}); + const [tempErrorCodes, setTempErrorCodes] = useState([]); + const [existingErrorCodes, setExistingErrorCodes] = useState([]); + const [selectedErrorCode, setSelectedErrorCode] = useState(null); + const [solutionFields, setSolutionFields] = useState([0]); + const [solutionTypes, setSolutionTypes] = useState({ 0: 'text' }); + const [solutionStatuses, setSolutionStatuses] = useState({ 0: true }); + const [currentSolutionData, setCurrentSolutionData] = useState([]); + const [confirmLoading, setConfirmLoading] = useState(false); + const [currentPage, setCurrentPage] = useState(1); + const [pageSize, setPageSize] = useState(10); + const [temporaryBrandId, setTemporaryBrandId] = useState(null); + const [isTemporaryBrand, setIsTemporaryBrand] = useState(false); const [isAddingNewErrorCode, setIsAddingNewErrorCode] = useState(false); - const { - solutionFields, - solutionTypes, - resetSolutionFields, - getSolutionData, - setSolutionsForExistingRecord, - } = useSolutionLogic(solutionForm); + 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 []; + } + }; + + const resetSolutionFields = () => { + if (solutionForm && solutionForm.resetFields) { + solutionForm.resetFields(); + solutionForm.setFieldsValue({ + solution_items: { + 0: { + name: '', + type: 'text', + text: '', + status: true + } + } + }); + } + setCurrentSolutionData([]); + }; + + const setSolutionsForExistingRecord = (solutions, targetForm) => { + + if (!targetForm || !solutions || solutions.length === 0) { + return; + } + + 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 !== false; + + 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 !== false, + 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 handleAddSolutionField = () => { + const newKey = Math.max(...solutionFields, 0) + 1; + setSolutionFields(prev => [...prev, newKey]); + setSolutionTypes(prev => ({ ...prev, [newKey]: 'text' })); + setSolutionStatuses(prev => ({ ...prev, [newKey]: true })); + }; + + const handleRemoveSolutionField = (fieldKey) => { + if (solutionFields.length > 1) { + setSolutionFields(prev => prev.filter(key => key !== fieldKey)); + const newTypes = { ...solutionTypes }; + const newStatuses = { ...solutionStatuses }; + delete newTypes[fieldKey]; + delete newStatuses[fieldKey]; + setSolutionTypes(newTypes); + setSolutionStatuses(newStatuses); + + const currentValues = solutionForm.getFieldsValue(); + if (currentValues.solution_items && currentValues.solution_items[fieldKey]) { + delete currentValues.solution_items[fieldKey]; + solutionForm.setFieldsValue(currentValues); + } + } + }; + + const handleSolutionTypeChange = (fieldKey, type) => { + setSolutionTypes(prev => ({ ...prev, [fieldKey]: type })); + + if (type === 'file') { + solutionForm.setFieldValue(['solution_items', fieldKey, 'text'], ''); + } + + if (type === 'text') { + solutionForm.setFieldValue(['solution_items', fieldKey, 'file'], null); + solutionForm.setFieldValue(['solution_items', fieldKey, 'fileUpload'], null); + } + }; + + const handleSolutionStatusChange = (fieldKey, status) => { + setSolutionStatuses(prev => ({ ...prev, [fieldKey]: status })); + }; - // Navigation functions const handleNextStep = async () => { try { setConfirmLoading(true); const brandValues = await brandForm.validateFields(); - const userId = JSON.parse(localStorage.getItem('user') || '{}').user_id || 1; const brandApiData = { brand_name: brandValues.brand_name, @@ -73,37 +238,37 @@ const AddBrandDevice = () => { is_active: brandValues.is_active !== undefined ? brandValues.is_active : true }; - // Create brand via API const response = await createBrand(brandApiData); if (response && (response.statusCode === 200 || response.statusCode === 201)) { - const createdBrand = response.data; - setCreatedBrandId(createdBrand.brand_id); - setBrandData(createdBrand); + const newBrandInfo = { + ...brandValues, + brand_id: response.data.brand_id, + brand_code: response.data.brand_code + }; + setBrandInfo(newBrandInfo); + setTemporaryBrandId(response.data.brand_id); + setIsTemporaryBrand(true); + setCurrentStep(1); + localStorage.setItem(`brand_device_add_last_phase`, '1'); - NotifOk({ icon: 'success', - title: 'Berhasil', - message: 'Brand device berhasil dibuat. Silakan tambahkan error codes.', + title: 'Brand Created', + message: 'Brand berhasil dibuat. Silakan tambahkan minimal 1 error code.', }); - - setCurrentStep(1); - setTimeout(() => { - setTrigerFilter(prev => !prev); - }, 100); } else { NotifAlert({ icon: 'error', title: 'Gagal', - message: response?.message || 'Gagal membuat brand device', + message: response?.message || 'Gagal membuat brand', }); } } catch (error) { NotifAlert({ icon: 'error', - title: 'Gagal', - message: error.message || 'Gagal membuat brand device', + title: 'Validasi Error', + message: error.message || 'Silakan lengkapi semua field yang wajib diisi', }); } finally { setConfirmLoading(false); @@ -114,56 +279,53 @@ const AddBrandDevice = () => { setCurrentStep(0); }; - const handleCancel = () => { + const handleCancel = async () => { + if (isTemporaryBrand && temporaryBrandId) { + try { + await deleteBrand(temporaryBrandId); + } catch (error) { + } + } navigate('/master/brand-device'); }; const handleAddErrorCode = () => { - if (createdBrandId) { - resetErrorCodeForm(); - setIsAddingNewErrorCode(true); - setIsErrorCodeFormReadOnly(false); - setEditingErrorCodeKey(null); - } else { - NotifAlert({ - icon: 'warning', - title: 'Perhatian', - message: 'Brand device harus dibuat terlebih dahulu.', + resetErrorCodeForm(); + setIsErrorCodeFormReadOnly(false); + setEditingErrorCodeKey(null); + }; + + const loadErrorCodeData = (record) => { + setIsErrorCodeFormReadOnly(false); + setEditingErrorCodeKey(record.error_code_id); + + errorCodeForm.setFieldsValue({ + error_code: record.error_code, + error_code_name: record.error_code_name || '', + error_code_description: record.error_code_description || '', + error_code_color: record.error_code_color || '#000000', + status: record.is_active !== false, + }); + + if (record.path_icon) { + setErrorCodeIcon({ + name: record.path_icon.split('/').pop(), + uploadPath: record.path_icon, + url: record.path_icon, }); } + + if (record.solution && record.solution.length > 0) { + setSolutionsForExistingRecord(record.solution, solutionForm); + } + + if (record.spareparts && record.spareparts.length > 0) { + setSelectedSparepartIds(record.spareparts); + } }; const handleEditErrorCode = (record) => { - if (createdBrandId) { - setIsAddingNewErrorCode(false); - setIsErrorCodeFormReadOnly(false); - setEditingErrorCodeKey(record.error_code_id); - - // Load error code data into form - errorCodeForm.setFieldsValue({ - error_code: record.error_code, - error_code_name: record.error_code_name || '', - error_code_description: record.error_code_description || '', - error_code_color: record.error_code_color || '#000000', - status: record.is_active !== false, - }); - - if (record.path_icon) { - setErrorCodeIcon({ - name: record.path_icon.split('/').pop(), - uploadPath: record.path_icon, - url: record.path_icon, - }); - } - - if (record.solution && record.solution.length > 0) { - setSolutionsForExistingRecord(record.solution, solutionForm); - } - - if (record.spareparts && record.spareparts.length > 0) { - setSelectedSparepartIds(record.spareparts); - } - } + loadErrorCodeData(record); }; const handleDeleteErrorCode = (record) => { @@ -174,7 +336,7 @@ const AddBrandDevice = () => { onConfirm: async () => { try { const errorCodeToDelete = record.error_code_id; - const response = await deleteErrorCode(createdBrandId, errorCodeToDelete); + const response = await deleteErrorCode(brandInfo.brand_id || id, errorCodeToDelete); if (response && (response.statusCode === 200 || response.statusCode === 201)) { NotifOk({ @@ -191,7 +353,6 @@ const AddBrandDevice = () => { }); } } catch (error) { - console.error('Error deleting error code:', error); NotifAlert({ icon: 'error', title: 'Gagal', @@ -199,12 +360,11 @@ const AddBrandDevice = () => { }); } }, - onCancel: () => {} + onCancel: () => { } }); }; const handlePreviewErrorCode = (record) => { - console.log('Preview error code:', record); }; const handleSearch = () => { @@ -219,25 +379,30 @@ const AddBrandDevice = () => { }; const handleBrandFormValuesChange = useCallback((changedValues, allValues) => { - setBrandData(allValues); }, []); const getErrorCodesData = async (params) => { try { const search = params.get('search') || ''; - const page = parseInt(params.get('page')) || 1; - const limit = parseInt(params.get('limit')) || 10; + const page = parseInt(params.get('page')) || currentPage; + const limit = parseInt(params.get('limit')) || pageSize; let allErrorCodes = []; + let paginationData = { + current_page: page, + current_limit: limit, + total_limit: 0, + total_page: 0, + }; - if (createdBrandId) { + if (brandInfo.brand_id) { const queryParams = new URLSearchParams({ page: page.toString(), limit: limit.toString(), ...(search && { search }) }); - const response = await getErrorCodesByBrandId(createdBrandId, queryParams); + const response = await getErrorCodesByBrandId(brandInfo.brand_id, queryParams); if (response && response.statusCode === 200) { const apiErrorData = response.data || []; allErrorCodes = apiErrorData.map(ec => ({ @@ -245,6 +410,15 @@ const AddBrandDevice = () => { tempId: `existing_${ec.error_code_id}`, status: 'existing' })); + + if (response.paging) { + paginationData = { + current_page: response.paging.current_page || page, + current_limit: response.paging.current_limit || limit, + total_limit: response.paging.total_limit || 0, + total_page: response.paging.total_page || 0, + }; + } } } @@ -255,28 +429,20 @@ const AddBrandDevice = () => { ec.error_code.toLowerCase().includes(searchText.toLowerCase()) || ec.error_code_name.toLowerCase().includes(searchText.toLowerCase()) ); + paginationData.total_limit = allErrorCodes.length; + paginationData.total_page = Math.ceil(allErrorCodes.length / limit); } - const startIndex = 0; - const endIndex = startIndex + limit; - const paginatedData = allErrorCodes.slice(startIndex, endIndex); - return { - data: paginatedData, - pagination: { - current_page: page, - current_limit: limit, - total_limit: allErrorCodes.length, - total_page: Math.ceil(allErrorCodes.length / limit), - } + data: allErrorCodes, + paging: paginationData }; } catch (error) { - console.error('Error getting error codes data:', error); return { data: [], - pagination: { + paging: { current_page: 1, - current_limit: 10, + current_limit: pageSize, total_limit: 0, total_page: 0, } @@ -284,7 +450,6 @@ const AddBrandDevice = () => { } }; - // Error code columns const errorCodeColumns = (showPreviewModal, showEditModal, showDeleteDialog) => [ { title: 'No', @@ -349,13 +514,18 @@ const AddBrandDevice = () => { const queryParams = useMemo(() => { const params = new URLSearchParams(); - params.set('page', '1'); - params.set('limit', '10'); + params.set('page', currentPage.toString()); + params.set('limit', pageSize.toString()); if (searchValue) { params.set('search', searchValue); } return params; - }, [searchValue]); + }, [searchValue, currentPage, pageSize]); + + const handlePaginationChange = (page, size) => { + setCurrentPage(page); + setPageSize(size); + }; const resetErrorCodeForm = () => { errorCodeForm.resetFields(); @@ -367,66 +537,93 @@ const AddBrandDevice = () => { setIsErrorCodeFormReadOnly(false); setEditingErrorCodeKey(null); setSelectedSparepartIds([]); - setIsAddingNewErrorCode(false); + setIsAddingNewErrorCode(true); }; const handleSaveErrorCode = async () => { try { - await errorCodeForm.validateFields(); + setConfirmLoading(true); + const errorCodeValues = await errorCodeForm.validateFields(); + const solutionData = getSolutionData(); - const solutionValues = solutionForm.getFieldsValue(); - const commaPath = `solution_items,${solutionFields[0]?.key || 0}`; - const dotPath = `solution_items.${solutionFields[0]?.key || 0}`; - const firstSolution = solutionValues[commaPath] || solutionValues[dotPath]; - - let isValid = false; - if (firstSolution && firstSolution.name && firstSolution.name.trim() !== '') { - const firstSolutionType = solutionTypes[solutionFields[0]?.key || 0]; - if (firstSolutionType === 'text') { - isValid = firstSolution.text && firstSolution.text.trim() !== ''; - } else { - isValid = true; - } - } - - if (!isValid) { + if (!errorCodeValues.error_code || !errorCodeValues.error_code_name) { NotifAlert({ icon: 'warning', title: 'Perhatian', - message: 'Harap lengkapi minimal 1 solution', + message: 'Error code dan error name wajib diisi!', }); return; } - const errorCodeValues = errorCodeForm.getFieldsValue(); - const solutionData = getSolutionData(); + if (!solutionData || solutionData.length === 0) { + NotifAlert({ + icon: 'warning', + title: 'Perhatian', + message: 'Setiap error code harus memiliki minimal 1 solution!', + }); + return; + } + + const formattedSolutions = solutionData.map(solution => { + const solutionType = solution.type || 'text'; + + let typeSolution = solutionType === 'text' ? 'text' : 'image'; + if (solution.file && solution.file.type_solution) { + typeSolution = solution.file.type_solution; + } else if (solution.fileUpload && solution.fileUpload.type_solution) { + typeSolution = solution.fileUpload.type_solution; + } + + const formattedSolution = { + solution_name: solution.name, + type_solution: typeSolution, + is_active: solution.status !== false, + }; + + if (typeSolution === 'text') { + formattedSolution.text_solution = solution.text || ''; + formattedSolution.path_solution = ''; + } else { + formattedSolution.text_solution = ''; + + formattedSolution.path_solution = solution.path_solution || solution.file?.uploadPath || solution.fileUpload?.uploadPath || ''; + } + + if (formattedSolution.brand_code_solution_id) { + delete formattedSolution.brand_code_solution_id; + } + + return formattedSolution; + }); const payload = { error_code_name: errorCodeValues.error_code_name, error_code_description: errorCodeValues.error_code_description || '', error_code_color: errorCodeValues.error_code_color || '#000000', path_icon: errorCodeIcon?.uploadPath || '', - is_active: errorCodeValues.status !== undefined ? errorCodeValues.status : true, - solution: solutionData || [], + is_active: errorCodeValues.status === undefined ? true : errorCodeValues.status, + solution: formattedSolutions, spareparts: selectedSparepartIds || [] }; - if (isAddingNewErrorCode) { + + if (!editingErrorCodeKey || !editingErrorCodeKey.startsWith('existing_')) { payload.error_code = errorCodeValues.error_code; } let response; - if (isAddingNewErrorCode) { - response = await createErrorCode(createdBrandId, payload); + if (editingErrorCodeKey && editingErrorCodeKey.startsWith('existing_')) { + const errorCodeId = editingErrorCodeKey.replace('existing_', ''); + response = await updateErrorCode(brandInfo.brand_id || id, errorCodeId, payload); } else { - response = await updateErrorCode(createdBrandId, editingErrorCodeKey, payload); + response = await createErrorCode(brandInfo.brand_id || id, payload); } if (response && (response.statusCode === 200 || response.statusCode === 201)) { NotifOk({ icon: 'success', title: 'Berhasil', - message: isAddingNewErrorCode ? 'Error Code berhasil ditambahkan!' : 'Error Code berhasil diupdate!', + message: editingErrorCodeKey ? 'Error code berhasil diupdate!' : 'Error code berhasil ditambahkan!', }); resetErrorCodeForm(); @@ -439,35 +636,99 @@ const AddBrandDevice = () => { }); } } catch (error) { - console.error('Error saving error code:', error); NotifAlert({ - icon: 'error', - title: 'Gagal', - message: error.message || 'Gagal menyimpan error code. Silakan coba lagi.', + icon: 'warning', + title: 'Perhatian', + message: error.message || 'Harap isi semua kolom wajib!', }); + } finally { + setConfirmLoading(false); } }; - + const handleErrorCodeIconRemove = () => { setErrorCodeIcon(null); }; - + const handleFinish = async () => { setConfirmLoading(true); try { + if (!brandInfo.brand_id) { + NotifAlert({ + icon: 'error', + title: 'Error', + message: 'Brand tidak ditemukan. Silakan refresh halaman.', + }); + return; + } + + if (brandInfo.brand_id) { + try { + const queryParams = new URLSearchParams(); + queryParams.set('page', '1'); + queryParams.set('limit', '15'); + + const response = await getErrorCodesByBrandId(brandInfo.brand_id, queryParams); + + if (response && response.statusCode === 200 && response.data) { + const freshErrorCodes = response.data.map(ec => ({ + ...ec, + tempId: `existing_${ec.error_code_id}`, + status: 'existing' + })); + setApiErrorCodes(freshErrorCodes); + + if (freshErrorCodes.length === 0 && tempErrorCodes.length === 0) { + NotifAlert({ + icon: 'warning', + title: 'Perhatian', + message: 'Harap tambahkan minimal 1 error code sebelum menyelesaikan.', + }); + return; + } + } else { + if (tempErrorCodes.length === 0) { + NotifAlert({ + icon: 'warning', + title: 'Perhatian', + message: 'Harap tambahkan minimal 1 error code sebelum menyelesaikan.', + }); + return; + } + } + } catch (error) { + NotifAlert({ + icon: 'error', + title: 'Error', + message: 'Gagal memeriksa error codes. Silakan coba lagi.', + }); + return; + } + } else if (!tempErrorCodes.length) { + NotifAlert({ + icon: 'warning', + title: 'Perhatian', + message: 'Harap tambahkan minimal 1 error code sebelum menyelesaikan.', + }); + return; + } + + setIsTemporaryBrand(false); + setTemporaryBrandId(null); + NotifOk({ icon: 'success', - title: 'Brand Device Tersimpan', - message: 'Brand device telah berhasil disimpan dengan error codes yang ditambahkan.', + title: 'Berhasil', + message: 'Brand device berhasil dibuat dengan error codes.', }); navigate('/master/brand-device'); } catch (error) { NotifAlert({ icon: 'error', title: 'Gagal', - message: error.message || 'Terjadi kesalahan. Silakan coba lagi.', + message: error.message || 'Gagal menyelesaikan brand device', }); } finally { setConfirmLoading(false); @@ -501,99 +762,296 @@ const AddBrandDevice = () => { form={brandForm} onValuesChange={handleBrandFormValuesChange} isEdit={false} - showSparepartSection={true} + brandInfo={brandInfo} /> ); } if (currentStep === 1) { + const handleErrorCodeSelect = (errorCode) => { + setSelectedErrorCode(errorCode); + loadErrorCodeData(errorCode); + }; + + const handleAddNew = () => { + setSelectedErrorCode(null); + resetErrorCodeForm(); + }; + return ( - - - - - - { - const value = e.target.value; - setSearchText(value); - setSearchValue(value); - if (value === '') { - setTrigerFilter((prev) => !prev); - } - }} - onSearch={handleSearch} - allowClear={{ - clearIcon: , - }} - enterButton={ + + + { + setSearchText(value); + setSearchValue(value); + if (value === '') { + setTrigerFilter((prev) => !prev); + } + }} + onSearch={handleSearch} + onSearchClear={handleSearchClear} + /> + + + +
+ + + 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 +

+
+ +
+ + + +
+
+
+

+ Solution +

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

+ Sparepart Selection +

+
+
+ +
+
+ +
+ +
+ {/* Cancel Button - Only show when editing existing error code */} + {editingErrorCodeKey && ( + + )} + + {/* Save Button - Always show on right */} +
- } - size="large" - /> - - - - - - - - - - - - navigate(`/master/brand-device/${createdBrandId}/error-code/edit/${record.error_code_id}`)} - showDeleteDialog={handleDeleteErrorCode} - getData={getErrorCodesData} - queryParams={queryParams} - columns={errorCodeColumns(handlePreviewErrorCode, (record) => navigate(`/master/brand-device/${createdBrandId}/error-code/edit/${record.error_code_id}`), handleDeleteErrorCode)} - triger={trigerFilter} - /> - - - +
+
+
+
+
+ +
); } return null; }; useEffect(() => { + errorCodeForm.setFieldsValue({ + status: true, + }); + + const tab = searchParams.get('tab') || 'brand'; + setBreadcrumbItems([ { title: • Master @@ -616,19 +1074,55 @@ const AddBrandDevice = () => { ), }, ]); - }, [setBreadcrumbItems, navigate]); + + if (location.state?.fromFileViewer && location.state.phase !== undefined) { + setCurrentStep(location.state.phase); + } + + }, [setBreadcrumbItems, navigate, searchParams, location.state]); useEffect(() => { - if (createdBrandId && currentStep === 1) { + if (brandInfo.brand_id && currentStep === 1) { setTrigerFilter(prev => !prev); } - }, [createdBrandId, currentStep]); + }, [brandInfo.brand_id, currentStep]); + useEffect(() => { + const fetchErrorCodes = async () => { + if (brandInfo.brand_id) { + try { + const response = await getErrorCodesByBrandId(brandInfo.brand_id); + if (response && response.statusCode === 200) { + const errorCodes = response.data || []; + setApiErrorCodes(errorCodes); + } + } catch (error) { + } + } + }; + fetchErrorCodes(); + }, [brandInfo.brand_id, trigerFilter]); + + useEffect(() => { + const handleBeforeUnload = async (event) => { + if (isTemporaryBrand && temporaryBrandId && currentStep === 0) { + try { + await deleteBrand(temporaryBrandId); + } catch (error) { + } + } + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, [isTemporaryBrand, temporaryBrandId, currentStep]); + + return ( - - Tambah Brand Device - @@ -675,7 +1169,7 @@ const AddBrandDevice = () => { onClick={handlePrevStep} style={{ marginLeft: 8 }} > - Back to Brand Info + Kembali ke Brand Info )} @@ -684,12 +1178,13 @@ const AddBrandDevice = () => { )} {currentStep === 1 && ( diff --git a/src/pages/master/brandDevice/AddEditErrorCode.jsx b/src/pages/master/brandDevice/AddEditErrorCode.jsx index 04376c0..b721c64 100644 --- a/src/pages/master/brandDevice/AddEditErrorCode.jsx +++ b/src/pages/master/brandDevice/AddEditErrorCode.jsx @@ -10,13 +10,13 @@ import { Spin, Upload, } from 'antd'; -import { ArrowLeftOutlined, UploadOutlined } from '@ant-design/icons'; -import { getBrandById, getErrorCodeById, updateBrand, getErrorCodesByBrandId, createErrorCode, updateErrorCode } from '../../../api/master-brand'; +import { ArrowLeftOutlined } from '@ant-design/icons'; +import { getErrorCodeById, createErrorCode, updateErrorCode } from '../../../api/master-brand'; import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb'; -import ErrorCodeSimpleForm from './component/ErrorCodeSimpleForm'; +import ErrorCodeForm from './component/ErrorCodeForm'; import SolutionForm from './component/SolutionForm'; import { useSolutionLogic } from './hooks/solution'; -import SingleSparepartSelect from './component/SingleSparepartSelect'; +import SparepartSelect from './component/SparepartSelect'; import { NotifAlert, NotifOk } from '../../../components/Global/ToastNotif'; const { Title } = Typography; @@ -29,10 +29,9 @@ const AddEditErrorCode = () => { const currentBrandId = routeBrandId; - const isFromAddBrand = location.pathname.includes('/master/brand-device/') && location.pathname.includes('/error-code/') && - (location.pathname.includes('/add') || (location.pathname.includes('/edit/') && !location.pathname.includes('/edit/'))); + const isFromAddBrand = location.pathname.includes('/master/brand-device/') && location.pathname.includes('/error-code/') && + (location.pathname.includes('/add') || (location.pathname.includes('/edit/') && !location.pathname.includes('/edit/'))); - // Forms const [errorCodeForm] = Form.useForm(); const [solutionForm] = Form.useForm(); @@ -62,7 +61,6 @@ const AddEditErrorCode = () => { const isEditMode = errorCodeId && errorCodeId !== 'add'; setIsEdit(isEditMode); - // Initialize solution form with proper structure if (!isEditMode) { resetSolutionFields(); } @@ -172,13 +170,13 @@ const AddEditErrorCode = () => { } }; - const handleSave = async () => { + const handleSave = async () => { try { await errorCodeForm.validateFields(); const solutionData = getSolutionData(); - // Validate that at least one solution exists and is valid + if (!solutionData || solutionData.length === 0) { NotifAlert({ icon: 'warning', @@ -188,16 +186,16 @@ const AddEditErrorCode = () => { return; } - // Validate solutions based on their type const invalidSolutions = solutionData.filter(solution => { if (solution.type_solution === 'text') { return !solution.text_solution || solution.text_solution.trim() === ''; - } else if (solution.type_solution === 'file') { + } else if (solution.type_solution !== 'text') { return !solution.path_solution || solution.path_solution.trim() === ''; } return false; }); + if (invalidSolutions.length > 0) { const invalidNames = invalidSolutions.map(s => s.solution_name).join(', '); NotifAlert({ @@ -229,14 +227,20 @@ const AddEditErrorCode = () => { payload.error_code = errorCodeValues.error_code; } + let response; if (isEdit && errorCodeId) { - response = await updateErrorCode(currentBrandId, errorCodeId, payload); + let cleanErrorCodeId = errorCodeId; + if (errorCodeId.startsWith('existing_')) { + cleanErrorCodeId = errorCodeId.replace('existing_', ''); + } + response = await updateErrorCode(currentBrandId, cleanErrorCodeId, payload); } else { response = await createErrorCode(currentBrandId, payload); } + if (response && (response.statusCode === 200 || response.statusCode === 201)) { NotifOk({ icon: 'success', @@ -247,7 +251,9 @@ const AddEditErrorCode = () => { if (isFromAddBrand) { navigate(`/master/brand-device/add`); } else { - navigate(`/master/brand-device/edit/${currentBrandId}?tab=error-codes`); + navigate(`/master/brand-device/edit/${currentBrandId}?tab=error-codes`, { + state: { refreshErrorCodes: true } + }); } } else { NotifAlert({ @@ -303,7 +309,6 @@ const AddEditErrorCode = () => { }; const handleSolutionFileUpload = (fileObject) => { - // Handle solution file upload if needed }; const resetForm = () => { @@ -373,7 +378,7 @@ const AddEditErrorCode = () => { error_code_color: '#000000', }} > - { solutionStatuses={solutionStatuses} firstSolutionValid={firstSolutionValid} checkFirstSolutionValid={() => { - // console.log('🔍 checkFirstSolutionValid function:', typeof checkFirstSolutionValid); - return checkFirstSolutionValid(); - }} + return checkFirstSolutionValid(); + }} onAddSolutionField={handleAddSolutionField} onRemoveSolutionField={handleRemoveSolutionField} onSolutionTypeChange={handleSolutionTypeChange} @@ -430,7 +434,7 @@ const AddEditErrorCode = () => { size="small" style={{ height: 'fit-content' }} > - { - +
); }; diff --git a/src/pages/master/brandDevice/EditBrandDevice.jsx b/src/pages/master/brandDevice/EditBrandDevice.jsx index da8038e..f3402a8 100644 --- a/src/pages/master/brandDevice/EditBrandDevice.jsx +++ b/src/pages/master/brandDevice/EditBrandDevice.jsx @@ -15,18 +15,17 @@ import { Input, } from 'antd'; import { EyeOutlined, EditOutlined, DeleteOutlined, PlusOutlined, SearchOutlined } from '@ant-design/icons'; -import TableList from '../../../components/Global/TableList'; -import { ConfigProvider } from 'antd'; import { NotifAlert, NotifOk, NotifConfirmDialog } from '../../../components/Global/ToastNotif'; import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb'; -import { getBrandById, getErrorCodesByBrandId, deleteErrorCode } from '../../../api/master-brand'; +import { getBrandById, getErrorCodesByBrandId, getErrorCodeById, deleteErrorCode, updateErrorCode as updateErrorCodeAPI, createErrorCode as createErrorCodeAPI } from '../../../api/master-brand'; import { getFileUrl } from '../../../api/file-uploads'; +import { SendRequest } from '../../../components/Global/ApiRequest'; import BrandForm from './component/BrandForm'; -import ErrorCodeSimpleForm from './component/ErrorCodeSimpleForm'; +import ErrorCodeForm from './component/ErrorCodeForm'; import SolutionForm from './component/SolutionForm'; import FormActions from './component/FormActions'; -import { useSolutionLogic } from './hooks/solution'; -import SingleSparepartSelect from './component/SingleSparepartSelect'; +import SparepartSelect from './component/SparepartSelect'; +import ListErrorCode from './component/ListErrorCode'; const { Title } = Typography; const { Step } = Steps; @@ -50,18 +49,180 @@ const EditBrandDevice = () => { const [searchText, setSearchText] = useState(''); const [apiErrorCodes, setApiErrorCodes] = useState([]); const [trigerFilter, setTrigerFilter] = useState(false); - const [searchValue, setSearchValue] = useState(''); const [brandInfo, setBrandInfo] = useState({}); const [tempErrorCodes, setTempErrorCodes] = useState([]); const [existingErrorCodes, setExistingErrorCodes] = useState([]); + const [selectedErrorCode, setSelectedErrorCode] = useState(null); + const [solutionFields, setSolutionFields] = useState([0]); + const [solutionTypes, setSolutionTypes] = useState({ 0: 'text' }); + const [solutionStatuses, setSolutionStatuses] = useState({ 0: true }); + const [currentSolutionData, setCurrentSolutionData] = useState([]); - const { - resetSolutionFields, - getSolutionData, - setSolutionsForExistingRecord, - } = useSolutionLogic(solutionForm); + 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 []; + } + }; + + const resetSolutionFields = () => { + if (solutionForm && solutionForm.resetFields) { + solutionForm.resetFields(); + solutionForm.setFieldsValue({ + solution_items: { + 0: { + name: '', + type: 'text', + text: '', + status: true + } + } + }); + } + setCurrentSolutionData([]); + }; + + const setSolutionsForExistingRecord = (solutions, targetForm) => { + + if (!targetForm || !solutions || solutions.length === 0) { + return; + } + + 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 !== false; + + 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 !== false, + 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 handleAddSolutionField = () => { + const newKey = Math.max(...solutionFields, 0) + 1; + setSolutionFields(prev => [...prev, newKey]); + setSolutionTypes(prev => ({ ...prev, [newKey]: 'text' })); + setSolutionStatuses(prev => ({ ...prev, [newKey]: true })); + }; + + const handleRemoveSolutionField = (fieldKey) => { + if (solutionFields.length > 1) { + setSolutionFields(prev => prev.filter(key => key !== fieldKey)); + const newTypes = { ...solutionTypes }; + const newStatuses = { ...solutionStatuses }; + delete newTypes[fieldKey]; + delete newStatuses[fieldKey]; + setSolutionTypes(newTypes); + setSolutionStatuses(newStatuses); + + const currentValues = solutionForm.getFieldsValue(); + if (currentValues.solution_items && currentValues.solution_items[fieldKey]) { + delete currentValues.solution_items[fieldKey]; + solutionForm.setFieldsValue(currentValues); + } + } + }; + + const handleSolutionTypeChange = (fieldKey, type) => { + setSolutionTypes(prev => ({ ...prev, [fieldKey]: type })); + + if (type === 'file') { + solutionForm.setFieldValue(['solution_items', fieldKey, 'text'], ''); + } + + if (type === 'text') { + solutionForm.setFieldValue(['solution_items', fieldKey, 'file'], null); + solutionForm.setFieldValue(['solution_items', fieldKey, 'fileUpload'], null); + } + }; + + const handleSolutionStatusChange = (fieldKey, status) => { + setSolutionStatuses(prev => ({ ...prev, [fieldKey]: status })); + }; useEffect(() => { + errorCodeForm.setFieldsValue({ + status: true, + }); + const fetchBrandData = async () => { const token = localStorage.getItem('token'); if (!token) { @@ -129,7 +290,6 @@ const EditBrandDevice = () => { setApiErrorCodes(existingCodes); } } catch (error) { - console.error('Error fetching error codes:', error); } } @@ -160,18 +320,35 @@ const EditBrandDevice = () => { setCurrentStep(tab === 'brand' ? 0 : 1); }, [searchParams]); - + useEffect(() => { if (currentStep === 1 && id) { setTrigerFilter(prev => !prev); } }, [currentStep, id]); - // Local functions to replace context methods + // 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, { + replace: true, + state + }); + } + }, [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, - tempId: Date.now().toString(), + tempId: uniqueId, status: 'new' }; setTempErrorCodes(prev => [...prev, errorCodeWithId]); @@ -228,7 +405,7 @@ const EditBrandDevice = () => { navigate('/master/brand-device'); }; - + const handleErrorCodeIconUpload = (iconData) => { setErrorCodeIcon(iconData); }; @@ -251,6 +428,7 @@ const EditBrandDevice = () => { const handleCreateNewErrorCode = () => { resetErrorCodeForm(); + setCurrentSolutionData([]); }; const handleSaveErrorCode = async () => { @@ -276,106 +454,138 @@ const EditBrandDevice = () => { return; } - const newErrorCode = { - error_code: errorCodeValues.error_code, + const formattedSolutions = solutionData.map(solution => { + const solutionType = solution.type || 'text'; + + let typeSolution = solutionType === 'text' ? 'text' : 'image'; + if (solution.file && solution.file.type_solution) { + typeSolution = solution.file.type_solution; + } else if (solution.fileUpload && solution.fileUpload.type_solution) { + typeSolution = solution.fileUpload.type_solution; + } + + const formattedSolution = { + solution_name: solution.name, + type_solution: typeSolution, + is_active: solution.status !== false, + }; + + if (typeSolution === 'text') { + formattedSolution.text_solution = solution.text || ''; + formattedSolution.path_solution = ''; + } else { + formattedSolution.text_solution = ''; + + formattedSolution.path_solution = solution.path_solution || solution.file?.uploadPath || solution.fileUpload?.uploadPath || ''; + } + + if (formattedSolution.brand_code_solution_id) { + delete formattedSolution.brand_code_solution_id; + } + + return formattedSolution; + }); + + const payload = { error_code_name: errorCodeValues.error_code_name, - error_code_description: errorCodeValues.error_code_description, + error_code_description: errorCodeValues.error_code_description || '', error_code_color: errorCodeValues.error_code_color || '#000000', path_icon: errorCodeIcon?.uploadPath || '', is_active: errorCodeValues.status === undefined ? true : errorCodeValues.status, - solution: solutionData, - spareparts: selectedSparepartIds + solution: formattedSolutions, + spareparts: selectedSparepartIds || [] }; - if (editingErrorCodeKey) { - updateErrorCode(editingErrorCodeKey, newErrorCode); - NotifOk({ - icon: 'success', - title: 'Berhasil', - message: 'Error code berhasil diupdate!', - }); - } else { - addErrorCode(newErrorCode); - NotifOk({ - icon: 'success', - title: 'Berhasil', - message: 'Error code berhasil ditambahkan!', - }); + if (!editingErrorCodeKey || !editingErrorCodeKey.startsWith('existing_')) { + payload.error_code = errorCodeValues.error_code; } - resetErrorCodeForm(); + let response; + + if (editingErrorCodeKey && editingErrorCodeKey.startsWith('existing_')) { + const errorCodeId = editingErrorCodeKey.replace('existing_', ''); + response = await updateErrorCodeAPI(id, errorCodeId, payload); + } else { + response = await createErrorCodeAPI(id, payload); + } + + + if (response && (response.statusCode === 200 || response.statusCode === 201)) { + NotifOk({ + icon: 'success', + title: 'Berhasil', + 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; + })); + + setTrigerFilter(prev => !prev); + resetErrorCodeForm(); + } else { + NotifAlert({ + icon: 'error', + title: 'Gagal', + message: response?.message || 'Gagal menyimpan error code', + }); + } } catch (error) { NotifAlert({ icon: 'warning', title: 'Perhatian', - message: 'Harap isi semua kolom wajib!', + message: error.message || 'Harap isi semua kolom wajib!', }); } }; + const loadErrorCodeData = (errorCode, isPreview = false) => { + if (errorCode) { + + const formValues = { + error_code: errorCode.error_code, + error_code_name: errorCode.error_code_name, + error_code_description: errorCode.error_code_description || '', + error_code_color: errorCode.error_code_color && errorCode.error_code_color !== '' ? errorCode.error_code_color : '#000000', + status: errorCode.is_active, + }; + + errorCodeForm.setFieldsValue(formValues); + + if (errorCode.path_icon && errorCode.path_icon !== '') { + const iconData = { + name: errorCode.path_icon.split('/').pop(), + uploadPath: errorCode.path_icon, + }; + setErrorCodeIcon(iconData); + } else { + setErrorCodeIcon(null); + } + + setIsErrorCodeFormReadOnly(isPreview); + + const editingKey = errorCode.tempId || `existing_${errorCode.error_code_id}`; + setEditingErrorCodeKey(editingKey); + + } + }; + const handlePreviewErrorCode = (record) => { const errorCode = getErrorCodeById(record.tempId || record.error_code_id); - if (errorCode) { - errorCodeForm.setFieldsValue({ - error_code: errorCode.error_code, - error_code_name: errorCode.error_code_name, - error_code_description: errorCode.error_code_description, - error_code_color: errorCode.error_code_color, - status: errorCode.is_active, - }); - setErrorCodeIcon(errorCode.path_icon ? { - name: errorCode.path_icon.split('/').pop(), - uploadPath: errorCode.path_icon, - } : null); - setIsErrorCodeFormReadOnly(true); - setEditingErrorCodeKey(errorCode.tempId); - - if (errorCode.solution && errorCode.solution.length > 0) { - setSolutionsForExistingRecord(errorCode.solution, solutionForm); - } else { - resetSolutionFields(); - } - - if (errorCode.spareparts && errorCode.spareparts.length > 0) { - setSelectedSparepartIds(errorCode.spareparts); - } - } + loadErrorCodeData(errorCode, true); }; const handleEditErrorCode = (record) => { const errorCode = getErrorCodeById(record.tempId || record.error_code_id); - if (errorCode) { - errorCodeForm.setFieldsValue({ - error_code: errorCode.error_code, - error_code_name: errorCode.error_code_name, - error_code_description: errorCode.error_code_description, - error_code_color: errorCode.error_code_color, - status: errorCode.is_active, - }); - setErrorCodeIcon(errorCode.path_icon ? { - name: errorCode.path_icon.split('/').pop(), - uploadPath: errorCode.path_icon, - } : null); - setIsErrorCodeFormReadOnly(false); - setEditingErrorCodeKey(errorCode.tempId); - - if (errorCode.solution && errorCode.solution.length > 0) { - setSolutionsForExistingRecord(errorCode.solution, solutionForm); - } - - if (errorCode.spareparts && errorCode.spareparts.length > 0) { - setSelectedSparepartIds(errorCode.spareparts); - } - } + loadErrorCodeData(errorCode, false); }; - const handleEditErrorCodeNavigate = (record) => { - const errorCodeId = record.status === 'existing' ? record.error_code_id : record.tempId; - const currentBrandId = id; - if (errorCodeId && currentBrandId) { - navigate(`/master/brand-device/${currentBrandId}/error-code/edit/${errorCodeId}`); - } - }; const handleDeleteErrorCode = async (record) => { NotifConfirmDialog({ @@ -418,7 +628,7 @@ const EditBrandDevice = () => { }); } }, - onCancel: () => {} + onCancel: () => { } }); }; @@ -518,25 +728,22 @@ const EditBrandDevice = () => { }, [setBrandInfo]); const handleSearch = () => { - setSearchText(searchValue); setTrigerFilter((prev) => !prev); }; const handleSearchClear = () => { - setSearchValue(''); setSearchText(''); setTrigerFilter((prev) => !prev); }; const getErrorCodesData = async (params) => { try { - const search = params.get('search') || ''; + const criteria = params.get('criteria') || ''; const page = parseInt(params.get('page')) || 1; const limit = parseInt(params.get('limit')) || 10; const currentBrandId = id; if (!currentBrandId) { - console.warn('Brand ID is not available'); return { data: [], pagination: { @@ -551,7 +758,7 @@ const EditBrandDevice = () => { const queryParams = new URLSearchParams({ page: page.toString(), limit: limit.toString(), - ...(search && { search }) + ...(criteria && { criteria }) }); const response = await getErrorCodesByBrandId(currentBrandId, queryParams); @@ -576,7 +783,6 @@ const EditBrandDevice = () => { spareparts: contextModified.spareparts || ec.spareparts || [] }; } else { - // Use original API data return { ...ec, tempId: `existing_${ec.error_code_id}`, @@ -587,7 +793,6 @@ const EditBrandDevice = () => { } }); - // Filter out deleted error codes const activeExistingCodes = existingCodes.filter(ec => ec.status !== 'deleted'); const allErrorCodes = [...activeExistingCodes, ...tempErrorCodes.filter(ec => ec.status !== 'deleted')]; @@ -626,7 +831,6 @@ const EditBrandDevice = () => { } }; } catch (error) { - console.error('Error fetching error codes:', error); return { data: [], pagination: { @@ -672,90 +876,321 @@ const EditBrandDevice = () => { } if (currentStep === 1) { - const queryParams = new URLSearchParams(); - if (searchText) { - queryParams.set('search', searchText); - } + 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}` + }; + loadErrorCodeData(fullErrorCodeData, false); + + 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}` + }; + loadErrorCodeData(basicErrorCodeData, false); + resetSolutionFields(); + setSelectedSparepartIds([]); + } + } catch (error) { + const basicErrorCodeData = { + ...errorCode, + tempId: `existing_${errorCode.error_code_id}` + }; + loadErrorCodeData(basicErrorCodeData, false); + } + }; + + const handleAddNew = () => { + setSelectedErrorCode(null); + resetErrorCodeForm(); + }; return ( - - - - - - { - const value = e.target.value; - setSearchText(value); - if (value === '') { - setTrigerFilter((prev) => !prev); - } - }} - onSearch={handleSearch} - allowClear={{ - clearIcon: , - }} - enterButton={ + + + { + setSearchText(value); + if (value === '') { + setTrigerFilter((prev) => !prev); + } + }} + onSearch={handleSearch} + onSearchClear={handleSearchClear} + /> + + + +
+ + + 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 +

+
+ +
+ + + +
+
+
+

+ Solution +

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

+ Sparepart Selection +

+
+
+ +
+
+ +
+ +
+ {/* Cancel Button - Only show when editing existing error code */} + {editingErrorCodeKey && ( + + )} + + {/* Save Button - Always show on right */} +
- } - size="large" - /> - - - - - - - - - - - - - - - +
+
+
+
+
+ +
); } return null; @@ -763,9 +1198,6 @@ const EditBrandDevice = () => { return ( - - Edit Brand Device - @@ -788,6 +1220,7 @@ const EditBrandDevice = () => { )} diff --git a/src/pages/master/brandDevice/ViewFilePage.jsx b/src/pages/master/brandDevice/ViewFilePage.jsx index 3d16f1b..16de7a7 100644 --- a/src/pages/master/brandDevice/ViewFilePage.jsx +++ b/src/pages/master/brandDevice/ViewFilePage.jsx @@ -138,12 +138,6 @@ const ViewFilePage = () => { const targetPhase = savedPhase ? parseInt(savedPhase) : 1; - console.log({ - savedPhase, - targetPhase, - id: fallbackId || id - }); - navigate(`/master/brand-device/edit/${fallbackId || id}`, { state: { phase: targetPhase, fromFileViewer: true }, replace: true @@ -174,9 +168,7 @@ const ViewFilePage = () => { 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 (
@@ -318,7 +310,6 @@ const ViewFilePage = () => {