feat: implement field auto-incrementing code
This commit is contained in:
@@ -12,7 +12,7 @@ import {
|
|||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
|
import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
|
||||||
import { createApd, getJenisPermit, updateApd } from '../../../../api/master-apd';
|
import { createApd, getJenisPermit, updateApd } from '../../../../api/master-apd';
|
||||||
import { createDevice, updateDevice } from '../../../../api/master-device';
|
import { createDevice, updateDevice, getAllDevice } from '../../../../api/master-device';
|
||||||
import { Checkbox } from 'antd';
|
import { Checkbox } from 'antd';
|
||||||
const CheckboxGroup = Checkbox.Group;
|
const CheckboxGroup = Checkbox.Group;
|
||||||
|
|
||||||
@@ -33,6 +33,7 @@ const DetailDevice = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const [FormData, setFormData] = useState(defaultData);
|
const [FormData, setFormData] = useState(defaultData);
|
||||||
|
const [nextDeviceCode, setNextDeviceCode] = useState('Auto-fill');
|
||||||
|
|
||||||
const [jenisPermit, setJenisPermit] = useState([]);
|
const [jenisPermit, setJenisPermit] = useState([]);
|
||||||
const [checkedList, setCheckedList] = useState([]);
|
const [checkedList, setCheckedList] = useState([]);
|
||||||
@@ -215,14 +216,57 @@ const DetailDevice = (props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const generateNextDeviceCode = async () => {
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams({ limit: 10000 });
|
||||||
|
const response = await getAllDevice(params);
|
||||||
|
|
||||||
|
if (response && response.data && response.data.data) {
|
||||||
|
const devices = response.data.data;
|
||||||
|
|
||||||
|
if (devices.length === 0) {
|
||||||
|
setNextDeviceCode('DVC001');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract numeric part from device codes and find the maximum
|
||||||
|
const deviceNumbers = devices
|
||||||
|
.map((device) => {
|
||||||
|
const match = device.device_code?.match(/dvc(\d+)/i);
|
||||||
|
return match ? parseInt(match[1], 10) : 0;
|
||||||
|
})
|
||||||
|
.filter((num) => !isNaN(num));
|
||||||
|
|
||||||
|
const maxNumber = deviceNumbers.length > 0 ? Math.max(...deviceNumbers) : 0;
|
||||||
|
const nextNumber = maxNumber + 1;
|
||||||
|
|
||||||
|
// Format with leading zeros (DVC001, DVC002, etc.)
|
||||||
|
const nextCode = `DVC${String(nextNumber).padStart(3, '0')}`;
|
||||||
|
setNextDeviceCode(nextCode);
|
||||||
|
} else {
|
||||||
|
setNextDeviceCode('DVC001');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating next device code:', error);
|
||||||
|
setNextDeviceCode('Auto-fill');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
if (token) {
|
if (token) {
|
||||||
|
if (props.showModal) {
|
||||||
// Only call getDataJenisPermit if permitDefault is enabled
|
// Only call getDataJenisPermit if permitDefault is enabled
|
||||||
if (props.permitDefault) {
|
if (props.permitDefault) {
|
||||||
getDataJenisPermit();
|
getDataJenisPermit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate next device code only for add mode
|
||||||
|
if (props.actionMode === 'add' && !props.selectedData) {
|
||||||
|
generateNextDeviceCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (props.selectedData != null) {
|
if (props.selectedData != null) {
|
||||||
setFormData(props.selectedData);
|
setFormData(props.selectedData);
|
||||||
if (props.permitDefault && props.selectedData.jenis_permit_default_arr) {
|
if (props.permitDefault && props.selectedData.jenis_permit_default_arr) {
|
||||||
@@ -234,7 +278,7 @@ const DetailDevice = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
// navigate('/signin'); // Uncomment if useNavigate is imported
|
// navigate('/signin'); // Uncomment if useNavigate is imported
|
||||||
}
|
}
|
||||||
}, [props.showModal]);
|
}, [props.showModal, props.actionMode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -332,17 +376,21 @@ const DetailDevice = (props) => {
|
|||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* <div style={{ marginBottom: 12 }}>
|
{/* Device Code - Auto Increment & Read Only */}
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
<Text strong>Device Code</Text>
|
<Text strong>Device Code</Text>
|
||||||
<Text style={{ color: 'red' }}> *</Text>
|
|
||||||
<Input
|
<Input
|
||||||
name="device_code"
|
name="device_code"
|
||||||
value={FormData.device_code}
|
value={FormData.device_code || nextDeviceCode}
|
||||||
onChange={handleInputChange}
|
placeholder={nextDeviceCode}
|
||||||
placeholder="Enter Device Code"
|
disabled
|
||||||
readOnly={props.readOnly}
|
style={{
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
cursor: 'not-allowed',
|
||||||
|
color: FormData.device_code ? '#000000' : '#bfbfbf'
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div> */}
|
</div>
|
||||||
<div style={{ marginBottom: 12 }}>
|
<div style={{ marginBottom: 12 }}>
|
||||||
<Text strong>Device Name</Text>
|
<Text strong>Device Name</Text>
|
||||||
<Text style={{ color: 'red' }}> *</Text>
|
<Text style={{ color: 'red' }}> *</Text>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
Divider,
|
Divider,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
|
import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
|
||||||
import { createPlantSection, updatePlantSection } from '../../../../api/master-plant-section';
|
import { createPlantSection, updatePlantSection, getAllPlantSection } from '../../../../api/master-plant-section';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
@@ -24,6 +24,7 @@ const DetailPlantSection = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const [FormData, setFormData] = useState(defaultData);
|
const [FormData, setFormData] = useState(defaultData);
|
||||||
|
const [nextPlantSectionCode, setNextPlantSectionCode] = useState('Auto-fill');
|
||||||
|
|
||||||
const handleInputChange = (e) => {
|
const handleInputChange = (e) => {
|
||||||
const { name, value } = e.target;
|
const { name, value } = e.target;
|
||||||
@@ -103,14 +104,56 @@ const DetailPlantSection = (props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const generateNextPlantSectionCode = async () => {
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams({ limit: 10000 });
|
||||||
|
const response = await getAllPlantSection(params);
|
||||||
|
|
||||||
|
if (response && response.data && response.data.data) {
|
||||||
|
const sections = response.data.data;
|
||||||
|
|
||||||
|
if (sections.length === 0) {
|
||||||
|
setNextPlantSectionCode('SUB001');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract numeric part from plant section codes and find the maximum
|
||||||
|
const sectionNumbers = sections
|
||||||
|
.map((section) => {
|
||||||
|
const match = section.sub_section_code?.match(/sub(\d+)/i);
|
||||||
|
return match ? parseInt(match[1], 10) : 0;
|
||||||
|
})
|
||||||
|
.filter((num) => !isNaN(num));
|
||||||
|
|
||||||
|
const maxNumber = sectionNumbers.length > 0 ? Math.max(...sectionNumbers) : 0;
|
||||||
|
const nextNumber = maxNumber + 1;
|
||||||
|
|
||||||
|
// Format with leading zeros (SUB001, SUB002, etc.)
|
||||||
|
const nextCode = `SUB${String(nextNumber).padStart(3, '0')}`;
|
||||||
|
setNextPlantSectionCode(nextCode);
|
||||||
|
} else {
|
||||||
|
setNextPlantSectionCode('SUB001');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating next plant section code:', error);
|
||||||
|
setNextPlantSectionCode('Auto-fill');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (props.showModal) {
|
||||||
|
// Generate next plant section code only for add mode
|
||||||
|
if (props.actionMode === 'add' && !props.selectedData) {
|
||||||
|
generateNextPlantSectionCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (props.selectedData) {
|
if (props.selectedData) {
|
||||||
setFormData(props.selectedData);
|
setFormData(props.selectedData);
|
||||||
} else {
|
} else {
|
||||||
setFormData(defaultData);
|
setFormData(defaultData);
|
||||||
}
|
}
|
||||||
}, [props.showModal, props.selectedData]);
|
}, [props.showModal, props.selectedData, props.actionMode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -184,16 +227,21 @@ const DetailPlantSection = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
<Divider style={{ margin: '12px 0' }} />
|
<Divider style={{ margin: '12px 0' }} />
|
||||||
|
|
||||||
{props.actionMode !== 'add' && (
|
{/* Plant Section Code - Auto Increment & Read Only */}
|
||||||
<div style={{ marginBottom: 12 }}>
|
<div style={{ marginBottom: 12 }}>
|
||||||
<Text strong>Plant Section Code</Text>
|
<Text strong>Plant Section Code</Text>
|
||||||
<Input
|
<Input
|
||||||
name="sub_section_code"
|
name="sub_section_code"
|
||||||
value={FormData.sub_section_code}
|
value={FormData.sub_section_code || nextPlantSectionCode}
|
||||||
|
placeholder={nextPlantSectionCode}
|
||||||
disabled
|
disabled
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
cursor: 'not-allowed',
|
||||||
|
color: FormData.sub_section_code ? '#000000' : '#bfbfbf'
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
<div style={{ marginBottom: 12 }}>
|
<div style={{ marginBottom: 12 }}>
|
||||||
<Text strong>Plant Sub Section Name</Text>
|
<Text strong>Plant Sub Section Name</Text>
|
||||||
|
|||||||
@@ -26,12 +26,19 @@ const DetailTag = (props) => {
|
|||||||
unit: '',
|
unit: '',
|
||||||
is_active: true,
|
is_active: true,
|
||||||
is_alarm: false,
|
is_alarm: false,
|
||||||
|
is_report: false,
|
||||||
|
is_history: false,
|
||||||
|
lim_low_crash: '',
|
||||||
|
lim_low: '',
|
||||||
|
lim_high: '',
|
||||||
|
lim_high_crash: '',
|
||||||
device_id: null,
|
device_id: null,
|
||||||
|
|
||||||
sub_section_id: null,
|
sub_section_id: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const [FormData, setFormData] = useState(defaultData);
|
const [FormData, setFormData] = useState(defaultData);
|
||||||
|
const [nextTagCode, setNextTagCode] = useState('Auto-fill');
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
props.setSelectedData(null);
|
props.setSelectedData(null);
|
||||||
@@ -120,13 +127,13 @@ const DetailTag = (props) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validasi data type harus Diskrit atau Analog
|
// Validasi data type harus Discrete atau Analog
|
||||||
const validDataTypes = ['Diskrit', 'Analog'];
|
const validDataTypes = ['Discrete', 'Analog'];
|
||||||
if (!validDataTypes.includes(FormData.data_type)) {
|
if (!validDataTypes.includes(FormData.data_type)) {
|
||||||
NotifOk({
|
NotifOk({
|
||||||
icon: 'warning',
|
icon: 'warning',
|
||||||
title: 'Peringatan',
|
title: 'Peringatan',
|
||||||
message: `Data Type harus "Diskrit" atau "Analog". Nilai "${FormData.data_type}" tidak valid. Silakan pilih dari dropdown.`,
|
message: `Data Type harus "Discrete" atau "Analog". Nilai "${FormData.data_type}" tidak valid. Silakan pilih dari dropdown.`,
|
||||||
});
|
});
|
||||||
setConfirmLoading(false);
|
setConfirmLoading(false);
|
||||||
return;
|
return;
|
||||||
@@ -153,6 +160,17 @@ const DetailTag = (props) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Plant Sub Section validation
|
||||||
|
if (!FormData.sub_section_id) {
|
||||||
|
NotifOk({
|
||||||
|
icon: 'warning',
|
||||||
|
title: 'Peringatan',
|
||||||
|
message: 'Plant Sub Section harus dipilih',
|
||||||
|
});
|
||||||
|
setConfirmLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare payload berdasarkan backend validation schema
|
// Prepare payload berdasarkan backend validation schema
|
||||||
const payload = {
|
const payload = {
|
||||||
tag_name: FormData.tag_name.trim(),
|
tag_name: FormData.tag_name.trim(),
|
||||||
@@ -161,9 +179,25 @@ const DetailTag = (props) => {
|
|||||||
unit: FormData.unit.trim(),
|
unit: FormData.unit.trim(),
|
||||||
is_active: FormData.is_active,
|
is_active: FormData.is_active,
|
||||||
is_alarm: FormData.is_alarm,
|
is_alarm: FormData.is_alarm,
|
||||||
|
is_report: FormData.is_report,
|
||||||
|
is_history: FormData.is_history,
|
||||||
device_id: parseInt(FormData.device_id),
|
device_id: parseInt(FormData.device_id),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add limit fields only if they have values
|
||||||
|
if (FormData.lim_low_crash !== '' && FormData.lim_low_crash !== null) {
|
||||||
|
payload.lim_low_crash = parseFloat(FormData.lim_low_crash);
|
||||||
|
}
|
||||||
|
if (FormData.lim_low !== '' && FormData.lim_low !== null) {
|
||||||
|
payload.lim_low = parseFloat(FormData.lim_low);
|
||||||
|
}
|
||||||
|
if (FormData.lim_high !== '' && FormData.lim_high !== null) {
|
||||||
|
payload.lim_high = parseFloat(FormData.lim_high);
|
||||||
|
}
|
||||||
|
if (FormData.lim_high_crash !== '' && FormData.lim_high_crash !== null) {
|
||||||
|
payload.lim_high_crash = parseFloat(FormData.lim_high_crash);
|
||||||
|
}
|
||||||
|
|
||||||
// Add sub_section_id only if it's selected
|
// Add sub_section_id only if it's selected
|
||||||
if (FormData.sub_section_id) {
|
if (FormData.sub_section_id) {
|
||||||
payload.sub_section_id = parseInt(FormData.sub_section_id);
|
payload.sub_section_id = parseInt(FormData.sub_section_id);
|
||||||
@@ -184,12 +218,15 @@ const DetailTag = (props) => {
|
|||||||
|
|
||||||
// Check if response is successful
|
// Check if response is successful
|
||||||
if (response && (response.statusCode === 200 || response.statusCode === 201)) {
|
if (response && (response.statusCode === 200 || response.statusCode === 201)) {
|
||||||
|
// response.data is already an object (converted in master-tag.jsx API)
|
||||||
|
const tagCode = response.data?.tag_code || '';
|
||||||
|
const tagName = response.data?.tag_name || FormData.tag_name || '';
|
||||||
|
const tagDisplay = tagCode ? `${tagCode} - ${tagName}` : tagName;
|
||||||
|
|
||||||
NotifOk({
|
NotifOk({
|
||||||
icon: 'success',
|
icon: 'success',
|
||||||
title: 'Berhasil',
|
title: 'Berhasil',
|
||||||
message:
|
message: `Data Tag "${tagDisplay}" berhasil ${
|
||||||
response.message ||
|
|
||||||
`Data Tag "${response.data?.tag_name || FormData.tag_name}" berhasil ${
|
|
||||||
FormData.tag_id ? 'diubah' : 'ditambahkan'
|
FormData.tag_id ? 'diubah' : 'ditambahkan'
|
||||||
}.`,
|
}.`,
|
||||||
});
|
});
|
||||||
@@ -252,6 +289,20 @@ const DetailTag = (props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleReportToggle = (isChecked) => {
|
||||||
|
setFormData({
|
||||||
|
...FormData,
|
||||||
|
is_report: isChecked,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleHistoryToggle = (isChecked) => {
|
||||||
|
setFormData({
|
||||||
|
...FormData,
|
||||||
|
is_history: isChecked,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const loadDevices = async () => {
|
const loadDevices = async () => {
|
||||||
setLoadingDevices(true);
|
setLoadingDevices(true);
|
||||||
try {
|
try {
|
||||||
@@ -314,6 +365,42 @@ const DetailTag = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const generateNextTagCode = async () => {
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams({ limit: 10000 });
|
||||||
|
const response = await getAllTag(params);
|
||||||
|
|
||||||
|
if (response && response.data && response.data.data) {
|
||||||
|
const tags = response.data.data;
|
||||||
|
|
||||||
|
if (tags.length === 0) {
|
||||||
|
setNextTagCode('TAG001');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract numeric part from tag codes and find the maximum
|
||||||
|
const tagNumbers = tags
|
||||||
|
.map((tag) => {
|
||||||
|
const match = tag.tag_code?.match(/tag(\d+)/i);
|
||||||
|
return match ? parseInt(match[1], 10) : 0;
|
||||||
|
})
|
||||||
|
.filter((num) => !isNaN(num));
|
||||||
|
|
||||||
|
const maxNumber = tagNumbers.length > 0 ? Math.max(...tagNumbers) : 0;
|
||||||
|
const nextNumber = maxNumber + 1;
|
||||||
|
|
||||||
|
// Format with leading zeros (TAG001, TAG002, etc.)
|
||||||
|
const nextCode = `TAG${String(nextNumber).padStart(3, '0')}`;
|
||||||
|
setNextTagCode(nextCode);
|
||||||
|
} else {
|
||||||
|
setNextTagCode('TAG001');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating next tag code:', error);
|
||||||
|
setNextTagCode('Auto-fill');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
if (token) {
|
if (token) {
|
||||||
@@ -322,6 +409,11 @@ const DetailTag = (props) => {
|
|||||||
loadDevices();
|
loadDevices();
|
||||||
loadPlantSubSections();
|
loadPlantSubSections();
|
||||||
loadUnits();
|
loadUnits();
|
||||||
|
|
||||||
|
// Generate next tag code only for add mode
|
||||||
|
if (props.actionMode === 'add' && !props.selectedData) {
|
||||||
|
generateNextTagCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.selectedData != null) {
|
if (props.selectedData != null) {
|
||||||
@@ -335,6 +427,12 @@ const DetailTag = (props) => {
|
|||||||
unit: props.selectedData.unit || '',
|
unit: props.selectedData.unit || '',
|
||||||
is_active: props.selectedData.is_active ?? true,
|
is_active: props.selectedData.is_active ?? true,
|
||||||
is_alarm: props.selectedData.is_alarm ?? false,
|
is_alarm: props.selectedData.is_alarm ?? false,
|
||||||
|
is_report: props.selectedData.is_report ?? false,
|
||||||
|
is_history: props.selectedData.is_history ?? false,
|
||||||
|
lim_low_crash: props.selectedData.lim_low_crash ?? '',
|
||||||
|
lim_low: props.selectedData.lim_low ?? '',
|
||||||
|
lim_high: props.selectedData.lim_high ?? '',
|
||||||
|
lim_high_crash: props.selectedData.lim_high_crash ?? '',
|
||||||
device_id: props.selectedData.device_id || null,
|
device_id: props.selectedData.device_id || null,
|
||||||
device_code: props.selectedData.device_code || '',
|
device_code: props.selectedData.device_code || '',
|
||||||
device_name: props.selectedData.device_name || '',
|
device_name: props.selectedData.device_name || '',
|
||||||
@@ -347,7 +445,7 @@ const DetailTag = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
// navigate('/signin'); // Uncomment if useNavigate is imported
|
// navigate('/signin'); // Uncomment if useNavigate is imported
|
||||||
}
|
}
|
||||||
}, [props.showModal]);
|
}, [props.showModal, props.actionMode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -360,6 +458,7 @@ const DetailTag = (props) => {
|
|||||||
} Tag`}
|
} Tag`}
|
||||||
open={props.showModal}
|
open={props.showModal}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
|
width={800}
|
||||||
footer={[
|
footer={[
|
||||||
<>
|
<>
|
||||||
<ConfigProvider
|
<ConfigProvider
|
||||||
@@ -413,19 +512,6 @@ const DetailTag = (props) => {
|
|||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/* Tag Code hanya ditampilkan saat EDIT atau PREVIEW */}
|
|
||||||
{(props.actionMode === 'edit' || props.actionMode === 'preview') && (
|
|
||||||
<div style={{ marginBottom: 12 }}>
|
|
||||||
<Text strong>Tag Code</Text>
|
|
||||||
<Input
|
|
||||||
name="tag_code"
|
|
||||||
value={FormData.tag_code}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
placeholder="Auto Generate"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{/* Status dan Alarm dalam satu baris */}
|
{/* Status dan Alarm dalam satu baris */}
|
||||||
<div style={{ marginBottom: 12 }}>
|
<div style={{ marginBottom: 12 }}>
|
||||||
<div
|
<div
|
||||||
@@ -500,6 +586,94 @@ const DetailTag = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Report dan History dalam satu baris */}
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
gap: '16px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Report Toggle */}
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div>
|
||||||
|
<Text strong>Report</Text>
|
||||||
|
<Text style={{ color: 'red' }}> *</Text>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: '8px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ marginRight: '8px' }}>
|
||||||
|
<Switch
|
||||||
|
disabled={props.readOnly}
|
||||||
|
style={{
|
||||||
|
backgroundColor:
|
||||||
|
FormData.is_report === true
|
||||||
|
? '#23A55A'
|
||||||
|
: '#bfbfbf',
|
||||||
|
}}
|
||||||
|
checked={FormData.is_report === true}
|
||||||
|
onChange={handleReportToggle}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Text>{FormData.is_report === true ? 'Yes' : 'No'}</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* History Toggle */}
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<div>
|
||||||
|
<Text strong>History</Text>
|
||||||
|
<Text style={{ color: 'red' }}> *</Text>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: '8px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div style={{ marginRight: '8px' }}>
|
||||||
|
<Switch
|
||||||
|
disabled={props.readOnly}
|
||||||
|
style={{
|
||||||
|
backgroundColor:
|
||||||
|
FormData.is_history === true
|
||||||
|
? '#23A55A'
|
||||||
|
: '#bfbfbf',
|
||||||
|
}}
|
||||||
|
checked={FormData.is_history === true}
|
||||||
|
onChange={handleHistoryToggle}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Text>{FormData.is_history === true ? 'Yes' : 'No'}</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Tag Code - Auto Increment & Read Only */}
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<Text strong>Tag Code</Text>
|
||||||
|
<Input
|
||||||
|
name="tag_code"
|
||||||
|
value={FormData.tag_code || nextTagCode}
|
||||||
|
placeholder={nextTagCode}
|
||||||
|
disabled
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
cursor: 'not-allowed',
|
||||||
|
color: FormData.tag_code ? '#000000' : '#bfbfbf',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div style={{ marginBottom: 12 }}>
|
<div style={{ marginBottom: 12 }}>
|
||||||
<Text strong>Tag Number</Text>
|
<Text strong>Tag Number</Text>
|
||||||
<Text style={{ color: 'red' }}> *</Text>
|
<Text style={{ color: 'red' }}> *</Text>
|
||||||
@@ -532,7 +706,7 @@ const DetailTag = (props) => {
|
|||||||
onChange={(value) => handleSelectChange('data_type', value)}
|
onChange={(value) => handleSelectChange('data_type', value)}
|
||||||
disabled={props.readOnly}
|
disabled={props.readOnly}
|
||||||
>
|
>
|
||||||
<Select.Option value="Diskrit">Diskrit</Select.Option>
|
<Select.Option value="Discrete">Discrete</Select.Option>
|
||||||
<Select.Option value="Analog">Analog</Select.Option>
|
<Select.Option value="Analog">Analog</Select.Option>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
@@ -562,8 +736,58 @@ const DetailTag = (props) => {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Limit Fields */}
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<Text strong>Limit Low Crash</Text>
|
||||||
|
<Input
|
||||||
|
name="lim_low_crash"
|
||||||
|
value={FormData.lim_low_crash}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Enter Limit Low Crash"
|
||||||
|
readOnly={props.readOnly}
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<Text strong>Limit Low</Text>
|
||||||
|
<Input
|
||||||
|
name="lim_low"
|
||||||
|
value={FormData.lim_low}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Enter Limit Low"
|
||||||
|
readOnly={props.readOnly}
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<Text strong>Limit High</Text>
|
||||||
|
<Input
|
||||||
|
name="lim_high"
|
||||||
|
value={FormData.lim_high}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Enter Limit High"
|
||||||
|
readOnly={props.readOnly}
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginBottom: 12 }}>
|
||||||
|
<Text strong>Limit High Crash</Text>
|
||||||
|
<Input
|
||||||
|
name="lim_high_crash"
|
||||||
|
value={FormData.lim_high_crash}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
placeholder="Enter Limit High Crash"
|
||||||
|
readOnly={props.readOnly}
|
||||||
|
type="number"
|
||||||
|
step="any"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div style={{ marginBottom: 12 }}>
|
<div style={{ marginBottom: 12 }}>
|
||||||
<Text strong>Plant Sub Section</Text>
|
<Text strong>Plant Sub Section</Text>
|
||||||
|
<Text style={{ color: 'red' }}> *</Text>
|
||||||
<Select
|
<Select
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
placeholder="Select Plant Sub Section"
|
placeholder="Select Plant Sub Section"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Modal, Input, Typography, Button, ConfigProvider, Switch } from 'antd';
|
import { Modal, Input, Typography, Button, ConfigProvider, Switch } from 'antd';
|
||||||
import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
|
import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif';
|
||||||
import { createUnit, updateUnit } from '../../../../api/master-unit';
|
import { createUnit, updateUnit, getAllUnit } from '../../../../api/master-unit';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@ const DetailUnit = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const [FormData, setFormData] = useState(defaultData);
|
const [FormData, setFormData] = useState(defaultData);
|
||||||
|
const [nextUnitCode, setNextUnitCode] = useState('Auto-fill');
|
||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
props.setSelectedData(null);
|
props.setSelectedData(null);
|
||||||
@@ -117,9 +118,52 @@ const DetailUnit = (props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const generateNextUnitCode = async () => {
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams({ limit: 10000 });
|
||||||
|
const response = await getAllUnit(params);
|
||||||
|
|
||||||
|
if (response && response.data && response.data.data) {
|
||||||
|
const units = response.data.data;
|
||||||
|
|
||||||
|
if (units.length === 0) {
|
||||||
|
setNextUnitCode('UNT001');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract numeric part from unit codes and find the maximum
|
||||||
|
const unitNumbers = units
|
||||||
|
.map((unit) => {
|
||||||
|
const match = unit.unit_code?.match(/unt(\d+)/i);
|
||||||
|
return match ? parseInt(match[1], 10) : 0;
|
||||||
|
})
|
||||||
|
.filter((num) => !isNaN(num));
|
||||||
|
|
||||||
|
const maxNumber = unitNumbers.length > 0 ? Math.max(...unitNumbers) : 0;
|
||||||
|
const nextNumber = maxNumber + 1;
|
||||||
|
|
||||||
|
// Format with leading zeros (UNT001, UNT002, etc.)
|
||||||
|
const nextCode = `UNT${String(nextNumber).padStart(3, '0')}`;
|
||||||
|
setNextUnitCode(nextCode);
|
||||||
|
} else {
|
||||||
|
setNextUnitCode('UNT001');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating next unit code:', error);
|
||||||
|
setNextUnitCode('Auto-fill');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
if (token) {
|
if (token) {
|
||||||
|
if (props.showModal) {
|
||||||
|
// Generate next unit code only for add mode
|
||||||
|
if (props.actionMode === 'add' && !props.selectedData) {
|
||||||
|
generateNextUnitCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (props.selectedData != null) {
|
if (props.selectedData != null) {
|
||||||
// Only set fields that are in defaultData
|
// Only set fields that are in defaultData
|
||||||
const filteredData = {
|
const filteredData = {
|
||||||
@@ -133,7 +177,7 @@ const DetailUnit = (props) => {
|
|||||||
setFormData(defaultData);
|
setFormData(defaultData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [props.showModal]);
|
}, [props.showModal, props.actionMode]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@@ -222,17 +266,21 @@ const DetailUnit = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Unit Code - Display only for edit/preview */}
|
{/* Unit Code - Auto Increment & Read Only */}
|
||||||
{FormData.unit_code && (
|
|
||||||
<div style={{ marginBottom: 12 }}>
|
<div style={{ marginBottom: 12 }}>
|
||||||
<Text strong>Unit Code</Text>
|
<Text strong>Unit Code</Text>
|
||||||
<Input
|
<Input
|
||||||
name="unit_code"
|
name="unit_code"
|
||||||
value={FormData.unit_code}
|
value={FormData.unit_code || nextUnitCode}
|
||||||
|
placeholder={nextUnitCode}
|
||||||
disabled
|
disabled
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#f5f5f5',
|
||||||
|
cursor: 'not-allowed',
|
||||||
|
color: FormData.unit_code ? '#000000' : '#bfbfbf'
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
<div style={{ marginBottom: 12 }}>
|
<div style={{ marginBottom: 12 }}>
|
||||||
<Text strong>Name</Text>
|
<Text strong>Name</Text>
|
||||||
<Text style={{ color: 'red' }}> *</Text>
|
<Text style={{ color: 'red' }}> *</Text>
|
||||||
|
|||||||
Reference in New Issue
Block a user