diff --git a/src/components/Global/AppInput.jsx b/src/components/Global/AppInput.jsx new file mode 100644 index 0000000..620d0bd --- /dev/null +++ b/src/components/Global/AppInput.jsx @@ -0,0 +1,52 @@ +import { Input } from "antd"; +import { useEffect, useState, useRef } from "react"; + +const InputNumber = ({ + name, + value = 0, + onChange, + ...props +}) => { + const [inputValue, setInputValue] = useState( + value !== null && value !== undefined ? value.toString() : '' + ); + + useEffect(() => { + const newValue = value !== null && value !== undefined ? value.toString() : ''; + setInputValue(newValue); + }, [value]); + + const handleChange = (e) => { + const rawValue = e.target.value; + if (/^\d*$/.test(rawValue)) { + setInputValue(rawValue); + const numValue = rawValue === '' ? 0 : parseInt(rawValue, 10); + + // Kirim event object sintetis yang konsisten dengan handler biasa + onChange?.({ + target: { + name: name, + value: numValue + } + }); + } + }; + + const handleFocus = (e) => { + // Pilih semua teks saat fokus (opsional) + e.target.select(); + }; + + return ( + + ); +}; + +export { InputNumber }; diff --git a/src/pages/master/device/component/DetailDevice.jsx b/src/pages/master/device/component/DetailDevice.jsx index 3bd9c35..8ba0c7b 100644 --- a/src/pages/master/device/component/DetailDevice.jsx +++ b/src/pages/master/device/component/DetailDevice.jsx @@ -1,9 +1,21 @@ import React, { useEffect, useState } from 'react'; -import { Modal, Input, Divider, Typography, Switch, Button, ConfigProvider, Select } from 'antd'; +import { + Modal, + Input, + Divider, + Typography, + Switch, + Button, + ConfigProvider, + Select, + DatePicker, +} from 'antd'; import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif'; import { createDevice, updateDevice } from '../../../../api/master-device'; import { getAllBrands } from '../../../../api/master-brand'; import { validateRun } from '../../../../Utils/validate'; +import { toApiDateFormatter } from '../../../../components/Global/Formatter'; +import dayjs from 'dayjs'; const { Text } = Typography; const { TextArea } = Input; @@ -23,6 +35,7 @@ const DetailDevice = (props) => { device_location: '', device_description: '', ip_address: '', + reminder_at: null, listen_channel: '', }; @@ -67,8 +80,12 @@ const DetailDevice = (props) => { ip_address: formData.ip_address, brand_id: formData.brand_id, listen_channel: formData.listen_channel, + listen_channel_reminder: formData.listen_channel_reminder, + reminder_at: formData.reminder_at, }; + console.log(payload); + const response = formData.device_id ? await updateDevice(formData.device_id, payload) : await createDevice(payload); @@ -128,6 +145,13 @@ const DetailDevice = (props) => { }); }; + const handleDateChange = (field, value) => { + setFormData({ + ...formData, + [field]: value, + }); + }; + // Fungsi untuk mengambil daftar brand const fetchBrands = async () => { setLoadingBrands(true); @@ -169,8 +193,8 @@ const DetailDevice = (props) => { props.actionMode === 'add' ? 'Tambah' : props.actionMode === 'preview' - ? 'Preview' - : 'Edit' + ? 'Preview' + : 'Edit' } Device`} open={props.showModal} // open={true} @@ -218,9 +242,10 @@ const DetailDevice = (props) => { , ]} > + {formData && (
-
+
Device Status
@@ -247,7 +272,7 @@ const DetailDevice = (props) => {
- + {/* */} +
+
+
+
+ {' '} + Channel Error + +
+
+ Channel Reminder + +
+
- Listen Channel - + Reminder At +
+ + handleDateChange( + 'reminder_at', + date ? date.toISOString() : null + ) + } + format="DD-MM-YYYY HH:mm" + showTime={{ format: 'HH:mm' }} + style={{ flex: 1 }} + /> + + +
Device Description diff --git a/src/pages/master/sparepart/component/DetailSparepart.jsx b/src/pages/master/sparepart/component/DetailSparepart.jsx index 85482b6..f611c20 100644 --- a/src/pages/master/sparepart/component/DetailSparepart.jsx +++ b/src/pages/master/sparepart/component/DetailSparepart.jsx @@ -11,6 +11,7 @@ import { Row, Col, Image, + Checkbox, } from 'antd'; import { PlusOutlined, EyeOutlined, DeleteOutlined } from '@ant-design/icons'; import { NotifAlert, NotifOk } from '../../../../components/Global/ToastNotif'; @@ -37,9 +38,46 @@ const DetailSparepart = (props) => { const [previewTitle, setPreviewTitle] = useState(''); const [isHovering, setIsHovering] = useState(false); + const optionsYearly = [ + { + key: 1, + value: 1, + text: '1 Year', + }, + { + key: 2, + value: 2, + text: '2 Year', + }, + { + key: 3, + value: 3, + text: '3 Year', + }, + { + key: 4, + value: 4, + text: '4 Year', + }, + { + key: 5, + value: 5, + text: '5 Year', + }, + { + key: 6, + value: 6, + text: '6 Year', + }, + ]; + const [checkedList, setCheckedList] = useState([]); + const [checkAll, setCheckAll] = useState(false); + const [indeterminate, setIndeterminate] = useState(false); + const defaultData = { sparepart_id: '', sparepart_name: '', + sparepart_number: '', sparepart_description: '', sparepart_model: '', sparepart_item_type: null, @@ -236,6 +274,9 @@ const DetailSparepart = (props) => { payload.sparepart_foto = imageUrl; } + payload.sparepart_number = formData.sparepart_number; + payload.sparepart_yearly = checkedList; + // console.log('Sending payload:', payload); const response = formData.sparepart_id @@ -282,6 +323,21 @@ const DetailSparepart = (props) => { setFormData({ ...formData, [name]: value }); }; + const onChangeCheckbox = (list) => { + setCheckedList(list); + + setIndeterminate(!!list.length && list.length < optionsYearly.length); + setCheckAll(list.length === optionsYearly.length); + }; + + const onCheckAllChange = (e) => { + const checked = e.target.checked; + + setCheckedList(checked ? optionsYearly.map((item) => item.value) : []); + setIndeterminate(false); + setCheckAll(checked); + }; + useEffect(() => { if (props.selectedData) { setFormData(props.selectedData); @@ -318,9 +374,13 @@ const DetailSparepart = (props) => { } else { setFileList([]); } + setCheckedList(JSON.parse(props.selectedData.sparepart_yearly)); } else { setFormData(defaultData); setFileList([]); + setIndeterminate(false); + setCheckedList([]); + setCheckAll(false); } }, [props.showModal, props.selectedData, props.actionMode]); @@ -333,12 +393,13 @@ const DetailSparepart = (props) => { return ( {
{ {/* Kolom untuk field lainnya */} + + Part Number + * + + Sparepart Name * @@ -560,7 +632,44 @@ const DetailSparepart = (props) => { /> - + + + + + {checkAll ? 'Unselect All Years' : 'Select All Years'} + + + + + + + {optionsYearly.map((item) => ( + + {item.text} + + ))} + + + + Description diff --git a/src/pages/master/sparepart/component/ListSparepart.jsx b/src/pages/master/sparepart/component/ListSparepart.jsx index fc290be..800b663 100644 --- a/src/pages/master/sparepart/component/ListSparepart.jsx +++ b/src/pages/master/sparepart/component/ListSparepart.jsx @@ -36,6 +36,12 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [ key: 'sparepart_name', width: '20%', }, + { + title: 'Sparepart Number', + dataIndex: 'sparepart_number', + key: 'sparepart_number', + width: '20%', + }, { title: 'Description', dataIndex: 'sparepart_description', @@ -277,8 +283,8 @@ const ListSparepart = memo(function ListSparepart(props) { onStockUpdate={doFilter} onGetData={(data) => { if(data && data.length > 0) { - console.log('Sample sparepart data from API:', data[0]); - console.log('Available fields:', Object.keys(data[0] || {})); + // console.log('Sample sparepart data from API:', data[0]); + // console.log('Available fields:', Object.keys(data[0] || {})); } }} // Log untuk debugging field-field yang tersedia />