Slicing menu history alarm and event alarm
This commit is contained in:
38
src/pages/report/trending/IndexTrending.jsx
Normal file
38
src/pages/report/trending/IndexTrending.jsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import React, { memo, useState, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
|
||||
import { Typography } from 'antd';
|
||||
import ReportTrending from './ReportTrending';
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
const IndexTrending = memo(function IndexTrending() {
|
||||
const navigate = useNavigate();
|
||||
const { setBreadcrumbItems } = useBreadcrumb();
|
||||
const [selectedData, setSelectedData] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
setBreadcrumbItems([
|
||||
{
|
||||
title: (
|
||||
<Text strong style={{ fontSize: '14px' }}>
|
||||
• Trending
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
navigate('/signin');
|
||||
}
|
||||
}, [navigate, setBreadcrumbItems]);
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ReportTrending/>
|
||||
</React.Fragment>
|
||||
);
|
||||
});
|
||||
|
||||
export default IndexTrending;
|
||||
222
src/pages/report/trending/ReportTrending.jsx
Normal file
222
src/pages/report/trending/ReportTrending.jsx
Normal file
@@ -0,0 +1,222 @@
|
||||
import React, { memo, useState, useEffect } from 'react';
|
||||
import { Button, Row, Col, Card, Input, DatePicker, Select, Typography } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { FileTextOutlined } from '@ant-design/icons';
|
||||
import { ResponsiveLine } from '@nivo/line';
|
||||
import './trending.css';
|
||||
|
||||
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 [trigerFilter, setTrigerFilter] = useState(false);
|
||||
|
||||
const defaultFilter = { search: '' };
|
||||
const [formDataFilter, setFormDataFilter] = useState(defaultFilter);
|
||||
|
||||
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('10 Menit');
|
||||
|
||||
const getAllReport = async (params) => {
|
||||
return {
|
||||
data: [],
|
||||
};
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setPlantSubSection('Semua Plant');
|
||||
setStartDate(dayjs('2025-09-30'));
|
||||
setEndDate(dayjs('2025-10-09'));
|
||||
setPeriode('10 Menit');
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Card>
|
||||
<Row>
|
||||
<Col xs={24}>
|
||||
<Row gutter={16} style={{ marginTop: '16px' }}>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<div className="filter-item">
|
||||
<Text style={{ fontSize: '12px', color: '#666' }}>
|
||||
Plant Sub Section
|
||||
</Text>
|
||||
<Select
|
||||
value={plantSubSection}
|
||||
onChange={setPlantSubSection}
|
||||
style={{ width: '100%', marginTop: '4px' }}
|
||||
options={[
|
||||
{ value: 'Semua Plant', label: 'Semua Plant' },
|
||||
{ value: 'Plant 1', label: 'Plant 1' },
|
||||
{ value: 'Plant 2', label: 'Plant 2' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<div className="filter-item">
|
||||
<Text style={{ fontSize: '12px', color: '#666' }}>
|
||||
Tanggal Mulai
|
||||
</Text>
|
||||
<DatePicker
|
||||
value={startDate}
|
||||
onChange={setStartDate}
|
||||
format="DD/MM/YYYY"
|
||||
style={{ width: '100%', marginTop: '4px' }}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<div className="filter-item">
|
||||
<Text style={{ fontSize: '12px', color: '#666' }}>
|
||||
Tanggal Akhir
|
||||
</Text>
|
||||
<DatePicker
|
||||
value={endDate}
|
||||
onChange={setEndDate}
|
||||
format="DD/MM/YYYY"
|
||||
style={{ width: '100%', marginTop: '4px' }}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<div className="filter-item">
|
||||
<Text style={{ fontSize: '12px', color: '#666' }}>Periode</Text>
|
||||
<Select
|
||||
value={periode}
|
||||
onChange={setPeriode}
|
||||
style={{ width: '100%', marginTop: '4px' }}
|
||||
options={[
|
||||
{ value: '5 Menit', label: '5 Menit' },
|
||||
{ value: '10 Menit', label: '10 Menit' },
|
||||
{ value: '30 Menit', label: '30 Menit' },
|
||||
{ value: '1 Jam', label: '1 Jam' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={8} style={{ marginTop: '16px' }}>
|
||||
<Col>
|
||||
<Button type="primary" danger icon={<FileTextOutlined />}>
|
||||
Tampilkan
|
||||
</Button>
|
||||
</Col>
|
||||
<Col>
|
||||
<Button
|
||||
onClick={handleReset}
|
||||
style={{ backgroundColor: '#6c757d', color: 'white' }}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
<Col xs={24} sm={24} md={24} lg={24} xl={24} style={{ marginTop: '16px' }}>
|
||||
<div style={{ height: '500px', marginTop: '16px' }}>
|
||||
<ResponsiveLine
|
||||
data={tagTrendingData}
|
||||
margin={{ top: 20, right: 20, bottom: 50, left: 60 }}
|
||||
xScale={{
|
||||
type: 'linear',
|
||||
min: 'auto',
|
||||
max: 'auto',
|
||||
stacked: false,
|
||||
reverse: false,
|
||||
}}
|
||||
yScale={{
|
||||
type: 'point',
|
||||
}}
|
||||
curve="natural"
|
||||
axisBottom={{
|
||||
tickSize: 5,
|
||||
tickPadding: 5,
|
||||
tickRotation: 0,
|
||||
legend: 'Value',
|
||||
legendOffset: 40,
|
||||
legendPosition: 'middle',
|
||||
}}
|
||||
axisLeft={{
|
||||
tickSize: 5,
|
||||
tickPadding: 5,
|
||||
tickRotation: 0,
|
||||
legend: 'Time',
|
||||
legendOffset: -45,
|
||||
legendPosition: 'middle',
|
||||
}}
|
||||
colors={{ datum: 'color' }}
|
||||
pointSize={6}
|
||||
pointColor={{ theme: 'background' }}
|
||||
pointBorderWidth={2}
|
||||
pointBorderColor={{ from: 'serieColor' }}
|
||||
pointLabelYOffset={-12}
|
||||
useMesh={true}
|
||||
legends={[
|
||||
{
|
||||
anchor: 'bottom-right',
|
||||
direction: 'column',
|
||||
justify: false,
|
||||
translateX: 100,
|
||||
translateY: 0,
|
||||
itemsSpacing: 2,
|
||||
itemDirection: 'left-to-right',
|
||||
itemWidth: 80,
|
||||
itemHeight: 20,
|
||||
itemOpacity: 0.75,
|
||||
symbolSize: 12,
|
||||
symbolShape: 'circle',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
</React.Fragment>
|
||||
);
|
||||
});
|
||||
|
||||
export default ReportTrending;
|
||||
118
src/pages/report/trending/trending.css
Normal file
118
src/pages/report/trending/trending.css
Normal file
@@ -0,0 +1,118 @@
|
||||
.trending-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Filter Card */
|
||||
.filter-card {
|
||||
margin-bottom: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.filter-header {
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Statistic Cards */
|
||||
.stat-card {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.stat-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.stat-card-red::before {
|
||||
background-color: #ff4d4f;
|
||||
}
|
||||
|
||||
.stat-card-orange::before {
|
||||
background-color: #ff9f43;
|
||||
}
|
||||
|
||||
.stat-card-green::before {
|
||||
background-color: #52c41a;
|
||||
}
|
||||
|
||||
.stat-card-blue::before {
|
||||
background-color: #1890ff;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.stat-card-red .stat-icon {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.stat-card-orange .stat-icon {
|
||||
color: #ff9f43;
|
||||
}
|
||||
|
||||
.stat-card-green .stat-icon {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.stat-card-blue .stat-icon {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* Chart Cards */
|
||||
.chart-card {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.trending-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom Ant Design overrides */
|
||||
.trending-container .ant-statistic-title {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.trending-container .ant-statistic-content {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.trending-container .ant-card-body {
|
||||
padding: 20px;
|
||||
}
|
||||
Reference in New Issue
Block a user