update ui brand device
This commit is contained in:
588
src/pages/master/brandDevice/AddEditErrorCode.jsx
Normal file
588
src/pages/master/brandDevice/AddEditErrorCode.jsx
Normal file
@@ -0,0 +1,588 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import {
|
||||
Card,
|
||||
Typography,
|
||||
Button,
|
||||
Form,
|
||||
Row,
|
||||
Col,
|
||||
Spin,
|
||||
Upload,
|
||||
} from 'antd';
|
||||
import { ArrowLeftOutlined, UploadOutlined } from '@ant-design/icons';
|
||||
import { getBrandById, getErrorCodeById, updateBrand, getErrorCodesByBrandId } from '../../../api/master-brand';
|
||||
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
|
||||
import { useBrandForm } from '../../../context/BrandFormContext';
|
||||
import { uploadFile } from '../../../api/file-uploads';
|
||||
import ErrorCodeSimpleForm from './component/ErrorCodeSimpleForm';
|
||||
import SolutionForm from './component/SolutionForm';
|
||||
import { useSolutionLogic } from './hooks/solution';
|
||||
import SingleSparepartSelect from './component/SingleSparepartSelect';
|
||||
import { NotifAlert, NotifOk } from '../../../components/Global/ToastNotif';
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
const AddEditErrorCode = () => {
|
||||
const navigate = useNavigate();
|
||||
const { brandId: routeBrandId, errorCodeId } = useParams();
|
||||
const { setBreadcrumbItems } = useBreadcrumb();
|
||||
|
||||
// Use BrandForm context
|
||||
const {
|
||||
brandId: contextBrandId,
|
||||
routeBrandId: contextRouteBrandId,
|
||||
setRouteBrandId,
|
||||
setErrorCodeId,
|
||||
initializeFromRoute,
|
||||
tempErrorCodes,
|
||||
existingErrorCodes,
|
||||
addErrorCode,
|
||||
updateErrorCode,
|
||||
setCurrentErrorCode
|
||||
} = useBrandForm();
|
||||
|
||||
// Use brandId from context first, fallback to route
|
||||
const currentBrandId = contextBrandId || routeBrandId;
|
||||
|
||||
// Forms
|
||||
const [errorCodeForm] = Form.useForm();
|
||||
const [solutionForm] = Form.useForm();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||
const [errorCodeIcon, setErrorCodeIcon] = useState(null);
|
||||
const [selectedSparepartIds, setSelectedSparepartIds] = useState([]);
|
||||
const [isEdit, setIsEdit] = useState(false);
|
||||
const [fileList, setFileList] = useState([]);
|
||||
|
||||
const {
|
||||
solutionFields,
|
||||
solutionTypes,
|
||||
solutionStatuses,
|
||||
solutionsToDelete,
|
||||
firstSolutionValid,
|
||||
handleAddSolutionField,
|
||||
handleRemoveSolutionField,
|
||||
handleSolutionTypeChange,
|
||||
handleSolutionStatusChange,
|
||||
resetSolutionFields,
|
||||
getSolutionData,
|
||||
setSolutionsForExistingRecord,
|
||||
} = useSolutionLogic(solutionForm);
|
||||
|
||||
useEffect(() => {
|
||||
const isEditMode = errorCodeId && errorCodeId !== 'add';
|
||||
setIsEdit(isEditMode);
|
||||
|
||||
// Initialize context with route parameters
|
||||
if (routeBrandId) {
|
||||
initializeFromRoute(routeBrandId, errorCodeId);
|
||||
}
|
||||
|
||||
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', cursor: 'pointer' }}
|
||||
onClick={() => navigate(`/master/brand-device/edit/${currentBrandId}?tab=error-codes`)}
|
||||
>
|
||||
Edit Brand Device
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: (
|
||||
<span style={{ fontSize: '14px', fontWeight: 'bold' }}>
|
||||
{isEditMode ? 'Edit Error Code' : 'Add Error Code'}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
]);
|
||||
|
||||
if (isEditMode && errorCodeId) {
|
||||
// For existing error codes, construct the proper tempId format
|
||||
const tempId = errorCodeId.startsWith('existing_') ? errorCodeId : `existing_${errorCodeId}`;
|
||||
loadExistingErrorCode(tempId);
|
||||
}
|
||||
}, [currentBrandId, errorCodeId, navigate, setBreadcrumbItems]);
|
||||
|
||||
const loadExistingErrorCode = async (tempId) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
// console.log(' Looking for error code with tempId:', tempId);
|
||||
// console.log(' Available error codes in context:', tempErrorCodes);
|
||||
|
||||
// Find error code in tempErrorCodes first
|
||||
let existingErrorCode = tempErrorCodes.find(ec => ec.tempId === tempId);
|
||||
|
||||
// If not found, check in existingErrorCodes with format existing_${error_code_id}
|
||||
if (!existingErrorCode && tempId.startsWith('existing_')) {
|
||||
const errorId = tempId.replace('existing_', '');
|
||||
existingErrorCode = existingErrorCodes.find(ec => ec.error_code_id == errorId);
|
||||
if (existingErrorCode) {
|
||||
existingErrorCode = {
|
||||
...existingErrorCode,
|
||||
tempId: tempId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// console.log(' Found error code in context:', existingErrorCode);
|
||||
|
||||
if (existingErrorCode) {
|
||||
errorCodeForm.setFieldsValue({
|
||||
error_code: existingErrorCode.error_code,
|
||||
error_code_name: existingErrorCode.error_code_name || '',
|
||||
error_code_description: existingErrorCode.error_code_description || '',
|
||||
error_code_color: existingErrorCode.error_code_color || '#000000',
|
||||
status: existingErrorCode.is_active !== false,
|
||||
});
|
||||
|
||||
if (existingErrorCode.path_icon) {
|
||||
setErrorCodeIcon({
|
||||
name: existingErrorCode.path_icon.split('/').pop(),
|
||||
uploadPath: existingErrorCode.path_icon,
|
||||
url: existingErrorCode.path_icon,
|
||||
});
|
||||
}
|
||||
|
||||
if (existingErrorCode.solution && existingErrorCode.solution.length > 0) {
|
||||
// console.log('🔍 Setting solutions from context:', existingErrorCode.solution);
|
||||
setSolutionsForExistingRecord(existingErrorCode.solution, solutionForm);
|
||||
}
|
||||
|
||||
if (existingErrorCode.spareparts && existingErrorCode.spareparts.length > 0) {
|
||||
// console.log('🔍 Setting spareparts from context:', existingErrorCode.spareparts);
|
||||
setSelectedSparepartIds(existingErrorCode.spareparts);
|
||||
}
|
||||
} else {
|
||||
// console.log('🔍 Error code not found in context, trying API...');
|
||||
|
||||
let errorIdToUse = tempId;
|
||||
// Extract the actual error_code_id from tempId format
|
||||
if (tempId.startsWith('existing_')) {
|
||||
errorIdToUse = tempId.replace('existing_', '');
|
||||
}
|
||||
|
||||
const errorCodeResponse = await getErrorCodeById(errorIdToUse);
|
||||
|
||||
if (errorCodeResponse && errorCodeResponse.statusCode === 200) {
|
||||
const errorData = errorCodeResponse.data;
|
||||
|
||||
if (errorData) {
|
||||
errorCodeForm.setFieldsValue({
|
||||
error_code: errorData.error_code,
|
||||
error_code_name: errorData.error_code_name || '',
|
||||
error_code_description: errorData.error_code_description || '',
|
||||
error_code_color: errorData.error_code_color || '#000000',
|
||||
status: errorData.is_active !== false,
|
||||
});
|
||||
|
||||
if (errorData.path_icon) {
|
||||
setErrorCodeIcon({
|
||||
name: errorData.path_icon.split('/').pop(),
|
||||
uploadPath: errorData.path_icon,
|
||||
url: errorData.path_icon,
|
||||
});
|
||||
}
|
||||
|
||||
// Set solutions from API data (include file data)
|
||||
if (errorData.solution && errorData.solution.length > 0) {
|
||||
setSolutionsForExistingRecord(errorData.solution, solutionForm);
|
||||
}
|
||||
|
||||
// Set spareparts from API data
|
||||
if (errorData.spareparts && errorData.spareparts.length > 0) {
|
||||
const sparepartIds = errorData.spareparts.map(sp => sp.sparepart_id);
|
||||
setSelectedSparepartIds(sparepartIds);
|
||||
}
|
||||
|
||||
// Don't add to context - this is existing data from API
|
||||
// The context should already have this error code from the brand data loading
|
||||
}
|
||||
} else {
|
||||
// console.log('🔍 API Response error or not found:', errorCodeResponse);
|
||||
errorCodeForm.setFieldsValue({
|
||||
error_code: '',
|
||||
error_code_name: '',
|
||||
error_code_description: '',
|
||||
error_code_color: '#000000',
|
||||
status: true,
|
||||
});
|
||||
|
||||
NotifAlert({
|
||||
icon: 'warning',
|
||||
title: 'Peringatan',
|
||||
message: 'Error code not found. Creating new error code.',
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load error code:', error);
|
||||
NotifAlert({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
message: 'Failed to load error code data',
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
await errorCodeForm.validateFields();
|
||||
|
||||
const solutionValues = solutionForm.getFieldsValue();
|
||||
|
||||
const firstSolutionPath = `solution_items,${solutionFields[0]?.key || 0}`;
|
||||
const firstSolution = solutionValues[firstSolutionPath];
|
||||
|
||||
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) {
|
||||
NotifAlert({
|
||||
icon: 'warning',
|
||||
title: 'Perhatian',
|
||||
message: 'Harap lengkapi minimal 1 solution',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const errorCodeValues = errorCodeForm.getFieldsValue();
|
||||
|
||||
const solutionData = getSolutionData();
|
||||
|
||||
// Determine the correct tempId for editing
|
||||
let updateTempId;
|
||||
if (isEdit) {
|
||||
// For existing error codes, find the correct tempId
|
||||
if (errorCodeId && !errorCodeId.startsWith('pending-')) {
|
||||
// Look for existing error code in context
|
||||
const existingEc = existingErrorCodes.find(ec => ec.error_code_id == errorCodeId);
|
||||
if (existingEc && existingEc.tempId) {
|
||||
updateTempId = existingEc.tempId;
|
||||
} else {
|
||||
updateTempId = `existing_${errorCodeId}`;
|
||||
}
|
||||
} else {
|
||||
updateTempId = errorCodeId;
|
||||
}
|
||||
} else {
|
||||
updateTempId = Date.now().toString();
|
||||
}
|
||||
|
||||
const currentErrorCode = {
|
||||
tempId: updateTempId,
|
||||
error_code_id: isEdit && errorCodeId && !errorCodeId.startsWith('pending-') ? errorCodeId : null,
|
||||
error_code: errorCodeValues.error_code || '',
|
||||
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 || [],
|
||||
spareparts: selectedSparepartIds || [],
|
||||
errorCodeIcon: errorCodeIcon,
|
||||
};
|
||||
|
||||
if (isEdit) {
|
||||
updateErrorCode(updateTempId, currentErrorCode);
|
||||
} else {
|
||||
addErrorCode(currentErrorCode);
|
||||
}
|
||||
|
||||
NotifOk({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
message: isEdit ? 'Error Code berhasil diupdate!' : 'Error Code berhasil ditambahkan!',
|
||||
});
|
||||
|
||||
navigate(`/master/brand-device/edit/${currentBrandId}?tab=error-codes`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error saving error code:', error);
|
||||
NotifAlert({
|
||||
icon: 'error',
|
||||
title: 'Error',
|
||||
message: 'Gagal menyimpan error code. Silakan coba lagi.',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
navigate(`/master/brand-device/edit/${currentBrandId}?tab=error-codes`);
|
||||
};
|
||||
|
||||
const handleErrorCodeIconUpload = async (file) => {
|
||||
if (!file) return null;
|
||||
|
||||
try {
|
||||
const folder = 'images';
|
||||
const response = await uploadFile(file, folder);
|
||||
|
||||
if (response && response.statusCode === 200) {
|
||||
const iconData = {
|
||||
name: file.name,
|
||||
uploadPath: response.data.path_document,
|
||||
url: response.data.path_document,
|
||||
};
|
||||
|
||||
setErrorCodeIcon(iconData);
|
||||
NotifOk({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
message: 'Error code icon uploaded successfully',
|
||||
});
|
||||
return iconData;
|
||||
} else {
|
||||
NotifAlert({
|
||||
icon: 'error',
|
||||
title: 'Gagal',
|
||||
message: response?.message || 'Failed to upload error code icon',
|
||||
});
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error uploading icon:', error);
|
||||
NotifAlert({
|
||||
icon: 'error',
|
||||
title: 'Gagal',
|
||||
message: 'Failed to upload error code icon',
|
||||
});
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const handleErrorCodeIconRemove = () => {
|
||||
setErrorCodeIcon(null);
|
||||
};
|
||||
|
||||
const handleSolutionFileUpload = async (file, solutionKey) => {
|
||||
if (!file) return null;
|
||||
|
||||
try {
|
||||
// Determine folder based on file type
|
||||
const fileExtension = file.name.split('.').pop().toLowerCase();
|
||||
const folder = ['pdf'].includes(fileExtension) ? 'pdf' : 'images';
|
||||
|
||||
const response = await uploadFile(file, folder);
|
||||
|
||||
if (response && response.statusCode === 200) {
|
||||
const fileData = {
|
||||
name: file.name,
|
||||
uploadPath: response.data.path_document,
|
||||
url: response.data.path_document,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
};
|
||||
|
||||
NotifOk({
|
||||
icon: 'success',
|
||||
title: 'Berhasil',
|
||||
message: 'Solution file uploaded successfully',
|
||||
});
|
||||
return fileData;
|
||||
} else {
|
||||
NotifAlert({
|
||||
icon: 'error',
|
||||
title: 'Gagal',
|
||||
message: response?.message || 'Failed to upload solution file',
|
||||
});
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error uploading solution file:', error);
|
||||
NotifAlert({
|
||||
icon: 'error',
|
||||
title: 'Gagal',
|
||||
message: 'Failed to upload solution file',
|
||||
});
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSolutionFileView = (fileData) => {
|
||||
if (fileData && fileData.url) {
|
||||
window.open(fileData.url, '_blank');
|
||||
}
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
errorCodeForm.resetFields();
|
||||
errorCodeForm.setFieldsValue({
|
||||
status: true,
|
||||
});
|
||||
setErrorCodeIcon(null);
|
||||
resetSolutionFields();
|
||||
setSelectedSparepartIds([]);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
{/* Header */}
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: 24
|
||||
}}>
|
||||
<Title level={4} style={{ margin: 0 }}>
|
||||
{isEdit ? 'Edit Error Code' : 'Add Error Code'}
|
||||
</Title>
|
||||
<Button
|
||||
icon={<ArrowLeftOutlined />}
|
||||
onClick={handleCancel}
|
||||
>
|
||||
Back to Brand Device
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div style={{ position: 'relative', minHeight: 500 }}>
|
||||
{loading && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.7)',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
zIndex: 10,
|
||||
}}
|
||||
>
|
||||
<Spin size="large" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Row gutter={[24, 24]}>
|
||||
{/* Error Code Form */}
|
||||
<Col xs={24} lg={8}>
|
||||
<Card
|
||||
title="Error Code Details"
|
||||
size="small"
|
||||
style={{ height: 'fit-content' }}
|
||||
>
|
||||
<Form
|
||||
form={errorCodeForm}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
status: true,
|
||||
error_code_color: '#000000',
|
||||
}}
|
||||
>
|
||||
<ErrorCodeSimpleForm
|
||||
errorCodeForm={errorCodeForm}
|
||||
isErrorCodeFormReadOnly={false}
|
||||
errorCodeIcon={errorCodeIcon}
|
||||
onErrorCodeIconUpload={handleErrorCodeIconUpload}
|
||||
onErrorCodeIconRemove={handleErrorCodeIconRemove}
|
||||
/>
|
||||
</Form>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
{/* Solutions Form */}
|
||||
<Col xs={24} lg={8}>
|
||||
<Card
|
||||
title="Solutions"
|
||||
size="small"
|
||||
style={{ height: 'fit-content' }}
|
||||
>
|
||||
<Form
|
||||
form={solutionForm}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
solution_items: [{
|
||||
status: true,
|
||||
type: 'text',
|
||||
}]
|
||||
}}
|
||||
>
|
||||
<SolutionForm
|
||||
solutionForm={solutionForm}
|
||||
solutionFields={solutionFields}
|
||||
solutionTypes={solutionTypes}
|
||||
solutionStatuses={solutionStatuses}
|
||||
firstSolutionValid={firstSolutionValid}
|
||||
checkFirstSolutionValid={() => {
|
||||
// console.log('🔍 checkFirstSolutionValid function:', typeof checkFirstSolutionValid);
|
||||
return checkFirstSolutionValid();
|
||||
}}
|
||||
onAddSolutionField={handleAddSolutionField}
|
||||
onRemoveSolutionField={handleRemoveSolutionField}
|
||||
onSolutionTypeChange={handleSolutionTypeChange}
|
||||
onSolutionStatusChange={handleSolutionStatusChange}
|
||||
onSolutionFileUpload={handleSolutionFileUpload}
|
||||
onFileView={handleSolutionFileView}
|
||||
fileList={fileList}
|
||||
isReadOnly={false}
|
||||
/>
|
||||
</Form>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
{/* Sparepart Selection */}
|
||||
<Col xs={24} lg={8}>
|
||||
<Card
|
||||
title="Spareparts"
|
||||
size="small"
|
||||
style={{ height: 'fit-content' }}
|
||||
>
|
||||
<SingleSparepartSelect
|
||||
selectedSparepartIds={selectedSparepartIds}
|
||||
onSparepartChange={setSelectedSparepartIds}
|
||||
isReadOnly={false}
|
||||
brandId={currentBrandId}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* Save Button */}
|
||||
<div style={{ marginTop: 24, textAlign: 'right' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
style={{
|
||||
backgroundColor: '#23A55A',
|
||||
borderColor: '#23A55A',
|
||||
}}
|
||||
onClick={handleSave}
|
||||
>
|
||||
{isEdit ? 'Update Error Code' : 'Save Error Code'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddEditErrorCode;
|
||||
Reference in New Issue
Block a user