diff --git a/src/pages/history/report/IndexReport.jsx b/src/pages/history/report/IndexReport.jsx index 4a26478..00a4149 100644 --- a/src/pages/history/report/IndexReport.jsx +++ b/src/pages/history/report/IndexReport.jsx @@ -8,54 +8,33 @@ import dayjs from 'dayjs'; const { Text } = Typography; -// Line Chart Data (same as IndexTrending) converted to table format -const lineChartData = [ +// New data structure for tag history +const tagHistoryData = [ { - id: 'Compressor Section 1', + tag: 'TEMP_SENSOR_1', color: '#FF6B4A', - data: [ - { x: 'Jan', y: 15 }, - { x: 'Feb', y: 18 }, - { x: 'Mar', y: 17 }, - { x: 'Apr', y: 24 }, - { x: 'Mei', y: 21 }, - { x: 'Jun', y: 19 }, - { x: 'Jul', y: 28 }, - { x: 'Agu', y: 26 }, - { x: 'Sep', y: 22 }, - { x: 'Okt', y: 25 }, + history: [ + { timestamp: '2025-10-09 08:00', value: 75 }, + { timestamp: '2025-10-09 08:05', value: 76 }, + { timestamp: '2025-10-09 08:10', value: 75 }, ], }, { - id: 'Compressor Section 2', + tag: 'GAS_LEAK_SENSOR_1', color: '#4ECDC4', - data: [ - { x: 'Jan', y: 12 }, - { x: 'Feb', y: 14 }, - { x: 'Mar', y: 19 }, - { x: 'Apr', y: 21 }, - { x: 'Mei', y: 22 }, - { x: 'Jun', y: 20 }, - { x: 'Jul', y: 19 }, - { x: 'Agu', y: 21 }, - { x: 'Sep', y: 18 }, - { x: 'Okt', y: 20 }, + history: [ + { timestamp: '2025-10-09 08:00', value: 10 }, + { timestamp: '2025-10-09 08:05', value: 150 }, + { timestamp: '2025-10-09 08:10', value: 12 }, ], }, { - id: 'Compressor Section 3', + tag: 'PRESSURE_SENSOR_1', color: '#FFE66D', - data: [ - { x: 'Jan', y: 8 }, - { x: 'Feb', y: 10 }, - { x: 'Mar', y: 12 }, - { x: 'Apr', y: 15 }, - { x: 'Mei', y: 18 }, - { x: 'Jun', y: 20 }, - { x: 'Jul', y: 22 }, - { x: 'Agu', y: 21 }, - { x: 'Sep', y: 19 }, - { x: 'Okt', y: 26 }, + history: [ + { timestamp: '2025-10-09 08:00', value: 1.2 }, + { timestamp: '2025-10-09 08:05', value: 1.3 }, + { timestamp: '2025-10-09 08:10', value: 1.2 }, ], }, ]; @@ -67,7 +46,7 @@ const IndexReport = memo(function IndexReport() { const [plantSubSection, setPlantSubSection] = useState('Semua Plant'); const [startDate, setStartDate] = useState(dayjs('2025-09-30')); const [endDate, setEndDate] = useState(dayjs('2025-10-09')); - const [periode, setPeriode] = useState('Bulanan'); + const [periode, setPeriode] = useState('30 Menit'); const [userRole, setUserRole] = useState(null); const [roleLevel, setRoleLevel] = useState(null); @@ -120,63 +99,53 @@ const IndexReport = memo(function IndexReport() { setPlantSubSection('Semua Plant'); setStartDate(dayjs('2025-09-30')); setEndDate(dayjs('2025-10-09')); - setPeriode('Bulanan'); + setPeriode('30 Menit'); }; // Check if user has permission to view data (all except guest) const canViewData = userRole && userRole !== 'guest'; - // Convert chart data to table format + // Convert tag history data to table format const convertToTableData = () => { - return lineChartData.map((section, index) => { - const rowData = { - key: index, - section: section.id, - color: section.color, - }; + const timestamps = {}; // Use an object to collect data per timestamp - // Add each month's data as a column - section.data.forEach((point) => { - rowData[point.x] = point.y; + tagHistoryData.forEach((tagData) => { + tagData.history.forEach((point) => { + if (!timestamps[point.timestamp]) { + timestamps[point.timestamp] = { + key: point.timestamp, + 'Date and Time': point.timestamp, + }; + } + timestamps[point.timestamp][tagData.tag] = point.value; }); - - return rowData; }); + + // Convert the object to an array + return Object.values(timestamps); }; const tableData = convertToTableData(); - // Create dynamic columns based on months - const months = lineChartData[0]?.data.map((point) => point.x) || []; + // Create dynamic columns based on tags + const tags = tagHistoryData.map((tagData) => tagData.tag); const columns = [ { - title: 'Plant Sub Section', - dataIndex: 'section', - key: 'section', + title: 'Date and Time', + dataIndex: 'Date and Time', + key: 'Date and Time', fixed: 'left', - width: 200, - render: (text, record) => ( -
-
- {text} -
- ), + width: 180, + render: (text) => {text}, }, - ...months.map((month) => ({ - title: month, - dataIndex: month, - key: month, + ...tags.map((tag) => ({ + title: tag, + dataIndex: tag, + key: tag, align: 'center', - width: 80, - render: (value) => {value}, + width: 150, + render: (value) => {value !== undefined ? value : '-'}, })), ]; @@ -242,9 +211,10 @@ const IndexReport = memo(function IndexReport() { onChange={setPeriode} style={{ width: '100%', marginTop: '4px' }} options={[ - { value: 'Harian', label: 'Harian' }, - { value: 'Mingguan', label: 'Mingguan' }, - { value: 'Bulanan', label: 'Bulanan' }, + { value: '5 Menit', label: '5 Menit' }, + { value: '10 Menit', label: '10 Menit' }, + { value: '30 Menit', label: '30 Menit' }, + { value: '1 Jam', label: '1 Jam' }, ]} />
@@ -272,9 +242,8 @@ const IndexReport = memo(function IndexReport() { - {/* Table Section */} - {!canViewData ? ( + {/* {!canViewData ? ( Anda tidak memiliki akses untuk melihat data report. @@ -282,22 +251,22 @@ const IndexReport = memo(function IndexReport() { Silakan hubungi administrator untuk mendapatkan akses. - ) : ( - -
- - ☰ Trending Error per Plant Sub Section - -
- - - )} + ) : ( */} + +
+ + ☰ History Report + +
+
+ + {/* )} */} ); diff --git a/src/pages/history/trending/IndexTrending.jsx b/src/pages/history/trending/IndexTrending.jsx index f61edd7..83e1cb0 100644 --- a/src/pages/history/trending/IndexTrending.jsx +++ b/src/pages/history/trending/IndexTrending.jsx @@ -17,7 +17,7 @@ const IndexTrending = memo(function IndexTrending() { const [plantSubSection, setPlantSubSection] = useState('Semua Plant'); const [startDate, setStartDate] = useState(dayjs('2025-09-30')); const [endDate, setEndDate] = useState(dayjs('2025-10-09')); - const [periode, setPeriode] = useState('Bulanan'); + const [periode, setPeriode] = useState('10 Menit'); const [userRole, setUserRole] = useState(null); const [roleLevel, setRoleLevel] = useState(null); @@ -66,79 +66,53 @@ const IndexTrending = memo(function IndexTrending() { } }, []); - const lineChartData = [ + const tagTrendingData = [ { - id: 'Compressor Section 1', + id: 'TEMP_SENSOR_1', color: '#FF6B4A', data: [ - { x: 'Jan', y: 15 }, - { x: 'Feb', y: 18 }, - { x: 'Mar', y: 17 }, - { x: 'Apr', y: 24 }, - { x: 'Mei', y: 21 }, - { x: 'Jun', y: 19 }, - { x: 'Jul', y: 28 }, - { x: 'Agu', y: 26 }, - { x: 'Sep', y: 22 }, - { x: 'Okt', y: 25 }, + { y: '08:00', x: 75 }, + { y: '08:05', x: 76 }, + { y: '08:10', x: 75 }, + { y: '08:15', x: 77 }, + { y: '08:20', x: 76 }, + { y: '08:25', x: 78 }, + { y: '08:30', x: 79 }, ], }, { - id: 'Compressor Section 2', + id: 'GAS_LEAK_SENSOR_1', color: '#4ECDC4', data: [ - { x: 'Jan', y: 12 }, - { x: 'Feb', y: 14 }, - { x: 'Mar', y: 19 }, - { x: 'Apr', y: 21 }, - { x: 'Mei', y: 22 }, - { x: 'Jun', y: 20 }, - { x: 'Jul', y: 19 }, - { x: 'Agu', y: 21 }, - { x: 'Sep', y: 18 }, - { x: 'Okt', y: 20 }, + { y: '08:00', x: 10 }, + { y: '08:05', x: 150 }, + { y: '08:10', x: 40 }, + { y: '08:15', x: 20 }, + { y: '08:20', x: 15 }, + { y: '08:25', x: 18 }, + { y: '08:30', x: 25 }, ], }, { - id: 'Compressor Section 3', + id: 'PRESSURE_SENSOR_1', color: '#FFE66D', data: [ - { x: 'Jan', y: 8 }, - { x: 'Feb', y: 10 }, - { x: 'Mar', y: 12 }, - { x: 'Apr', y: 15 }, - { x: 'Mei', y: 18 }, - { x: 'Jun', y: 20 }, - { x: 'Jul', y: 22 }, - { x: 'Agu', y: 21 }, - { x: 'Sep', y: 19 }, - { x: 'Okt', y: 26 }, + { y: '08:00', x: 1.2 }, + { y: '08:05', x: 1.3 }, + { y: '08:10', x: 1.2 }, + { y: '08:15', x: 1.4 }, + { y: '08:20', x: 1.5 }, + { y: '08:25', x: 1.3 }, + { y: '08:30', x: 1.2 }, ], }, ]; - const donutChartData = [ - { id: 'Section 1', label: 'Section 1', value: 35, color: '#FF6B4A' }, - { id: 'Section 2', label: 'Section 2', value: 25, color: '#4ECDC4' }, - { id: 'Section 3', label: 'Section 3', value: 15, color: '#FFE66D' }, - { id: 'Section 4', label: 'Section 4', value: 12, color: '#95E1D3' }, - { id: 'Section 5', label: 'Section 5', value: 8, color: '#F38BA0' }, - { id: 'Section 6', label: 'Section 6', value: 5, color: '#A78BFA' }, - ]; - - const barChartData = [ - { label: 'Overheat', value: 65 }, - { label: 'Low Pressure', value: 48 }, - { label: 'High Vibration', value: 42 }, - { label: 'Oil Leak', value: 35 }, - { label: 'Sensor Error', value: 28 }, - ]; - const handleReset = () => { setPlantSubSection('Semua Plant'); setStartDate(dayjs('2025-09-30')); setEndDate(dayjs('2025-10-09')); - setPeriode('Bulanan'); + setPeriode('10 Menit'); }; // Check if user has permission to view data (all except guest) @@ -204,9 +178,10 @@ const IndexTrending = memo(function IndexTrending() { onChange={setPeriode} style={{ width: '100%', marginTop: '4px' }} options={[ - { value: 'Harian', label: 'Harian' }, - { value: 'Mingguan', label: 'Mingguan' }, - { value: 'Bulanan', label: 'Bulanan' }, + { value: '5 Menit', label: '5 Menit' }, + { value: '10 Menit', label: '10 Menit' }, + { value: '30 Menit', label: '30 Menit' }, + { value: '1 Jam', label: '1 Jam' }, ]} /> @@ -234,9 +209,8 @@ const IndexTrending = memo(function IndexTrending() { - {/* Charts Section */} - {!canViewData ? ( + {/* {!canViewData ? ( Anda tidak memiliki akses untuk melihat data trending. @@ -244,64 +218,78 @@ const IndexTrending = memo(function IndexTrending() { Silakan hubungi administrator untuk mendapatkan akses. - ) : ( - <> - - {/* Line Chart */} - - -
- - ☰ Trending Error per Plant Sub Section - -
-
- -
-
- - - - )} + ) : ( */} + <> + + {/* Line Chart */} + + +
+ + ☰ Tag Value Trending + +
+
+ +
+
+ + + + {/* )} */} ); }); diff --git a/src/pages/master/device/component/DetailDevice.jsx b/src/pages/master/device/component/DetailDevice.jsx index ba9e858..b8fb6d3 100644 --- a/src/pages/master/device/component/DetailDevice.jsx +++ b/src/pages/master/device/component/DetailDevice.jsx @@ -26,7 +26,7 @@ const DetailDevice = (props) => { device_id: '', device_code: '', device_name: '', - device_status: true, + is_active: true, device_location: 'Building A', device_description: '', ip_address: '', @@ -139,7 +139,7 @@ const DetailDevice = (props) => { // Backend validation schema doesn't include device_code const payload = { device_name: FormData.device_name, - device_status: FormData.device_status, + is_active: FormData.is_active, device_location: FormData.device_location, ip_address: FormData.ip_address, }; @@ -211,11 +211,10 @@ const DetailDevice = (props) => { const isChecked = event; setFormData({ ...FormData, - device_status: isChecked ? true : false, + is_active: isChecked ? true : false, }); }; - useEffect(() => { const token = localStorage.getItem('token'); if (token) { @@ -312,16 +311,14 @@ const DetailDevice = (props) => { disabled={props.readOnly} style={{ backgroundColor: - FormData.device_status === true ? '#23A55A' : '#bfbfbf', + FormData.is_active === true ? '#23A55A' : '#bfbfbf', }} - checked={FormData.device_status === true} + checked={FormData.is_active === true} onChange={handleStatusToggle} />
- - {FormData.device_status === true ? 'Running' : 'Offline'} - + {FormData.is_active === true ? 'Running' : 'Offline'}
diff --git a/src/pages/master/tag/component/DetailTag.jsx b/src/pages/master/tag/component/DetailTag.jsx index 3863ae6..1e4154c 100644 --- a/src/pages/master/tag/component/DetailTag.jsx +++ b/src/pages/master/tag/component/DetailTag.jsx @@ -16,7 +16,7 @@ const DetailTag = (props) => { const defaultData = { tag_id: '', - tag_code: '', + tag_name: '', tag_number: '', data_type: '', @@ -24,8 +24,7 @@ const DetailTag = (props) => { is_active: true, is_alarm: false, device_id: null, - device_code: '', - device_name: '', + sub_section_id: null, }; @@ -118,6 +117,18 @@ const DetailTag = (props) => { return; } + // Validasi data type harus Diskrit atau Analog + const validDataTypes = ['Diskrit', 'Analog']; + if (!validDataTypes.includes(FormData.data_type)) { + NotifOk({ + icon: 'warning', + title: 'Peringatan', + message: `Data Type harus "Diskrit" atau "Analog". Nilai "${FormData.data_type}" tidak valid. Silakan pilih dari dropdown.`, + }); + setConfirmLoading(false); + return; + } + if (!FormData.unit || FormData.unit.trim() === '') { NotifOk({ icon: 'warning', @@ -141,17 +152,22 @@ const DetailTag = (props) => { // Prepare payload berdasarkan backend validation schema const payload = { - tag_name: FormData.tag_name, + tag_name: FormData.tag_name.trim(), tag_number: parseInt(FormData.tag_number), data_type: FormData.data_type, - unit: FormData.unit, + unit: FormData.unit.trim(), is_active: FormData.is_active, is_alarm: FormData.is_alarm, device_id: parseInt(FormData.device_id), - device_code: FormData.device_code, - device_name: FormData.device_name, }; + // Add sub_section_id only if it's selected + if (FormData.sub_section_id) { + payload.sub_section_id = parseInt(FormData.sub_section_id); + } + + // Debug logging + try { let response; @@ -185,6 +201,7 @@ const DetailTag = (props) => { } } catch (error) { console.error('Save Tag Error:', error); + console.error('Error details:', error); NotifAlert({ icon: 'error', title: 'Error', @@ -215,8 +232,6 @@ const DetailTag = (props) => { setFormData({ ...FormData, device_id: deviceId, - device_code: selectedDevice?.device_code || '', - device_name: selectedDevice?.device_name || '', }); }; diff --git a/src/pages/master/tag/component/ListTag.jsx b/src/pages/master/tag/component/ListTag.jsx index 270561e..2451e71 100644 --- a/src/pages/master/tag/component/ListTag.jsx +++ b/src/pages/master/tag/component/ListTag.jsx @@ -51,6 +51,13 @@ const columns = (showPreviewModal, showEditModal, showDeleteDialog) => [ key: 'unit', width: '8%', }, + { + title: 'Sub Section', + dataIndex: 'sub_section_name', + key: 'sub_section_name', + width: '12%', + render: (text) => text || '-', + }, { title: 'Device', dataIndex: 'device_name',