repair: add edit brand device

This commit is contained in:
2025-12-08 16:45:49 +07:00
parent 03be3a6a99
commit 5703ff0e8d
16 changed files with 1194 additions and 2198 deletions

View File

@@ -19,10 +19,9 @@ import TableList from '../../../components/Global/TableList';
import { ConfigProvider } from 'antd';
import { NotifAlert, NotifOk, NotifConfirmDialog } from '../../../components/Global/ToastNotif';
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
import { createBrand } from '../../../api/master-brand';
import { createBrand, createErrorCode, getErrorCodesByBrandId, updateErrorCode, deleteErrorCode } from '../../../api/master-brand';
import BrandForm from './component/BrandForm';
import { useSolutionLogic } from './hooks/solution';
import { useBrandForm } from '../../../context/BrandFormContext';
const { Title } = Typography;
const { Step } = Steps;
@@ -32,32 +31,24 @@ const AddBrandDevice = () => {
const [searchParams] = useSearchParams();
const { setBreadcrumbItems } = useBreadcrumb();
const [brandForm] = Form.useForm();
const [solutionForm] = Form.useForm();
const [errorCodeIcon, setErrorCodeIcon] = useState(null);
const [selectedSparepartIds, setSelectedSparepartIds] = useState([]);
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([]);
// Context integration
const {
brandId,
brandInfo,
setBrandInfo,
tempErrorCodes,
addErrorCode,
updateErrorCode,
deleteErrorCode,
prepareSubmissionData,
validateForm,
resetForm,
} = useBrandForm();
// Use step from query parameter or context
const tab = searchParams.get('tab');
const [currentStep, setCurrentStep] = useState(tab === 'error-codes' ? 1 : 0);
// Error code management states
const [errorCodeForm] = Form.useForm();
const [solutionForm] = Form.useForm();
const [errorCodeIcon, setErrorCodeIcon] = useState(null);
const [selectedSparepartIds, setSelectedSparepartIds] = useState([]);
const [editingErrorCodeKey, setEditingErrorCodeKey] = useState(null);
const [isErrorCodeFormReadOnly, setIsErrorCodeFormReadOnly] = useState(false);
const [isAddingNewErrorCode, setIsAddingNewErrorCode] = useState(false);
const {
solutionFields,
@@ -77,14 +68,54 @@ const AddBrandDevice = () => {
// Navigation functions
const handleNextStep = async () => {
try {
await brandForm.validateFields();
setCurrentStep(1);
setConfirmLoading(true);
const brandValues = await brandForm.validateFields();
const userId = JSON.parse(localStorage.getItem('user') || '{}').user_id || 1;
// Prepare brand data for API
const brandApiData = {
brand_name: brandValues.brand_name,
brand_type: brandValues.brand_type || '',
brand_manufacture: brandValues.brand_manufacture || '',
brand_model: brandValues.brand_model || '',
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);
NotifOk({
icon: 'success',
title: 'Berhasil',
message: 'Brand device berhasil dibuat. Silakan tambahkan error codes.',
});
setCurrentStep(1);
// Trigger refresh untuk error codes table di fase 2
setTimeout(() => {
setTrigerFilter(prev => !prev);
}, 100);
} else {
NotifAlert({
icon: 'error',
title: 'Gagal',
message: response?.message || 'Gagal membuat brand device',
});
}
} catch (error) {
NotifAlert({
icon: 'warning',
title: 'Perhatian',
message: 'Harap isi semua kolom wajib untuk brand device!',
icon: 'error',
title: 'Gagal',
message: error.message || 'Gagal membuat brand device',
});
} finally {
setConfirmLoading(false);
}
};
@@ -97,13 +128,50 @@ const AddBrandDevice = () => {
};
const handleAddErrorCode = () => {
navigate(`/master/brand-device/add/error-code/add`);
if (createdBrandId) {
resetErrorCodeForm();
setIsAddingNewErrorCode(true);
setIsErrorCodeFormReadOnly(false);
setEditingErrorCodeKey(null);
} else {
NotifAlert({
icon: 'warning',
title: 'Perhatian',
message: 'Brand device harus dibuat terlebih dahulu.',
});
}
};
const handleEditErrorCodeNavigate = (record) => {
const errorCodeId = record.status === 'existing' ? record.error_code_id : record.tempId;
if (errorCodeId) {
navigate(`/master/brand-device/add/error-code/edit/${errorCodeId}`);
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);
}
}
};
@@ -112,15 +180,33 @@ const AddBrandDevice = () => {
icon: 'question',
title: 'Konfirmasi Hapus',
message: `Apakah Anda yakin ingin menghapus error code "${record.error_code}"?`,
onConfirm: () => {
const tempId = record.tempId || `existing_${record.error_code_id}`;
deleteErrorCode(tempId, false); // false = soft delete
NotifOk({
icon: 'success',
title: 'Berhasil',
message: 'Error code berhasil dihapus!',
});
setTrigerFilter(prev => !prev);
onConfirm: async () => {
try {
const errorCodeToDelete = record.error_code_id;
const response = await deleteErrorCode(createdBrandId, errorCodeToDelete);
if (response && (response.statusCode === 200 || response.statusCode === 201)) {
NotifOk({
icon: 'success',
title: 'Berhasil',
message: 'Error code berhasil dihapus!',
});
setTrigerFilter(prev => !prev);
} else {
NotifAlert({
icon: 'error',
title: 'Gagal',
message: response?.message || 'Gagal menghapus error code',
});
}
} catch (error) {
console.error('Error deleting error code:', error);
NotifAlert({
icon: 'error',
title: 'Gagal',
message: error.message || 'Gagal menghapus error code',
});
}
},
onCancel: () => {}
});
@@ -142,8 +228,8 @@ const AddBrandDevice = () => {
};
const handleBrandFormValuesChange = useCallback((changedValues, allValues) => {
setBrandInfo(allValues);
}, [setBrandInfo]);
setBrandData(allValues);
}, []);
const getErrorCodesData = async (params) => {
try {
@@ -151,12 +237,33 @@ const AddBrandDevice = () => {
const page = parseInt(params.get('page')) || 1;
const limit = parseInt(params.get('limit')) || 10;
const allErrorCodes = tempErrorCodes.filter(ec => ec.status !== 'deleted');
let allErrorCodes = [];
let filteredData = allErrorCodes;
// Get error codes from API if brand is created
if (createdBrandId) {
const queryParams = new URLSearchParams({
page: page.toString(),
limit: limit.toString(),
...(search && { search })
});
const response = await getErrorCodesByBrandId(createdBrandId, queryParams);
if (response && response.statusCode === 200) {
const apiErrorData = response.data || [];
allErrorCodes = apiErrorData.map(ec => ({
...ec,
tempId: `existing_${ec.error_code_id}`,
status: 'existing'
}));
}
}
// Add temp error codes
allErrorCodes = [...allErrorCodes, ...tempErrorCodes.filter(ec => ec.status !== 'deleted')];
// Filter by search text if needed
if (searchText) {
filteredData = allErrorCodes.filter(ec =>
allErrorCodes = allErrorCodes.filter(ec =>
ec.error_code.toLowerCase().includes(searchText.toLowerCase()) ||
ec.error_code_name.toLowerCase().includes(searchText.toLowerCase())
);
@@ -164,15 +271,15 @@ const AddBrandDevice = () => {
const startIndex = 0;
const endIndex = startIndex + limit;
const paginatedData = filteredData.slice(startIndex, endIndex);
const paginatedData = allErrorCodes.slice(startIndex, endIndex);
return {
data: paginatedData,
pagination: {
current_page: page,
current_limit: limit,
total_limit: filteredData.length,
total_page: Math.ceil(filteredData.length / limit),
total_limit: allErrorCodes.length,
total_page: Math.ceil(allErrorCodes.length / limit),
}
};
} catch (error) {
@@ -264,39 +371,120 @@ const AddBrandDevice = () => {
return params;
}, [searchValue]);
const handleFinish = async () => {
setConfirmLoading(true);
const resetErrorCodeForm = () => {
errorCodeForm.resetFields();
errorCodeForm.setFieldsValue({
status: true,
});
setErrorCodeIcon(null);
resetSolutionFields();
setIsErrorCodeFormReadOnly(false);
setEditingErrorCodeKey(null);
setSelectedSparepartIds([]);
setIsAddingNewErrorCode(false);
};
const handleSaveErrorCode = async () => {
try {
// Validate form using context
const validation = validateForm();
if (!validation.isValid) {
await errorCodeForm.validateFields();
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) {
NotifAlert({
icon: 'warning',
title: 'Perhatian',
message: 'Harap lengkapi minimal 1 solution',
});
return;
}
const submissionData = prepareSubmissionData(1);
const errorCodeValues = errorCodeForm.getFieldsValue();
const solutionData = getSolutionData();
const response = await createBrand(submissionData);
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 || [],
spareparts: selectedSparepartIds || []
};
// For create, include error_code field (required)
if (isAddingNewErrorCode) {
payload.error_code = errorCodeValues.error_code;
}
let response;
if (isAddingNewErrorCode) {
response = await createErrorCode(createdBrandId, payload);
} else {
response = await updateErrorCode(createdBrandId, editingErrorCodeKey, payload);
}
if (response && (response.statusCode === 200 || response.statusCode === 201)) {
NotifOk({
icon: 'success',
title: 'Berhasil',
message: response.message || 'Brand Device berhasil ditambahkan.',
message: isAddingNewErrorCode ? 'Error Code berhasil ditambahkan!' : 'Error Code berhasil diupdate!',
});
resetForm();
navigate('/master/brand-device');
resetErrorCodeForm();
setTrigerFilter(prev => !prev);
} else {
NotifAlert({
icon: 'error',
title: 'Gagal',
message: response?.message || 'Gagal menambahkan Brand Device',
message: response?.message || 'Gagal menyimpan error code',
});
}
} catch (error) {
console.error('Error saving error code:', error);
NotifAlert({
icon: 'error',
title: 'Gagal',
message: error.message || 'Gagal menyimpan error code. Silakan coba lagi.',
});
}
};
const handleErrorCodeIconRemove = () => {
setErrorCodeIcon(null);
};
const handleFinish = async () => {
setConfirmLoading(true);
try {
// Fase 2 completion - brand sudah dibuat di fase 1
NotifOk({
icon: 'success',
title: 'Brand Device Tersimpan',
message: 'Brand device telah berhasil disimpan dengan error codes yang ditambahkan.',
});
navigate('/master/brand-device');
} catch (error) {
NotifAlert({
icon: 'error',
title: 'Gagal',
message: error.message || 'Gagal menyimpan data. Silakan coba lagi.',
message: error.message || 'Terjadi kesalahan. Silakan coba lagi.',
});
} finally {
setConfirmLoading(false);
@@ -391,7 +579,7 @@ const AddBrandDevice = () => {
>
<Button
icon={<PlusOutlined />}
onClick={handleAddErrorCode}
onClick={() => navigate(`/master/brand-device/${createdBrandId}/error-code/add`)}
size="large"
>
Add Error Code
@@ -407,11 +595,11 @@ const AddBrandDevice = () => {
cardColor={'#42AAFF'}
header={'error_code'}
showPreviewModal={handlePreviewErrorCode}
showEditModal={handleEditErrorCodeNavigate}
showEditModal={(record) => navigate(`/master/brand-device/${createdBrandId}/error-code/edit/${record.error_code_id}`)}
showDeleteDialog={handleDeleteErrorCode}
getData={getErrorCodesData}
queryParams={queryParams}
columns={errorCodeColumns(handlePreviewErrorCode, handleEditErrorCodeNavigate, handleDeleteErrorCode)}
columns={errorCodeColumns(handlePreviewErrorCode, (record) => navigate(`/master/brand-device/${createdBrandId}/error-code/edit/${record.error_code_id}`), handleDeleteErrorCode)}
triger={trigerFilter}
/>
</Col>
@@ -447,6 +635,12 @@ const AddBrandDevice = () => {
]);
}, [setBreadcrumbItems, navigate]);
useEffect(() => {
if (createdBrandId && currentStep === 1) {
setTrigerFilter(prev => !prev);
}
}, [createdBrandId, currentStep]);
return (
<Card>
<Title level={4} style={{ margin: '0 0 24px 0' }}>
@@ -525,7 +719,7 @@ const AddBrandDevice = () => {
borderColor: '#23A55A',
}}
>
Save Brand Device
Selesai
</Button>
)}
</div>