update history trending

This commit is contained in:
2025-10-10 10:58:45 +07:00
parent 76cf2de6ed
commit 7d10c3f5b2
2 changed files with 480 additions and 5 deletions

View File

@@ -1,7 +1,12 @@
import React, { memo, useEffect } from 'react'; import React, { memo, useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb'; import { useBreadcrumb } from '../../../layout/LayoutBreadcrumb';
import { Typography } from 'antd'; import { Typography, Select, DatePicker, Button, Row, Col, Card, Statistic } from 'antd';
import { ResponsiveLine } from '@nivo/line';
import { ResponsivePie } from '@nivo/pie';
import { FileTextOutlined, ToolOutlined, CheckCircleOutlined, ClockCircleOutlined } from '@ant-design/icons';
import dayjs from 'dayjs';
import './trending.css';
const { Text } = Typography; const { Text } = Typography;
@@ -9,22 +14,374 @@ const IndexTrending = memo(function IndexTrending() {
const navigate = useNavigate(); const navigate = useNavigate();
const { setBreadcrumbItems } = useBreadcrumb(); 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('Bulanan');
useEffect(() => { useEffect(() => {
const token = localStorage.getItem('token'); const token = localStorage.getItem('token');
if (token) { if (token) {
setBreadcrumbItems([ setBreadcrumbItems([
{ title: <Text strong style={{ fontSize: '14px' }}> History</Text> }, { title: <Text strong style={{ fontSize: '14px' }}> History</Text> },
{ title: <Text strong style={{ fontSize: '14px' }}>Trending</Text> } { title: <Text strong style={{ fontSize: '14px' }}>Trending</Text> },
]); ]);
} else { } else {
navigate('/signin'); navigate('/signin');
} }
}, []); }, []);
const lineChartData = [
{
id: 'Compressor Section 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 },
],
},
{
id: 'Compressor Section 2',
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 },
],
},
{
id: 'Compressor Section 3',
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 },
],
},
];
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');
};
return ( return (
<div> <React.Fragment>
<h1>Trending Page</h1> <div className="trending-container">
{/* Filter Section */}
<Card className="filter-card">
<div className="filter-header">
<Text strong style={{ fontSize: '14px' }}> Filter Data</Text>
</div> </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: 'Harian', label: 'Harian' },
{ value: 'Mingguan', label: 'Mingguan' },
{ value: 'Bulanan', label: 'Bulanan' }
]}
/>
</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>
</Card>
{/* Statistics Cards */}
<Row gutter={16} style={{ marginTop: '24px' }}>
<Col xs={24} sm={12} md={6}>
<Card className="stat-card stat-card-red">
<div className="stat-icon">
<FileTextOutlined style={{ fontSize: '24px' }} />
</div>
<Statistic
title={<Text style={{ fontSize: '12px', color: '#666' }}>Total Error</Text>}
value={245}
valueStyle={{ fontSize: '24px', fontWeight: 'bold', color: '#333' }}
/>
</Card>
</Col>
<Col xs={24} sm={12} md={6}>
<Card className="stat-card stat-card-orange">
<div className="stat-icon">
<ToolOutlined style={{ fontSize: '24px' }} />
</div>
<Statistic
title={<Text style={{ fontSize: '12px', color: '#666' }}>Sedang Maintenance</Text>}
value={12}
valueStyle={{ fontSize: '24px', fontWeight: 'bold', color: '#333' }}
/>
</Card>
</Col>
<Col xs={24} sm={12} md={6}>
<Card className="stat-card stat-card-green">
<div className="stat-icon">
<CheckCircleOutlined style={{ fontSize: '24px' }} />
</div>
<Statistic
title={<Text style={{ fontSize: '12px', color: '#666' }}>Selesai Diperbaiki</Text>}
value={233}
valueStyle={{ fontSize: '24px', fontWeight: 'bold', color: '#333' }}
/>
</Card>
</Col>
<Col xs={24} sm={12} md={6}>
<Card className="stat-card stat-card-blue">
<div className="stat-icon">
<ClockCircleOutlined style={{ fontSize: '24px' }} />
</div>
<Statistic
title={<Text style={{ fontSize: '12px', color: '#666' }}>Rata-rata Waktu Perbaikan</Text>}
value="2.5 jam"
valueStyle={{ fontSize: '24px', fontWeight: 'bold', color: '#333' }}
/>
</Card>
</Col>
</Row>
{/* Charts Section */}
<Row gutter={16} style={{ marginTop: '24px' }}>
{/* Line Chart */}
<Col xs={24} lg={12}>
<Card className="chart-card">
<div className="chart-header">
<Text strong style={{ fontSize: '14px' }}> Trending Error per Plant Sub Section</Text>
</div>
<div style={{ height: '300px', marginTop: '16px' }}>
<ResponsiveLine
data={lineChartData}
margin={{ top: 20, right: 20, bottom: 50, left: 50 }}
xScale={{ type: 'point' }}
yScale={{ type: 'linear', min: 0, max: 35 }}
curve="natural"
axisBottom={{
tickSize: 5,
tickPadding: 5,
tickRotation: 0
}}
axisLeft={{
tickSize: 5,
tickPadding: 5,
tickRotation: 0
}}
colors={{ datum: 'color' }}
pointSize={6}
pointColor={{ theme: 'background' }}
pointBorderWidth={2}
pointBorderColor={{ from: 'serieColor' }}
pointLabelYOffset={-12}
useMesh={true}
legends={[
{
anchor: 'bottom',
direction: 'row',
justify: false,
translateX: 0,
translateY: 50,
itemsSpacing: 10,
itemDirection: 'left-to-right',
itemWidth: 140,
itemHeight: 20,
itemOpacity: 0.75,
symbolSize: 12,
symbolShape: 'circle'
}
]}
/>
</div>
</Card>
</Col>
{/* Donut Chart */}
<Col xs={24} lg={12}>
<Card className="chart-card">
<div className="chart-header">
<Text strong style={{ fontSize: '14px' }}> Distribusi Error per Plant</Text>
</div>
<div style={{ height: '300px', marginTop: '16px' }}>
<ResponsivePie
data={donutChartData}
margin={{ top: 20, right: 80, bottom: 80, left: 80 }}
innerRadius={0.6}
padAngle={1}
cornerRadius={3}
activeOuterRadiusOffset={8}
colors={{ datum: 'data.color' }}
borderWidth={1}
borderColor={{ from: 'color', modifiers: [['darker', 0.2]] }}
arcLinkLabelsSkipAngle={10}
arcLinkLabelsTextColor="#333333"
arcLinkLabelsThickness={2}
arcLinkLabelsColor={{ from: 'color' }}
arcLabelsSkipAngle={10}
arcLabelsTextColor={{ from: 'color', modifiers: [['darker', 2]] }}
legends={[
{
anchor: 'bottom',
direction: 'row',
justify: false,
translateX: 0,
translateY: 56,
itemsSpacing: 8,
itemWidth: 80,
itemHeight: 18,
itemTextColor: '#999',
itemDirection: 'left-to-right',
itemOpacity: 1,
symbolSize: 12,
symbolShape: 'circle'
}
]}
/>
</div>
</Card>
</Col>
</Row>
{/* Bar Chart and Bottom Section */}
<Row gutter={16} style={{ marginTop: '24px' }}>
{/* Bar Chart */}
<Col xs={24} lg={12}>
<Card className="chart-card">
<div className="chart-header">
<Text strong style={{ fontSize: '14px' }}> Jenis Error Terbanyak</Text>
</div>
<div style={{ marginTop: '24px', padding: '0 20px' }}>
{barChartData.map((item, index) => (
<div key={index} style={{ marginBottom: '20px' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>
<Text style={{ fontSize: '12px' }}>{item.label}</Text>
<Text strong style={{ fontSize: '12px' }}>{item.value}</Text>
</div>
<div style={{
width: '100%',
height: '24px',
backgroundColor: '#f0f0f0',
borderRadius: '4px',
overflow: 'hidden'
}}>
<div style={{
width: `${(item.value / 70) * 100}%`,
height: '100%',
backgroundColor: '#FF6B4A',
transition: 'width 0.3s ease'
}} />
</div>
</div>
))}
</div>
</Card>
</Col>
{/* Perbandingan Error Section */}
<Col xs={24} lg={12}>
<Card className="chart-card">
<div className="chart-header">
<Text strong style={{ fontSize: '14px' }}> Perbandingan Error Bulan Ini vs Bulan Lalu</Text>
</div>
<div style={{ marginTop: '24px', padding: '20px', textAlign: 'center' }}>
<Text style={{ color: '#999' }}>Data akan ditampilkan di sini</Text>
</div>
</Card>
</Col>
</Row>
</div>
</React.Fragment>
); );
}); });

View 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;
}