- {loading && (
+
+
+
+
+
+
+
-
+
+
);
};
diff --git a/src/pages/master/brandDevice/AddEditErrorCode.jsx b/src/pages/master/brandDevice/AddEditErrorCode.jsx
deleted file mode 100644
index b721c64..0000000
--- a/src/pages/master/brandDevice/AddEditErrorCode.jsx
+++ /dev/null
@@ -1,467 +0,0 @@
-import { useEffect, useState } from 'react';
-import { useNavigate, useParams, useLocation } from 'react-router-dom';
-import {
- Card,
- Typography,
- Button,
- Form,
- Row,
- Col,
- Spin,
- Upload,
-} from 'antd';
-import { ArrowLeftOutlined } from '@ant-design/icons';
-import { getErrorCodeById, createErrorCode, updateErrorCode } from '../../../api/master-brand';
-import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
-import ErrorCodeForm from './component/ErrorCodeForm';
-import SolutionForm from './component/SolutionForm';
-import { useSolutionLogic } from './hooks/solution';
-import SparepartSelect from './component/SparepartSelect';
-import { NotifAlert, NotifOk } from '../../../components/Global/ToastNotif';
-
-const { Title } = Typography;
-
-const AddEditErrorCode = () => {
- const navigate = useNavigate();
- const { brandId: routeBrandId, errorCodeId } = useParams();
- const { setBreadcrumbItems } = useBreadcrumb();
- const location = useLocation();
-
- 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 [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);
-
- if (!isEditMode) {
- resetSolutionFields();
- }
-
- setBreadcrumbItems([
- {
- title:
• Master
- },
- {
- title: (
-
navigate('/master/brand-device')}
- >
- Brand Device
-
- ),
- },
- {
- title: (
-
navigate(`/master/brand-device/edit/${currentBrandId}?tab=error-codes`)}
- >
- Edit Brand Device
-
- ),
- },
- {
- title: (
-
- {isEditMode ? 'Edit Error Code' : 'Add Error Code'}
-
- ),
- },
- ]);
-
- if (isEditMode && errorCodeId) {
- const tempId = errorCodeId.startsWith('existing_') ? errorCodeId : `existing_${errorCodeId}`;
- loadExistingErrorCode(tempId);
- }
- }, [currentBrandId, errorCodeId, navigate, setBreadcrumbItems]);
-
- const loadExistingErrorCode = async (tempId) => {
- try {
- setLoading(true);
-
- let errorIdToUse = tempId;
- 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,
- });
- }
-
- if (errorData.solution && errorData.solution.length > 0) {
- setSolutionsForExistingRecord(errorData.solution, solutionForm);
- }
-
- if (errorData.spareparts && errorData.spareparts.length > 0) {
- const sparepartIds = errorData.spareparts.map(sp => sp.sparepart_id);
- setSelectedSparepartIds(sparepartIds);
- }
- }
- } else {
- 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) {
- NotifAlert({
- icon: 'error',
- title: 'Error',
- message: 'Failed to load error code data',
- });
- } finally {
- setLoading(false);
- }
- };
-
- const handleSave = async () => {
- try {
- await errorCodeForm.validateFields();
-
- const solutionData = getSolutionData();
-
-
- if (!solutionData || solutionData.length === 0) {
- NotifAlert({
- icon: 'warning',
- title: 'Perhatian',
- message: 'Harap lengkapi minimal 1 solution',
- });
- return;
- }
-
- const invalidSolutions = solutionData.filter(solution => {
- if (solution.type_solution === 'text') {
- return !solution.text_solution || solution.text_solution.trim() === '';
- } 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({
- icon: 'warning',
- title: 'Perhatian',
- message: `Harap lengkapi solution berikut:\n${invalidSolutions.map(s =>
- `- ${s.solution_name}: ${s.type_solution === 'text' ? 'Text solution wajib diisi' : 'File wajib diupload'}`
- ).join('\n')}`,
- });
- return;
- }
-
- const errorCodeValues = errorCodeForm.getFieldsValue();
-
- setConfirmLoading(true);
-
- try {
- 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?.path_icon || errorCodeIcon?.uploadPath || '',
- is_active: errorCodeValues.status !== undefined ? errorCodeValues.status : true,
- solution: solutionData || [],
- spareparts: selectedSparepartIds || []
- };
-
- if (!isEdit) {
- payload.error_code = errorCodeValues.error_code;
- }
-
-
- let response;
-
- if (isEdit && errorCodeId) {
- 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',
- title: 'Berhasil',
- message: isEdit ? 'Error Code berhasil diupdate!' : 'Error Code berhasil ditambahkan!',
- });
-
- if (isFromAddBrand) {
- navigate(`/master/brand-device/add`);
- } else {
- navigate(`/master/brand-device/edit/${currentBrandId}?tab=error-codes`, {
- state: { refreshErrorCodes: true }
- });
- }
- } else {
- NotifAlert({
- icon: 'error',
- title: 'Gagal',
- message: response?.message || 'Gagal menyimpan error code',
- });
- }
- } catch (error) {
- NotifAlert({
- icon: 'error',
- title: 'Gagal',
- message: error.message || 'Gagal menyimpan error code. Silakan coba lagi.',
- });
- } finally {
- setConfirmLoading(false);
- }
-
- } catch (error) {
- NotifAlert({
- icon: 'error',
- title: 'Error',
- message: 'Gagal menyimpan error code. Silakan coba lagi.',
- });
- }
- };
-
- const handleCancel = () => {
- if (isFromAddBrand) {
- navigate(`/master/brand-device/add`);
- } else {
- navigate(`/master/brand-device/edit/${currentBrandId}?tab=error-codes`);
- }
- };
-
- const handleErrorCodeIconUpload = (iconData) => {
- if (!iconData || !iconData.uploadPath) {
- return null;
- }
-
- const formattedIconData = {
- name: iconData.name,
- uploadPath: iconData.uploadPath,
- url: iconData.uploadPath,
- };
-
- setErrorCodeIcon(formattedIconData);
- return formattedIconData;
- };
-
- const handleErrorCodeIconRemove = () => {
- setErrorCodeIcon(null);
- };
-
- const handleSolutionFileUpload = (fileObject) => {
- };
-
- const resetForm = () => {
- errorCodeForm.resetFields();
- errorCodeForm.setFieldsValue({
- status: true,
- });
- setErrorCodeIcon(null);
- resetSolutionFields();
- setSelectedSparepartIds([]);
- };
-
- return (
-
- {/* Header */}
-
-
- {isEdit ? 'Edit Error Code' : 'Add Error Code'}
-
- }
- onClick={handleCancel}
- >
- Back to Brand Device
-
-
-
- {/* Content */}
-
- {loading && (
-
-
-
- )}
-
-
- {/* Error Code Form */}
-
-
-
-
-
-
- {/* Solutions Form */}
-
-
-
-
-
-
- {/* Sparepart Selection */}
-
-
-
-
-
-
-
- {/* Save Button */}
-
-
-
-
-
-
- );
-};
-
-export default AddEditErrorCode;
\ No newline at end of file
diff --git a/src/pages/master/brandDevice/EditBrandDevice.jsx b/src/pages/master/brandDevice/EditBrandDevice.jsx
index f3402a8..d5190e4 100644
--- a/src/pages/master/brandDevice/EditBrandDevice.jsx
+++ b/src/pages/master/brandDevice/EditBrandDevice.jsx
@@ -13,17 +13,17 @@ import {
Tag,
Space,
Input,
+ ConfigProvider
} from 'antd';
import { EyeOutlined, EditOutlined, DeleteOutlined, PlusOutlined, SearchOutlined } from '@ant-design/icons';
import { NotifAlert, NotifOk, NotifConfirmDialog } from '../../../components/Global/ToastNotif';
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
-import { getBrandById, getErrorCodesByBrandId, getErrorCodeById, deleteErrorCode, updateErrorCode as updateErrorCodeAPI, createErrorCode as createErrorCodeAPI } from '../../../api/master-brand';
+import { getBrandById, updateBrand, 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 ErrorCodeForm from './component/ErrorCodeForm';
import SolutionForm from './component/SolutionForm';
-import FormActions from './component/FormActions';
import SparepartSelect from './component/SparepartSelect';
import ListErrorCode from './component/ListErrorCode';
@@ -57,6 +57,7 @@ const EditBrandDevice = () => {
const [solutionTypes, setSolutionTypes] = useState({ 0: 'text' });
const [solutionStatuses, setSolutionStatuses] = useState({ 0: true });
const [currentSolutionData, setCurrentSolutionData] = useState([]);
+ const [confirmLoading, setConfirmLoading] = useState(false);
const getSolutionData = () => {
if (!solutionForm) return [];
@@ -115,7 +116,7 @@ const EditBrandDevice = () => {
const isFileType = solution.type_solution && solution.type_solution !== 'text';
newSolutionTypes[fieldKey] = isFileType ? 'file' : 'text';
- newSolutionStatuses[fieldKey] = solution.is_active !== false;
+ newSolutionStatuses[fieldKey] = solution.is_active;
let fileObject = null;
if (isFileType && (solution.path_solution || solution.path_document)) {
@@ -135,7 +136,7 @@ const EditBrandDevice = () => {
name: solution.solution_name || '',
type: isFileType ? 'file' : 'text',
text: solution.text_solution || '',
- status: solution.is_active !== false,
+ status: solution.is_active,
file: fileObject,
fileUpload: fileObject,
path_solution: solution.path_solution || solution.path_document || null,
@@ -387,17 +388,64 @@ const EditBrandDevice = () => {
const handleNextStep = async () => {
try {
- await brandForm.validateFields();
- const currentBrandId = id;
- if (currentBrandId) {
- navigate(`/master/brand-device/edit/${currentBrandId}?tab=error-codes`);
+ setConfirmLoading(true);
+
+ const brandValues = brandForm.getFieldsValue();
+
+ if (!brandValues.brand_name || brandValues.brand_name.trim() === '') {
+ NotifAlert({
+ icon: 'warning',
+ title: 'Perhatian',
+ message: 'Brand Name wajib diisi!',
+ });
+ return;
+ }
+
+ if (!brandValues.brand_manufacture || brandValues.brand_manufacture.trim() === '') {
+ NotifAlert({
+ icon: 'warning',
+ title: 'Perhatian',
+ message: 'Manufacturer wajib diisi!',
+ });
+ return;
+ }
+
+ const brandApiData = {
+ brand_name: brandValues.brand_name.trim(),
+ brand_type: brandValues.brand_type || '',
+ brand_manufacture: brandValues.brand_manufacture.trim(),
+ brand_model: brandValues.brand_model || '',
+ is_active: brandValues.is_active !== undefined ? brandValues.is_active : true
+ };
+
+ const response = await updateBrand(id, brandApiData);
+
+ if (response && (response.statusCode === 200 || response.statusCode === 201)) {
+ NotifOk({
+ icon: 'success',
+ title: 'Berhasil',
+ message: 'Brand device berhasil diupdate.',
+ });
+
+ const currentBrandId = id;
+ if (currentBrandId) {
+ navigate(`/master/brand-device/edit/${currentBrandId}?tab=error-codes`);
+ }
+ } else {
+ NotifAlert({
+ icon: 'error',
+ title: 'Gagal',
+ message: response?.message || 'Gagal mengupdate 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 mengupdate brand device',
});
+ } finally {
+ setConfirmLoading(false);
}
};
@@ -433,6 +481,7 @@ const EditBrandDevice = () => {
const handleSaveErrorCode = async () => {
try {
+ setConfirmLoading(true);
const errorCodeValues = await errorCodeForm.validateFields();
const solutionData = getSolutionData();
@@ -542,6 +591,8 @@ const EditBrandDevice = () => {
title: 'Perhatian',
message: error.message || 'Harap isi semua kolom wajib!',
});
+ } finally {
+ setConfirmLoading(false);
}
};
@@ -1165,6 +1216,7 @@ const EditBrandDevice = () => {
type="primary"
size="large"
onClick={handleSaveErrorCode}
+ loading={confirmLoading}
style={{
backgroundColor: '#23A55A',
borderColor: '#23A55A',
@@ -1197,13 +1249,23 @@ const EditBrandDevice = () => {
};
return (
-
-
-
-
-
- {renderStepContent()}
-
+
+
+
+
+
+
+ {renderStepContent()}
+
{currentStep === 1 && (
@@ -1220,7 +1282,7 @@ const EditBrandDevice = () => {
-
+
+
);
};
diff --git a/src/pages/master/brandDevice/component/FormActions.jsx b/src/pages/master/brandDevice/component/FormActions.jsx
deleted file mode 100644
index e3f8280..0000000
--- a/src/pages/master/brandDevice/component/FormActions.jsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import React from 'react';
-import { Button, ConfigProvider } from 'antd';
-import { ArrowLeftOutlined } from '@ant-design/icons';
-
-const FormActions = ({
- currentStep,
- onPreviousStep,
- onNextStep,
- onSave,
- onCancel,
- confirmLoading,
- isEditMode = false,
- showCancelButton = true
-}) => {
- return (
-
-
- {showCancelButton && (
-
- )}
- {currentStep > 0 && (
-
- )}
-
-
-
- {currentStep < 1 && (
-
- )}
- {currentStep === 1 && (
-
- )}
-
-
- );
-};
-
-export default FormActions;
\ No newline at end of file