integration api brandDevice, file upload in brand device
This commit is contained in:
445
src/pages/master/brandDevice/EditBrandDevice.jsx
Normal file
445
src/pages/master/brandDevice/EditBrandDevice.jsx
Normal file
@@ -0,0 +1,445 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams, useLocation } from 'react-router-dom';
|
||||
import { Divider, Typography, Button, Steps, Form, Row, Col, Card, Spin, Modal } from 'antd';
|
||||
import { NotifAlert, NotifOk } from '../../../components/Global/ToastNotif';
|
||||
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
|
||||
import { getBrandById, updateBrand } from '../../../api/master-brand';
|
||||
import BrandForm from './component/BrandForm';
|
||||
import ErrorCodeForm from './component/ErrorCodeForm';
|
||||
import ErrorCodeTable from './component/ListErrorCode';
|
||||
import FormActions from './component/FormActions';
|
||||
import { useErrorCodeLogic } from './hooks/errorCode';
|
||||
|
||||
const { Title } = Typography;
|
||||
const { Step } = Steps;
|
||||
|
||||
const defaultData = {
|
||||
brand_name: '',
|
||||
brand_type: '',
|
||||
brand_model: '',
|
||||
brand_manufacture: '',
|
||||
is_active: true,
|
||||
brand_code: '',
|
||||
};
|
||||
|
||||
const EditBrandDevice = () => {
|
||||
const navigate = useNavigate();
|
||||
const { id } = useParams();
|
||||
const location = useLocation();
|
||||
const { setBreadcrumbItems } = useBreadcrumb();
|
||||
const [brandForm] = Form.useForm();
|
||||
const [errorCodeForm] = Form.useForm();
|
||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
const [currentStep, setCurrentStep] = useState(0);
|
||||
const [fileList, setFileList] = useState([]);
|
||||
const [isErrorCodeFormReadOnly, setIsErrorCodeFormReadOnly] = useState(false);
|
||||
const [editingErrorCodeKey, setEditingErrorCodeKey] = useState(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [formData, setFormData] = useState(defaultData);
|
||||
const [errorCodes, setErrorCodes] = useState([]);
|
||||
|
||||
const {
|
||||
solutionFields,
|
||||
solutionTypes,
|
||||
solutionStatuses,
|
||||
firstSolutionValid,
|
||||
solutionsToDelete,
|
||||
handleAddSolutionField,
|
||||
handleRemoveSolutionField,
|
||||
handleSolutionTypeChange,
|
||||
handleSolutionStatusChange,
|
||||
resetSolutionFields,
|
||||
checkFirstSolutionValid,
|
||||
setSolutionsForExistingRecord
|
||||
} = useErrorCodeLogic(errorCodeForm, fileList);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchBrandData = async () => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) {
|
||||
navigate('/signin');
|
||||
return;
|
||||
}
|
||||
|
||||
const savedPhase = location.state?.phase || localStorage.getItem(`brand_device_edit_${id}_last_phase`);
|
||||
if (savedPhase) {
|
||||
setCurrentStep(parseInt(savedPhase));
|
||||
localStorage.removeItem(`brand_device_edit_${id}_last_phase`);
|
||||
}
|
||||
|
||||
setBreadcrumbItems([
|
||||
{ title: <span style={{ fontSize: '14px', fontWeight: 'bold' }}>• Master</span> },
|
||||
{
|
||||
title: <span style={{ fontSize: '14px', fontWeight: 'bold', cursor: 'pointer' }} onClick={() => navigate('/master/brand-device')}>Brand Device</span>
|
||||
},
|
||||
{ title: <span style={{ fontSize: '14px', fontWeight: 'bold' }}>Edit Brand Device</span> }
|
||||
]);
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
const response = await getBrandById(id);
|
||||
|
||||
if (response && response.statusCode === 200) {
|
||||
const brandData = response.data;
|
||||
const newFormData = {
|
||||
brand_name: brandData.brand_name,
|
||||
brand_type: brandData.brand_type,
|
||||
brand_model: brandData.brand_model,
|
||||
brand_manufacture: brandData.brand_manufacture,
|
||||
is_active: brandData.is_active,
|
||||
brand_code: brandData.brand_code,
|
||||
};
|
||||
|
||||
const existingErrorCodes = brandData.error_code ? brandData.error_code.map((ec, index) => ({
|
||||
key: `existing-${ec.error_code_id}`,
|
||||
error_code_id: ec.error_code_id,
|
||||
error_code: ec.error_code,
|
||||
error_code_name: ec.error_code_name || '',
|
||||
error_code_description: ec.error_code_description || '',
|
||||
status: ec.is_active,
|
||||
solution: ec.solution || []
|
||||
})) : [];
|
||||
|
||||
setFormData(newFormData);
|
||||
brandForm.setFieldsValue(newFormData);
|
||||
setErrorCodes(existingErrorCodes);
|
||||
|
||||
} else {
|
||||
NotifAlert({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
message: response?.message || 'Failed to fetch brand device data',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
NotifAlert({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
message: error.message || 'Failed to fetch brand device data',
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchBrandData();
|
||||
}, [id, setBreadcrumbItems, navigate, brandForm, location]);
|
||||
|
||||
const handleCancel = () => {
|
||||
localStorage.removeItem(`brand_device_edit_${id}_temp_data`);
|
||||
navigate('/master/brand-device');
|
||||
};
|
||||
|
||||
const handleNextStep = async () => {
|
||||
try {
|
||||
await brandForm.validateFields();
|
||||
setCurrentStep(1);
|
||||
} catch (error) {
|
||||
NotifAlert({ icon: 'warning', title: 'Perhatian', message: 'Harap isi semua kolom wajib untuk brand device!' });
|
||||
}
|
||||
};
|
||||
|
||||
const handleFinish = async () => {
|
||||
setConfirmLoading(true);
|
||||
try {
|
||||
const finalFormData = {
|
||||
brand_name: formData.brand_name,
|
||||
brand_type: formData.brand_type || '',
|
||||
brand_model: formData.brand_model || '',
|
||||
brand_manufacture: formData.brand_manufacture,
|
||||
is_active: formData.is_active,
|
||||
error_code: errorCodes.map(ec => ({
|
||||
error_code: ec.error_code,
|
||||
error_code_name: ec.error_code_name || '',
|
||||
error_code_description: ec.error_code_description || '',
|
||||
is_active: ec.status !== undefined ? ec.status : true,
|
||||
solution: (ec.solution || []).map(sol => ({
|
||||
solution_name: sol.solution_name,
|
||||
type_solution: sol.type_solution,
|
||||
text_solution: sol.text_solution || '',
|
||||
path_solution: sol.path_solution || '',
|
||||
is_active: sol.is_active !== false
|
||||
}))
|
||||
}))
|
||||
};
|
||||
|
||||
const response = await updateBrand(id, finalFormData);
|
||||
|
||||
if (response && (response.statusCode === 200 || response.statusCode === 201)) {
|
||||
localStorage.removeItem(`brand_device_edit_${id}_temp_data`);
|
||||
NotifOk({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
message: response.message || 'Brand Device dan Error Codes berhasil diupdate.',
|
||||
});
|
||||
navigate('/master/brand-device');
|
||||
} else {
|
||||
NotifAlert({
|
||||
icon: 'error',
|
||||
title: 'Gagal',
|
||||
message: response?.message || 'Gagal mengupdate Brand Device',
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
NotifAlert({
|
||||
icon: "error",
|
||||
title: "Gagal",
|
||||
message: error.message || "Gagal mengupdate data. Silakan coba lagi.",
|
||||
});
|
||||
} finally {
|
||||
setConfirmLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePreviewErrorCode = (record) => {
|
||||
errorCodeForm.setFieldsValue({
|
||||
error_code: record.error_code,
|
||||
error_code_name: record.error_code_name,
|
||||
error_code_description: record.error_code_description,
|
||||
status: record.status,
|
||||
});
|
||||
setIsErrorCodeFormReadOnly(true);
|
||||
setEditingErrorCodeKey(record.key);
|
||||
|
||||
if (record.solution && record.solution.length > 0) {
|
||||
setSolutionsForExistingRecord(record.solution, errorCodeForm);
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditErrorCode = (record) => {
|
||||
errorCodeForm.setFieldsValue({
|
||||
error_code: record.error_code,
|
||||
error_code_name: record.error_code_name,
|
||||
error_code_description: record.error_code_description,
|
||||
status: record.status,
|
||||
});
|
||||
setIsErrorCodeFormReadOnly(false);
|
||||
setEditingErrorCodeKey(record.key);
|
||||
|
||||
if (record.solution && record.solution.length > 0) {
|
||||
setSolutionsForExistingRecord(record.solution, errorCodeForm);
|
||||
}
|
||||
|
||||
const formElement = document.querySelector('.ant-form');
|
||||
if (formElement) {
|
||||
formElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const handleAddErrorCode = (newErrorCode) => {
|
||||
let updatedErrorCodes;
|
||||
if (editingErrorCodeKey) {
|
||||
updatedErrorCodes = errorCodes.map(item => item.key === editingErrorCodeKey ? newErrorCode : item);
|
||||
NotifOk({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
message: 'Error code berhasil diupdate!'
|
||||
});
|
||||
} else {
|
||||
updatedErrorCodes = [...errorCodes, newErrorCode];
|
||||
NotifOk({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
message: 'Error code berhasil ditambahkan!'
|
||||
});
|
||||
}
|
||||
|
||||
setErrorCodes(updatedErrorCodes);
|
||||
resetErrorCodeForm();
|
||||
};
|
||||
|
||||
const resetErrorCodeForm = () => {
|
||||
errorCodeForm.resetFields();
|
||||
errorCodeForm.setFieldsValue({
|
||||
status: true,
|
||||
solution_status_0: true,
|
||||
solution_type_0: 'text'
|
||||
});
|
||||
setFileList([]);
|
||||
resetSolutionFields();
|
||||
setIsErrorCodeFormReadOnly(false);
|
||||
setEditingErrorCodeKey(null);
|
||||
};
|
||||
|
||||
const handleDeleteErrorCode = async (key) => {
|
||||
if (errorCodes.length <= 1) {
|
||||
NotifAlert({
|
||||
icon: 'warning',
|
||||
title: 'Perhatian',
|
||||
message: 'Setiap brand harus memiliki minimal 1 error code!'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedErrorCodes = errorCodes.filter(item => item.key !== key);
|
||||
setErrorCodes(updatedErrorCodes);
|
||||
NotifOk({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
message: 'Error code berhasil dihapus!'
|
||||
});
|
||||
};
|
||||
|
||||
const handleCreateNewErrorCode = () => {
|
||||
resetErrorCodeForm();
|
||||
};
|
||||
|
||||
const handleFileView = (pathSolution, fileType) => {
|
||||
localStorage.setItem(`brand_device_edit_${id}_last_phase`, currentStep.toString());
|
||||
|
||||
const tempData = {
|
||||
errorCodes: errorCodes,
|
||||
fileList: fileList,
|
||||
solutionFields: solutionFields,
|
||||
solutionTypes: solutionTypes,
|
||||
solutionStatuses: solutionStatuses,
|
||||
editingErrorCodeKey: editingErrorCodeKey,
|
||||
isErrorCodeFormReadOnly: isErrorCodeFormReadOnly,
|
||||
solutionsToDelete: Array.from(solutionsToDelete),
|
||||
currentSolutionData: window.currentSolutionData || {}
|
||||
};
|
||||
localStorage.setItem(`brand_device_edit_${id}_temp_data`, JSON.stringify(tempData));
|
||||
|
||||
const filePath = pathSolution || '';
|
||||
if (!filePath) return;
|
||||
|
||||
const parts = filePath.split('/');
|
||||
if (parts.length < 2) return;
|
||||
|
||||
const [folder, filename] = parts;
|
||||
const encodedFileName = encodeURIComponent(filename);
|
||||
const navigationPath = `/master/brand-device/edit/${id}/files/${folder}/${encodedFileName}`;
|
||||
navigate(navigationPath);
|
||||
};
|
||||
|
||||
const handleSolutionFileUpload = (file) => {
|
||||
setFileList(prevList => [...prevList, file]);
|
||||
};
|
||||
|
||||
const handleFileRemove = (file) => {
|
||||
const newFileList = fileList.filter(item => item.uid !== file.uid);
|
||||
setFileList(newFileList);
|
||||
};
|
||||
|
||||
const renderStepContent = () => {
|
||||
if (currentStep === 0) {
|
||||
return (
|
||||
<BrandForm
|
||||
form={brandForm}
|
||||
formData={formData}
|
||||
onValuesChange={(changedValues, allValues) => setFormData(prev => ({...prev, ...allValues}))}
|
||||
isEdit={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (currentStep === 1) {
|
||||
return (
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Title level={5} style={{ marginBottom: 16 }}>
|
||||
{isErrorCodeFormReadOnly
|
||||
? (editingErrorCodeKey ? 'View Error Code' : 'Error Code Form')
|
||||
: (editingErrorCodeKey ? 'Edit Error Code' : 'Tambah Error Code')
|
||||
}
|
||||
</Title>
|
||||
<Form
|
||||
form={errorCodeForm}
|
||||
layout="vertical"
|
||||
initialValues={{ status: true, solution_status_0: true, solution_type_0: 'text' }}
|
||||
onValuesChange={checkFirstSolutionValid}
|
||||
>
|
||||
<ErrorCodeForm
|
||||
errorCodeForm={errorCodeForm}
|
||||
isErrorCodeFormReadOnly={isErrorCodeFormReadOnly}
|
||||
editingErrorCodeKey={editingErrorCodeKey}
|
||||
solutionFields={solutionFields}
|
||||
solutionTypes={solutionTypes}
|
||||
solutionStatuses={solutionStatuses}
|
||||
fileList={fileList}
|
||||
solutionsToDelete={solutionsToDelete}
|
||||
firstSolutionValid={firstSolutionValid}
|
||||
onAddErrorCode={handleAddErrorCode}
|
||||
onAddSolutionField={handleAddSolutionField}
|
||||
onRemoveSolutionField={handleRemoveSolutionField}
|
||||
onSolutionTypeChange={handleSolutionTypeChange}
|
||||
onSolutionStatusChange={handleSolutionStatusChange}
|
||||
onSolutionFileUpload={handleSolutionFileUpload}
|
||||
onFileView={handleFileView}
|
||||
onCreateNewErrorCode={handleCreateNewErrorCode}
|
||||
onResetForm={resetErrorCodeForm}
|
||||
errorCodes={errorCodes}
|
||||
/>
|
||||
</Form>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<ErrorCodeTable
|
||||
errorCodes={loading ?
|
||||
Array.from({ length: 3 }, (_, index) => ({
|
||||
key: `loading-${index}`,
|
||||
error_code: 'Loading...',
|
||||
error_code_name: 'Loading...',
|
||||
solution: []
|
||||
})) :
|
||||
errorCodes
|
||||
}
|
||||
loading={loading}
|
||||
onPreview={handlePreviewErrorCode}
|
||||
onEdit={handleEditErrorCode}
|
||||
onDelete={handleDeleteErrorCode}
|
||||
onFileView={handleFileView}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Title level={4} style={{ margin: '0 0 24px 0' }}>Edit Brand Device</Title>
|
||||
<Steps current={currentStep} style={{ marginBottom: 24 }}>
|
||||
<Step title="Brand Device Details" />
|
||||
<Step title="Error Codes" />
|
||||
</Steps>
|
||||
<div style={{ position: 'relative' }}>
|
||||
{loading && (
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.6)',
|
||||
backdropFilter: 'blur(0.8px)',
|
||||
filter: 'blur(0.5px)',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
zIndex: 10,
|
||||
borderRadius: '8px'
|
||||
}}>
|
||||
<Spin size="large" />
|
||||
</div>
|
||||
)}
|
||||
<div style={{ filter: loading ? 'blur(0.5px)' : 'none', transition: 'filter 0.3s ease' }}>
|
||||
{renderStepContent()}
|
||||
</div>
|
||||
</div>
|
||||
<Divider />
|
||||
<FormActions
|
||||
currentStep={currentStep}
|
||||
onPreviousStep={() => setCurrentStep(currentStep - 1)}
|
||||
onNextStep={handleNextStep}
|
||||
onSave={handleFinish}
|
||||
onCancel={handleCancel}
|
||||
confirmLoading={confirmLoading}
|
||||
isEditMode={true}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditBrandDevice;
|
||||
Reference in New Issue
Block a user