progress history report
This commit is contained in:
@@ -18,4 +18,37 @@ const getAllHistoryEvent = async (queryParams) => {
|
|||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { getAllHistoryAlarm, getAllHistoryEvent };
|
const getAllHistoryValueReport = async (queryParams) => {
|
||||||
|
const response = await SendRequest({
|
||||||
|
method: 'get',
|
||||||
|
prefix: `history/value-report?${queryParams.toString()}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAllHistoryValueReportPivot = async (queryParams) => {
|
||||||
|
const response = await SendRequest({
|
||||||
|
method: 'get',
|
||||||
|
prefix: `history/value-report-pivot?${queryParams.toString()}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAllHistoryValueTrendingPivot = async (queryParams) => {
|
||||||
|
const response = await SendRequest({
|
||||||
|
method: 'get',
|
||||||
|
prefix: `history/value-trending?${queryParams.toString()}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
getAllHistoryAlarm,
|
||||||
|
getAllHistoryEvent,
|
||||||
|
getAllHistoryValueReport,
|
||||||
|
getAllHistoryValueReportPivot,
|
||||||
|
getAllHistoryValueTrendingPivot,
|
||||||
|
};
|
||||||
|
|||||||
@@ -57,22 +57,28 @@ const CardList = ({
|
|||||||
}
|
}
|
||||||
style={getCardStyle(fieldColor ? item[fieldColor] : cardColor)}
|
style={getCardStyle(fieldColor ? item[fieldColor] : cardColor)}
|
||||||
actions={[
|
actions={[
|
||||||
<EyeOutlined
|
showPreviewModal && (
|
||||||
|
<EyeOutlined
|
||||||
style={{ color: '#1890ff' }}
|
style={{ color: '#1890ff' }}
|
||||||
key="preview"
|
key="preview"
|
||||||
onClick={() => showPreviewModal(item)}
|
onClick={() => showPreviewModal(item)}
|
||||||
/>,
|
/>
|
||||||
<EditOutlined
|
),
|
||||||
|
showEditModal && (
|
||||||
|
<EditOutlined
|
||||||
style={{ color: '#faad14' }}
|
style={{ color: '#faad14' }}
|
||||||
key="edit"
|
key="edit"
|
||||||
onClick={() => showEditModal(item)}
|
onClick={() => showEditModal(item)}
|
||||||
/>,
|
/>
|
||||||
<DeleteOutlined
|
),
|
||||||
|
showDeleteDialog && (
|
||||||
|
<DeleteOutlined
|
||||||
style={{ color: '#ff1818' }}
|
style={{ color: '#ff1818' }}
|
||||||
key="delete"
|
key="delete"
|
||||||
onClick={() => showDeleteDialog(item)}
|
onClick={() => showDeleteDialog(item)}
|
||||||
/>,
|
/>
|
||||||
]}
|
),
|
||||||
|
].filter(Boolean)} // <== Hapus elemen yang undefined
|
||||||
>
|
>
|
||||||
<div style={{ textAlign: 'left' }}>
|
<div style={{ textAlign: 'left' }}>
|
||||||
{column.map((itemCard, index) => (
|
{column.map((itemCard, index) => (
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { memo, useState, useEffect, useRef } from 'react';
|
import React, { memo, useState, useEffect, useRef } from 'react';
|
||||||
import { Table, Pagination, Row, Col, Card, Grid, Button, Typography, Tag, Segmented } from 'antd';
|
import { Table, Pagination, Row, Col, Card, Grid, Button, Typography, Tag, Segmented } from 'antd';
|
||||||
import { AppstoreOutlined, TableOutlined } from '@ant-design/icons';
|
import { MacCommandOutlined, TableOutlined } from '@ant-design/icons';
|
||||||
import CardList from './CardList';
|
import CardList from './CardList';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
@@ -18,6 +18,7 @@ const TableList = memo(function TableList({
|
|||||||
showDeleteDialog,
|
showDeleteDialog,
|
||||||
cardColor,
|
cardColor,
|
||||||
fieldColor,
|
fieldColor,
|
||||||
|
firstLoad = true,
|
||||||
}) {
|
}) {
|
||||||
const [gridLoading, setGridLoading] = useState(false);
|
const [gridLoading, setGridLoading] = useState(false);
|
||||||
|
|
||||||
@@ -30,12 +31,19 @@ const TableList = memo(function TableList({
|
|||||||
total_page: 1,
|
total_page: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [viewMode, setViewMode] = useState('list');
|
const [viewMode, setViewMode] = useState('table');
|
||||||
|
|
||||||
const { useBreakpoint } = Grid;
|
const { useBreakpoint } = Grid;
|
||||||
|
|
||||||
|
const [renderCount, setRenderCount] = useState(firstLoad ? 1 : 0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
filter(1, pagination.current_limit);
|
if (renderCount < 1) {
|
||||||
|
setRenderCount(renderCount + 1);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
filter(1, pagination.current_limit);
|
||||||
|
}
|
||||||
}, [triger]);
|
}, [triger]);
|
||||||
|
|
||||||
const filter = async (currentPage, pageSize) => {
|
const filter = async (currentPage, pageSize) => {
|
||||||
@@ -90,8 +98,8 @@ const TableList = memo(function TableList({
|
|||||||
<div>
|
<div>
|
||||||
<Segmented
|
<Segmented
|
||||||
options={[
|
options={[
|
||||||
{ value: 'card', icon: <AppstoreOutlined /> },
|
|
||||||
{ value: 'table', icon: <TableOutlined /> },
|
{ value: 'table', icon: <TableOutlined /> },
|
||||||
|
{ value: 'card', icon: <MacCommandOutlined /> },
|
||||||
]}
|
]}
|
||||||
value={viewMode}
|
value={viewMode}
|
||||||
onChange={setViewMode}
|
onChange={setViewMode}
|
||||||
@@ -116,6 +124,7 @@ const TableList = memo(function TableList({
|
|||||||
pagination={false}
|
pagination={false}
|
||||||
loading={gridLoading}
|
loading={gridLoading}
|
||||||
scroll={{ y: 520 }}
|
scroll={{ y: 520 }}
|
||||||
|
size="small"
|
||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { Button, Row, Col, Card, Input, DatePicker, Select, Typography } from 'a
|
|||||||
import TableList from '../../../../components/Global/TableList';
|
import TableList from '../../../../components/Global/TableList';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { FileTextOutlined } from '@ant-design/icons';
|
import { FileTextOutlined } from '@ant-design/icons';
|
||||||
|
import { getAllHistoryValueReport } from '../../../../api/history-value';
|
||||||
|
import { getAllPlantSection } from '../../../../api/master-plant-section';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
@@ -19,7 +21,7 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
title: 'Datetime',
|
title: 'Datetime',
|
||||||
dataIndex: 'datetime',
|
dataIndex: 'datetime',
|
||||||
key: 'datetime',
|
key: 'datetime',
|
||||||
width: '10%',
|
width: '15%',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Tag Name',
|
title: 'Tag Name',
|
||||||
@@ -32,38 +34,73 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
dataIndex: 'val',
|
dataIndex: 'val',
|
||||||
key: 'val',
|
key: 'val',
|
||||||
width: '10%',
|
width: '10%',
|
||||||
|
render: (_, record) => Number(record.val).toFixed(4),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Stat',
|
title: 'Stat',
|
||||||
dataIndex: 'stat',
|
dataIndex: 'status',
|
||||||
key: 'stat',
|
key: 'status',
|
||||||
width: '10%',
|
width: '10%',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const dateNow = dayjs();
|
||||||
|
const dateNowFormated = dateNow.format('YYYY-MM-DD');
|
||||||
|
|
||||||
const [trigerFilter, setTrigerFilter] = useState(false);
|
const [trigerFilter, setTrigerFilter] = useState(false);
|
||||||
|
|
||||||
const defaultFilter = { search: '' };
|
const defaultFilter = {
|
||||||
|
criteria: '',
|
||||||
|
plant_sub_section_id: 0,
|
||||||
|
from: dateNowFormated,
|
||||||
|
to: dateNowFormated,
|
||||||
|
interval: 10,
|
||||||
|
};
|
||||||
const [formDataFilter, setFormDataFilter] = useState(defaultFilter);
|
const [formDataFilter, setFormDataFilter] = useState(defaultFilter);
|
||||||
|
|
||||||
const [plantSubSection, setPlantSubSection] = useState('Semua Plant');
|
const [plantSubSection, setPlantSubSection] = useState(0);
|
||||||
const [startDate, setStartDate] = useState(dayjs('2025-09-30'));
|
const [plantSubSectionList, setPlantSubSectionList] = useState([]);
|
||||||
const [endDate, setEndDate] = useState(dayjs('2025-10-09'));
|
const [startDate, setStartDate] = useState(dateNow);
|
||||||
const [periode, setPeriode] = useState('10 Menit');
|
const [endDate, setEndDate] = useState(dateNow);
|
||||||
|
const [periode, setPeriode] = useState(5);
|
||||||
|
|
||||||
const getAllReport = async (params) => {
|
const handleSearch = () => {
|
||||||
return {
|
const formattedDateStart = startDate.format('YYYY-MM-DD');
|
||||||
data: [],
|
const formattedDateEnd = endDate.format('YYYY-MM-DD');
|
||||||
};
|
|
||||||
|
setFormDataFilter({
|
||||||
|
criteria: '',
|
||||||
|
plant_sub_section_id: plantSubSection,
|
||||||
|
from: formattedDateStart,
|
||||||
|
to: formattedDateEnd,
|
||||||
|
interval: periode,
|
||||||
|
});
|
||||||
|
setTrigerFilter((prev) => !prev);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
setPlantSubSection('Semua Plant');
|
setPlantSubSection(0);
|
||||||
setStartDate(dayjs('2025-09-30'));
|
setStartDate(dateNow);
|
||||||
setEndDate(dayjs('2025-10-09'));
|
setEndDate(dateNow);
|
||||||
setPeriode('10 Menit');
|
setPeriode(5);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPlantSubSection = async () => {
|
||||||
|
const params = new URLSearchParams({ page: 1 });
|
||||||
|
const response = await getAllPlantSection(params);
|
||||||
|
|
||||||
|
if (response && response.data) {
|
||||||
|
const activePlantSubSections = response.data.filter(
|
||||||
|
(section) => section.is_active === true
|
||||||
|
);
|
||||||
|
setPlantSubSectionList(activePlantSubSections);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getPlantSubSection();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Card>
|
<Card>
|
||||||
@@ -77,14 +114,21 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
</Text>
|
</Text>
|
||||||
<Select
|
<Select
|
||||||
value={plantSubSection}
|
value={plantSubSection}
|
||||||
onChange={setPlantSubSection}
|
onChange={(value) => setPlantSubSection(value)}
|
||||||
style={{ width: '100%', marginTop: '4px' }}
|
style={{ width: '100%', marginTop: '4px' }}
|
||||||
options={[
|
>
|
||||||
{ value: 'Semua Plant', label: 'Semua Plant' },
|
<Select.Option key={0} value={0}>
|
||||||
{ value: 'Plant 1', label: 'Plant 1' },
|
Pilih Plant Sub Section
|
||||||
{ value: 'Plant 2', label: 'Plant 2' },
|
</Select.Option>
|
||||||
]}
|
{plantSubSectionList.map((item) => (
|
||||||
/>
|
<Select.Option
|
||||||
|
key={item.plant_sub_section_id}
|
||||||
|
value={item.plant_sub_section_id}
|
||||||
|
>
|
||||||
|
{item.plant_sub_section_name}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={12} md={6}>
|
<Col xs={24} sm={12} md={6}>
|
||||||
@@ -95,7 +139,7 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
<DatePicker
|
<DatePicker
|
||||||
value={startDate}
|
value={startDate}
|
||||||
onChange={setStartDate}
|
onChange={setStartDate}
|
||||||
format="DD/MM/YYYY"
|
format="DD-MM-YYYY"
|
||||||
style={{ width: '100%', marginTop: '4px' }}
|
style={{ width: '100%', marginTop: '4px' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -108,7 +152,7 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
<DatePicker
|
<DatePicker
|
||||||
value={endDate}
|
value={endDate}
|
||||||
onChange={setEndDate}
|
onChange={setEndDate}
|
||||||
format="DD/MM/YYYY"
|
format="DD-MM-YYYY"
|
||||||
style={{ width: '100%', marginTop: '4px' }}
|
style={{ width: '100%', marginTop: '4px' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -121,18 +165,24 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
onChange={setPeriode}
|
onChange={setPeriode}
|
||||||
style={{ width: '100%', marginTop: '4px' }}
|
style={{ width: '100%', marginTop: '4px' }}
|
||||||
options={[
|
options={[
|
||||||
{ value: '5 Menit', label: '5 Menit' },
|
{ value: 5, label: '5 Minute' },
|
||||||
{ value: '10 Menit', label: '10 Menit' },
|
{ value: 10, label: '10 Minute' },
|
||||||
{ value: '30 Menit', label: '30 Menit' },
|
{ value: 30, label: '30 Minute' },
|
||||||
{ value: '1 Jam', label: '1 Jam' },
|
{ value: 60, label: '1 Hour' },
|
||||||
|
{ value: 120, label: '2 Hour' },
|
||||||
]}
|
]}
|
||||||
/>
|
></Select>
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row gutter={8} style={{ marginTop: '16px' }}>
|
<Row gutter={8} style={{ marginTop: '16px' }}>
|
||||||
<Col>
|
<Col>
|
||||||
<Button type="primary" danger icon={<FileTextOutlined />}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
danger
|
||||||
|
icon={<FileTextOutlined />}
|
||||||
|
onClick={handleSearch}
|
||||||
|
>
|
||||||
Tampilkan
|
Tampilkan
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -148,7 +198,8 @@ const ListReport = memo(function ListReport(props) {
|
|||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={24} md={24} lg={24} xl={24} style={{ marginTop: '16px' }}>
|
<Col xs={24} sm={24} md={24} lg={24} xl={24} style={{ marginTop: '16px' }}>
|
||||||
<TableList
|
<TableList
|
||||||
getData={getAllReport}
|
firstLoad={false}
|
||||||
|
getData={getAllHistoryValueReport}
|
||||||
queryParams={formDataFilter}
|
queryParams={formDataFilter}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
triger={trigerFilter}
|
triger={trigerFilter}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ const { Text } = Typography;
|
|||||||
const IndexTrending = memo(function IndexTrending() {
|
const IndexTrending = memo(function IndexTrending() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { setBreadcrumbItems } = useBreadcrumb();
|
const { setBreadcrumbItems } = useBreadcrumb();
|
||||||
const [selectedData, setSelectedData] = useState(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
|
|||||||
@@ -4,75 +4,94 @@ import dayjs from 'dayjs';
|
|||||||
import { FileTextOutlined } from '@ant-design/icons';
|
import { FileTextOutlined } from '@ant-design/icons';
|
||||||
import { ResponsiveLine } from '@nivo/line';
|
import { ResponsiveLine } from '@nivo/line';
|
||||||
import './trending.css';
|
import './trending.css';
|
||||||
|
import { getAllPlantSection } from '../../../api/master-plant-section';
|
||||||
|
import { getAllHistoryValueTrendingPivot } from '../../../api/history-value';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
const tagTrendingData = [
|
|
||||||
{
|
|
||||||
id: 'TEMP_SENSOR_1',
|
|
||||||
color: '#FF6B4A',
|
|
||||||
data: [
|
|
||||||
{ 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: 'GAS_LEAK_SENSOR_1',
|
|
||||||
color: '#4ECDC4',
|
|
||||||
data: [
|
|
||||||
{ 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: 'PRESSURE_SENSOR_1',
|
|
||||||
color: '#FFE66D',
|
|
||||||
data: [
|
|
||||||
{ 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 ReportTrending = memo(function ReportTrending(props) {
|
const ReportTrending = memo(function ReportTrending(props) {
|
||||||
const [trigerFilter, setTrigerFilter] = useState(false);
|
const dateNow = dayjs();
|
||||||
|
const dateNowFormated = dateNow.format('YYYY-MM-DD');
|
||||||
|
|
||||||
const defaultFilter = { search: '' };
|
const [plantSubSection, setPlantSubSection] = useState(0);
|
||||||
|
const [plantSubSectionList, setPlantSubSectionList] = useState([]);
|
||||||
|
const [startDate, setStartDate] = useState(dateNow);
|
||||||
|
const [endDate, setEndDate] = useState(dateNow);
|
||||||
|
const [periode, setPeriode] = useState(60);
|
||||||
|
|
||||||
|
const defaultFilter = {
|
||||||
|
criteria: '',
|
||||||
|
plant_sub_section_id: plantSubSection,
|
||||||
|
from: dateNowFormated,
|
||||||
|
to: dateNowFormated,
|
||||||
|
interval: periode,
|
||||||
|
};
|
||||||
const [formDataFilter, setFormDataFilter] = useState(defaultFilter);
|
const [formDataFilter, setFormDataFilter] = useState(defaultFilter);
|
||||||
|
|
||||||
const [plantSubSection, setPlantSubSection] = useState('Semua Plant');
|
const [trendingValue, setTrendingValue] = useState([]);
|
||||||
const [startDate, setStartDate] = useState(dayjs('2025-09-30'));
|
|
||||||
const [endDate, setEndDate] = useState(dayjs('2025-10-09'));
|
|
||||||
const [periode, setPeriode] = useState('10 Menit');
|
|
||||||
|
|
||||||
const getAllReport = async (params) => {
|
const handleSearch = async () => {
|
||||||
return {
|
const formattedDateStart = startDate.format('YYYY-MM-DD');
|
||||||
data: [],
|
const formattedDateEnd = endDate.format('YYYY-MM-DD');
|
||||||
|
|
||||||
|
const newFilter = {
|
||||||
|
criteria: '',
|
||||||
|
plant_sub_section_id: plantSubSection,
|
||||||
|
from: formattedDateStart,
|
||||||
|
to: formattedDateEnd,
|
||||||
|
interval: periode,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
setFormDataFilter(newFilter);
|
||||||
|
|
||||||
|
const param = new URLSearchParams(newFilter);
|
||||||
|
const response = await getAllHistoryValueTrendingPivot(param);
|
||||||
|
|
||||||
|
if (response?.data?.length > 0) {
|
||||||
|
// 🔹 Bersihkan dan format data agar aman untuk Nivo
|
||||||
|
const cleanedData = response.data.map((serie) => ({
|
||||||
|
id: serie.id ?? 'Unknown',
|
||||||
|
data: Array.isArray(serie.data)
|
||||||
|
? serie.data.map((d) => ({
|
||||||
|
x: d?.x ?? null,
|
||||||
|
y:
|
||||||
|
d?.y !== null && d?.y !== undefined
|
||||||
|
? Number(d.y).toFixed(4) // format 4 angka di belakang koma
|
||||||
|
: null,
|
||||||
|
}))
|
||||||
|
: [],
|
||||||
|
}));
|
||||||
|
|
||||||
|
// setTrendingValue(cleanedData);
|
||||||
|
} else {
|
||||||
|
// 🔹 Jika tidak ada data dari API
|
||||||
|
// setTrendingValue([]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
setPlantSubSection('Semua Plant');
|
setPlantSubSection(0);
|
||||||
setStartDate(dayjs('2025-09-30'));
|
setStartDate(dateNow);
|
||||||
setEndDate(dayjs('2025-10-09'));
|
setEndDate(dateNow);
|
||||||
setPeriode('10 Menit');
|
setPeriode(5);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getPlantSubSection = async () => {
|
||||||
|
const params = new URLSearchParams({ page: 1 });
|
||||||
|
const response = await getAllPlantSection(params);
|
||||||
|
|
||||||
|
if (response && response.data) {
|
||||||
|
const activePlantSubSections = response.data.filter(
|
||||||
|
(section) => section.is_active === true
|
||||||
|
);
|
||||||
|
setPlantSubSectionList(activePlantSubSections);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getPlantSubSection();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Card>
|
<Card>
|
||||||
@@ -86,14 +105,21 @@ const ReportTrending = memo(function ReportTrending(props) {
|
|||||||
</Text>
|
</Text>
|
||||||
<Select
|
<Select
|
||||||
value={plantSubSection}
|
value={plantSubSection}
|
||||||
onChange={setPlantSubSection}
|
onChange={(value) => setPlantSubSection(value)}
|
||||||
style={{ width: '100%', marginTop: '4px' }}
|
style={{ width: '100%', marginTop: '4px' }}
|
||||||
options={[
|
>
|
||||||
{ value: 'Semua Plant', label: 'Semua Plant' },
|
<Select.Option key={0} value={0}>
|
||||||
{ value: 'Plant 1', label: 'Plant 1' },
|
Pilih Plant Sub Section
|
||||||
{ value: 'Plant 2', label: 'Plant 2' },
|
</Select.Option>
|
||||||
]}
|
{plantSubSectionList.map((item) => (
|
||||||
/>
|
<Select.Option
|
||||||
|
key={item.plant_sub_section_id}
|
||||||
|
value={item.plant_sub_section_id}
|
||||||
|
>
|
||||||
|
{item.plant_sub_section_name}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={12} md={6}>
|
<Col xs={24} sm={12} md={6}>
|
||||||
@@ -104,7 +130,7 @@ const ReportTrending = memo(function ReportTrending(props) {
|
|||||||
<DatePicker
|
<DatePicker
|
||||||
value={startDate}
|
value={startDate}
|
||||||
onChange={setStartDate}
|
onChange={setStartDate}
|
||||||
format="DD/MM/YYYY"
|
format="DD-MM-YYYY"
|
||||||
style={{ width: '100%', marginTop: '4px' }}
|
style={{ width: '100%', marginTop: '4px' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,7 +143,7 @@ const ReportTrending = memo(function ReportTrending(props) {
|
|||||||
<DatePicker
|
<DatePicker
|
||||||
value={endDate}
|
value={endDate}
|
||||||
onChange={setEndDate}
|
onChange={setEndDate}
|
||||||
format="DD/MM/YYYY"
|
format="DD-MM-YYYY"
|
||||||
style={{ width: '100%', marginTop: '4px' }}
|
style={{ width: '100%', marginTop: '4px' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,18 +156,24 @@ const ReportTrending = memo(function ReportTrending(props) {
|
|||||||
onChange={setPeriode}
|
onChange={setPeriode}
|
||||||
style={{ width: '100%', marginTop: '4px' }}
|
style={{ width: '100%', marginTop: '4px' }}
|
||||||
options={[
|
options={[
|
||||||
{ value: '5 Menit', label: '5 Menit' },
|
{ value: 5, label: '5 Minute' },
|
||||||
{ value: '10 Menit', label: '10 Menit' },
|
{ value: 10, label: '10 Minute' },
|
||||||
{ value: '30 Menit', label: '30 Menit' },
|
{ value: 30, label: '30 Minute' },
|
||||||
{ value: '1 Jam', label: '1 Jam' },
|
{ value: 60, label: '1 Hour' },
|
||||||
|
{ value: 120, label: '2 Hour' },
|
||||||
]}
|
]}
|
||||||
/>
|
></Select>
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row gutter={8} style={{ marginTop: '16px' }}>
|
<Row gutter={8} style={{ marginTop: '16px' }}>
|
||||||
<Col>
|
<Col>
|
||||||
<Button type="primary" danger icon={<FileTextOutlined />}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
danger
|
||||||
|
icon={<FileTextOutlined />}
|
||||||
|
onClick={handleSearch}
|
||||||
|
>
|
||||||
Tampilkan
|
Tampilkan
|
||||||
</Button>
|
</Button>
|
||||||
</Col>
|
</Col>
|
||||||
@@ -157,60 +189,105 @@ const ReportTrending = memo(function ReportTrending(props) {
|
|||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} sm={24} md={24} lg={24} xl={24} style={{ marginTop: '16px' }}>
|
<Col xs={24} sm={24} md={24} lg={24} xl={24} style={{ marginTop: '16px' }}>
|
||||||
<div style={{ height: '500px', marginTop: '16px' }}>
|
<div style={{ height: '500px', marginTop: '16px' }}>
|
||||||
<ResponsiveLine
|
{trendingValue && trendingValue.length > 0 ? (
|
||||||
data={tagTrendingData}
|
<ResponsiveLine
|
||||||
margin={{ top: 20, right: 20, bottom: 50, left: 60 }}
|
data={trendingValue} // [{ id, data: [{x, y}] }]
|
||||||
xScale={{
|
// data={
|
||||||
type: 'linear',
|
// trendingValue && trendingValue.length
|
||||||
min: 'auto',
|
// ? trendingValue
|
||||||
max: 'auto',
|
// : [{ id, data: [{ x, y }] }]
|
||||||
stacked: false,
|
// }
|
||||||
reverse: false,
|
margin={{ top: 40, right: 100, bottom: 70, left: 70 }}
|
||||||
}}
|
xScale={{
|
||||||
yScale={{
|
type: 'time',
|
||||||
type: 'point',
|
format: '%Y-%m-%d %H:%M',
|
||||||
}}
|
useUTC: false,
|
||||||
curve="natural"
|
precision: 'minute',
|
||||||
axisBottom={{
|
}}
|
||||||
tickSize: 5,
|
xFormat="time:%Y-%m-%d %H:%M"
|
||||||
tickPadding: 5,
|
yScale={{
|
||||||
tickRotation: 0,
|
type: 'linear',
|
||||||
legend: 'Value',
|
min: 'auto',
|
||||||
legendOffset: 40,
|
max: 'auto',
|
||||||
legendPosition: 'middle',
|
stacked: false,
|
||||||
}}
|
reverse: false,
|
||||||
axisLeft={{
|
}}
|
||||||
tickSize: 5,
|
yFormat={(value) => Number(value).toFixed(4)} // ✅ format 4 angka di belakang koma
|
||||||
tickPadding: 5,
|
axisBottom={{
|
||||||
tickRotation: 0,
|
format: '%Y-%m-%d %H:%M', // ✅ tampilkan tanggal + jam
|
||||||
legend: 'Time',
|
tickValues: 'every 2 hours', // tampilkan setiap 2 jam (bisa ubah ke every 30 minutes)
|
||||||
legendOffset: -45,
|
tickSize: 5,
|
||||||
legendPosition: 'middle',
|
tickPadding: 5,
|
||||||
}}
|
tickRotation: -45,
|
||||||
colors={{ datum: 'color' }}
|
legend: 'Tanggal & Waktu',
|
||||||
pointSize={6}
|
legendOffset: 60,
|
||||||
pointColor={{ theme: 'background' }}
|
legendPosition: 'middle',
|
||||||
pointBorderWidth={2}
|
}}
|
||||||
pointBorderColor={{ from: 'serieColor' }}
|
axisLeft={{
|
||||||
pointLabelYOffset={-12}
|
tickSize: 5,
|
||||||
useMesh={true}
|
tickPadding: 5,
|
||||||
legends={[
|
tickRotation: 0,
|
||||||
{
|
legend: 'Nilai (Avg)',
|
||||||
anchor: 'bottom-right',
|
legendOffset: -60,
|
||||||
direction: 'column',
|
legendPosition: 'middle',
|
||||||
justify: false,
|
format: (value) => Number(value).toFixed(4), // ✅ tampilkan 4 angka di sumbu Y
|
||||||
translateX: 100,
|
}}
|
||||||
translateY: 0,
|
curve="monotoneX"
|
||||||
itemsSpacing: 2,
|
colors={{ scheme: 'category10' }}
|
||||||
itemDirection: 'left-to-right',
|
pointSize={6}
|
||||||
itemWidth: 80,
|
pointColor={{ theme: 'background' }}
|
||||||
itemHeight: 20,
|
pointBorderWidth={2}
|
||||||
itemOpacity: 0.75,
|
pointBorderColor={{ from: 'serieColor' }}
|
||||||
symbolSize: 12,
|
enablePointLabel={false}
|
||||||
symbolShape: 'circle',
|
enableGridX={true}
|
||||||
},
|
enableGridY={true}
|
||||||
]}
|
useMesh={true}
|
||||||
/>
|
tooltip={({ point }) => (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
background: 'white',
|
||||||
|
padding: '6px 9px',
|
||||||
|
border: '1px solid #ccc',
|
||||||
|
borderRadius: '6px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<strong>{point.serieId}</strong>
|
||||||
|
<br />
|
||||||
|
{point.data.xFormatted}
|
||||||
|
<br />
|
||||||
|
<span style={{ color: point.serieColor }}>
|
||||||
|
{Number(point.data.y).toFixed(4)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
legends={[
|
||||||
|
{
|
||||||
|
anchor: 'bottom-right',
|
||||||
|
direction: 'column',
|
||||||
|
justify: false,
|
||||||
|
translateX: 100,
|
||||||
|
translateY: 0,
|
||||||
|
itemsSpacing: 2,
|
||||||
|
itemDirection: 'left-to-right',
|
||||||
|
itemWidth: 120,
|
||||||
|
itemHeight: 20,
|
||||||
|
itemOpacity: 0.85,
|
||||||
|
symbolSize: 12,
|
||||||
|
symbolShape: 'circle',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: '40px',
|
||||||
|
color: '#999',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Tidak ada data untuk ditampilkan
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|||||||
Reference in New Issue
Block a user