298 lines
12 KiB
JavaScript
298 lines
12 KiB
JavaScript
import React, { memo, useEffect, useState } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
|
|
import { Typography, Select, DatePicker, Button, Row, Col, Card } from 'antd';
|
|
import { ResponsiveLine } from '@nivo/line';
|
|
import { FileTextOutlined } from '@ant-design/icons';
|
|
import { decryptData } from '../../../components/Global/Formatter';
|
|
import dayjs from 'dayjs';
|
|
import './trending.css';
|
|
|
|
const { Text } = Typography;
|
|
|
|
const IndexTrending = memo(function IndexTrending() {
|
|
const navigate = useNavigate();
|
|
const { setBreadcrumbItems } = useBreadcrumb();
|
|
|
|
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 [userRole, setUserRole] = useState(null);
|
|
const [roleLevel, setRoleLevel] = useState(null);
|
|
|
|
useEffect(() => {
|
|
const token = localStorage.getItem('token');
|
|
if (token) {
|
|
// Get user data and role
|
|
let userData = null;
|
|
const sessionData = localStorage.getItem('session');
|
|
if (sessionData) {
|
|
userData = decryptData(sessionData);
|
|
} else {
|
|
const userRaw = localStorage.getItem('user');
|
|
if (userRaw) {
|
|
try {
|
|
userData = { user: JSON.parse(userRaw) };
|
|
} catch (e) {
|
|
console.error('Error parsing user data:', e);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (userData?.user) {
|
|
setUserRole(userData.user.role_name);
|
|
setRoleLevel(userData.user.role_level);
|
|
}
|
|
|
|
setBreadcrumbItems([
|
|
{
|
|
title: (
|
|
<Text strong style={{ fontSize: '14px' }}>
|
|
• History
|
|
</Text>
|
|
),
|
|
},
|
|
{
|
|
title: (
|
|
<Text strong style={{ fontSize: '14px' }}>
|
|
Trending
|
|
</Text>
|
|
),
|
|
},
|
|
]);
|
|
} else {
|
|
navigate('/signin');
|
|
}
|
|
}, []);
|
|
|
|
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 handleReset = () => {
|
|
setPlantSubSection('Semua Plant');
|
|
setStartDate(dayjs('2025-09-30'));
|
|
setEndDate(dayjs('2025-10-09'));
|
|
setPeriode('10 Menit');
|
|
};
|
|
|
|
// Check if user has permission to view data (all except guest)
|
|
const canViewData = userRole && userRole !== 'guest';
|
|
|
|
// Check if user can export/filter (administrator, engineer)
|
|
const canExportData = userRole && (userRole === 'administrator' || userRole === 'engineer');
|
|
|
|
return (
|
|
<React.Fragment>
|
|
{/* Filter Section */}
|
|
<Card className="filter-card">
|
|
<div className="filter-header">
|
|
<Text strong style={{ fontSize: '14px' }}>
|
|
☰ Filter Data
|
|
</Text>
|
|
</div>
|
|
<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 />}
|
|
disabled={!canViewData}
|
|
>
|
|
Tampilkan
|
|
</Button>
|
|
</Col>
|
|
<Col>
|
|
<Button
|
|
onClick={handleReset}
|
|
style={{ backgroundColor: '#6c757d', color: 'white' }}
|
|
disabled={!canViewData}
|
|
>
|
|
Reset
|
|
</Button>
|
|
</Col>
|
|
</Row>
|
|
</Card>
|
|
{/* Charts Section */}
|
|
{/* {!canViewData ? (
|
|
<Card style={{ marginTop: '24px', textAlign: 'center', padding: '40px' }}>
|
|
<Text style={{ fontSize: '16px', color: '#999' }}>
|
|
Anda tidak memiliki akses untuk melihat data trending.
|
|
<br />
|
|
Silakan hubungi administrator untuk mendapatkan akses.
|
|
</Text>
|
|
</Card>
|
|
) : ( */}
|
|
<>
|
|
<Row gutter={16} style={{ marginTop: '24px' }}>
|
|
{/* Line Chart */}
|
|
<Col xs={24}>
|
|
<Card className="chart-card">
|
|
<div className="chart-header">
|
|
<Text strong style={{ fontSize: '14px' }}>
|
|
☰ Tag Value Trending
|
|
</Text>
|
|
</div>
|
|
<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>
|
|
</Card>
|
|
</Col>
|
|
</Row>
|
|
</>
|
|
{/* )} */}
|
|
</React.Fragment>
|
|
);
|
|
});
|
|
|
|
export default IndexTrending;
|