588 lines
23 KiB
JavaScript
588 lines
23 KiB
JavaScript
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; |