feat: convert line chart data to table format and enhance filtering options in IndexReport
This commit is contained in:
@@ -1,116 +1,305 @@
|
|||||||
import React, { memo, useEffect, useState } 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, Table } from 'antd';
|
import { Typography, Table, Card, Select, DatePicker, Button, Row, Col } from 'antd';
|
||||||
|
import { FileTextOutlined } from '@ant-design/icons';
|
||||||
|
import { decryptData } from '../../../components/Global/Formatter';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
// Mock Data
|
// Line Chart Data (same as IndexTrending) converted to table format
|
||||||
const initialData = [
|
const lineChartData = [
|
||||||
{
|
{
|
||||||
key: '1',
|
id: 'Compressor Section 1',
|
||||||
plantSubSection: 'Section A',
|
color: '#FF6B4A',
|
||||||
device: 'Device 1',
|
data: [
|
||||||
errorCode: 'E-101',
|
{ x: 'Jan', y: 15 },
|
||||||
status: 'Warning',
|
{ 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 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: '2',
|
id: 'Compressor Section 2',
|
||||||
plantSubSection: 'Section B',
|
color: '#4ECDC4',
|
||||||
device: 'Device 2',
|
data: [
|
||||||
errorCode: 'E-102',
|
{ x: 'Jan', y: 12 },
|
||||||
status: 'Alarm',
|
{ 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 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: '3',
|
id: 'Compressor Section 3',
|
||||||
plantSubSection: 'Section C',
|
color: '#FFE66D',
|
||||||
device: 'Device 3',
|
data: [
|
||||||
errorCode: 'E-103',
|
{ x: 'Jan', y: 8 },
|
||||||
status: 'Done',
|
{ x: 'Feb', y: 10 },
|
||||||
},
|
{ x: 'Mar', y: 12 },
|
||||||
{
|
{ x: 'Apr', y: 15 },
|
||||||
key: '4',
|
{ x: 'Mei', y: 18 },
|
||||||
plantSubSection: 'Section A',
|
{ x: 'Jun', y: 20 },
|
||||||
device: 'Device 4',
|
{ x: 'Jul', y: 22 },
|
||||||
errorCode: 'E-104',
|
{ x: 'Agu', y: 21 },
|
||||||
status: 'Warning',
|
{ x: 'Sep', y: 19 },
|
||||||
|
{ x: 'Okt', y: 26 },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const IndexReport = memo(function IndexReport() {
|
const IndexReport = memo(function IndexReport() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { setBreadcrumbItems } = useBreadcrumb();
|
const { setBreadcrumbItems } = useBreadcrumb();
|
||||||
const [data, setData] = useState(initialData);
|
|
||||||
|
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 [userRole, setUserRole] = useState(null);
|
||||||
|
const [roleLevel, setRoleLevel] = useState(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
if (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([
|
setBreadcrumbItems([
|
||||||
{ title: <Text strong style={{ fontSize: '14px' }}>• History</Text> },
|
{
|
||||||
{ title: <Text strong style={{ fontSize: '14px' }}>Report</Text> }
|
title: (
|
||||||
|
<Text strong style={{ fontSize: '14px' }}>
|
||||||
|
• History
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: (
|
||||||
|
<Text strong style={{ fontSize: '14px' }}>
|
||||||
|
Report
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
navigate('/signin');
|
navigate('/signin');
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getTitleStyle = (statusName) => {
|
const handleReset = () => {
|
||||||
let backgroundColor;
|
setPlantSubSection('Semua Plant');
|
||||||
switch (statusName.toLowerCase()) {
|
setStartDate(dayjs('2025-09-30'));
|
||||||
case 'done':
|
setEndDate(dayjs('2025-10-09'));
|
||||||
backgroundColor = '#52c41a'; // green
|
setPeriode('Bulanan');
|
||||||
break;
|
|
||||||
case 'warning':
|
|
||||||
backgroundColor = '#faad14'; // orange
|
|
||||||
break;
|
|
||||||
case 'alarm':
|
|
||||||
backgroundColor = '#f5222d'; // red
|
|
||||||
break;
|
|
||||||
case 'critical':
|
|
||||||
backgroundColor = '#000000'; // black
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
backgroundColor = 'transparent';
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
backgroundColor,
|
|
||||||
color: '#fff',
|
|
||||||
padding: '2px 8px',
|
|
||||||
borderRadius: '4px',
|
|
||||||
display: 'inline-block'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check if user has permission to view data (all except guest)
|
||||||
|
const canViewData = userRole && userRole !== 'guest';
|
||||||
|
|
||||||
|
// Convert chart data to table format
|
||||||
|
const convertToTableData = () => {
|
||||||
|
return lineChartData.map((section, index) => {
|
||||||
|
const rowData = {
|
||||||
|
key: index,
|
||||||
|
section: section.id,
|
||||||
|
color: section.color,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add each month's data as a column
|
||||||
|
section.data.forEach((point) => {
|
||||||
|
rowData[point.x] = point.y;
|
||||||
|
});
|
||||||
|
|
||||||
|
return rowData;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableData = convertToTableData();
|
||||||
|
|
||||||
|
// Create dynamic columns based on months
|
||||||
|
const months = lineChartData[0]?.data.map((point) => point.x) || [];
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: 'Plant Sub Section',
|
title: 'Plant Sub Section',
|
||||||
dataIndex: 'plantSubSection',
|
dataIndex: 'section',
|
||||||
key: 'plantSubSection',
|
key: 'section',
|
||||||
},
|
fixed: 'left',
|
||||||
{
|
width: 200,
|
||||||
title: 'Device',
|
render: (text, record) => (
|
||||||
dataIndex: 'device',
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||||
key: 'device',
|
<div
|
||||||
},
|
style={{
|
||||||
{
|
width: '12px',
|
||||||
title: 'Error Code',
|
height: '12px',
|
||||||
dataIndex: 'errorCode',
|
borderRadius: '50%',
|
||||||
key: 'errorCode',
|
backgroundColor: record.color,
|
||||||
},
|
}}
|
||||||
{
|
/>
|
||||||
title: 'Status',
|
<Text strong>{text}</Text>
|
||||||
dataIndex: 'status',
|
</div>
|
||||||
key: 'status',
|
|
||||||
render: (status) => (
|
|
||||||
<span style={getTitleStyle(status)}>{status}</span>
|
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
...months.map((month) => ({
|
||||||
|
title: month,
|
||||||
|
dataIndex: month,
|
||||||
|
key: month,
|
||||||
|
align: 'center',
|
||||||
|
width: 80,
|
||||||
|
render: (value) => <Text>{value}</Text>,
|
||||||
|
})),
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: 24, minHeight: 360 }}>
|
<React.Fragment>
|
||||||
<Table columns={columns} dataSource={data} />
|
<div style={{ minHeight: 360 }}>
|
||||||
|
{/* 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 />}
|
||||||
|
disabled={!canViewData}
|
||||||
|
>
|
||||||
|
Tampilkan
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
<Col>
|
||||||
|
<Button
|
||||||
|
onClick={handleReset}
|
||||||
|
style={{ backgroundColor: '#6c757d', color: 'white' }}
|
||||||
|
disabled={!canViewData}
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Table Section */}
|
||||||
|
{!canViewData ? (
|
||||||
|
<Card style={{ marginTop: '24px', textAlign: 'center', padding: '40px' }}>
|
||||||
|
<Text style={{ fontSize: '16px', color: '#999' }}>
|
||||||
|
Anda tidak memiliki akses untuk melihat data report.
|
||||||
|
<br />
|
||||||
|
Silakan hubungi administrator untuk mendapatkan akses.
|
||||||
|
</Text>
|
||||||
|
</Card>
|
||||||
|
) : (
|
||||||
|
<Card style={{ marginTop: '24px' }}>
|
||||||
|
<div style={{ marginBottom: '16px' }}>
|
||||||
|
<Text strong style={{ fontSize: '16px' }}>
|
||||||
|
☰ Trending Error per Plant Sub Section
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={tableData}
|
||||||
|
pagination={false}
|
||||||
|
scroll={{ x: 1000 }}
|
||||||
|
size="middle"
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import React, { memo, useEffect, useState } 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, Select, DatePicker, Button, Row, Col, Card, Statistic } from 'antd';
|
import { Typography, Select, DatePicker, Button, Row, Col, Card } from 'antd';
|
||||||
import { ResponsiveLine } from '@nivo/line';
|
import { ResponsiveLine } from '@nivo/line';
|
||||||
import { ResponsivePie } from '@nivo/pie';
|
import { FileTextOutlined } from '@ant-design/icons';
|
||||||
import { FileTextOutlined, ToolOutlined, CheckCircleOutlined, ClockCircleOutlined } from '@ant-design/icons';
|
|
||||||
import { decryptData } from '../../../components/Global/Formatter';
|
import { decryptData } from '../../../components/Global/Formatter';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import './trending.css';
|
import './trending.css';
|
||||||
@@ -47,8 +46,20 @@ const IndexTrending = memo(function IndexTrending() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setBreadcrumbItems([
|
setBreadcrumbItems([
|
||||||
{ title: <Text strong style={{ fontSize: '14px' }}>• History</Text> },
|
{
|
||||||
{ title: <Text strong style={{ fontSize: '14px' }}>Trending</Text> },
|
title: (
|
||||||
|
<Text strong style={{ fontSize: '14px' }}>
|
||||||
|
• History
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: (
|
||||||
|
<Text strong style={{ fontSize: '14px' }}>
|
||||||
|
Trending
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
navigate('/signin');
|
navigate('/signin');
|
||||||
@@ -138,16 +149,19 @@ const IndexTrending = memo(function IndexTrending() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className="trending-container">
|
|
||||||
{/* Filter Section */}
|
{/* Filter Section */}
|
||||||
<Card className="filter-card">
|
<Card className="filter-card">
|
||||||
<div className="filter-header">
|
<div className="filter-header">
|
||||||
<Text strong style={{ fontSize: '14px' }}>☰ Filter Data</Text>
|
<Text strong style={{ fontSize: '14px' }}>
|
||||||
|
☰ Filter Data
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
<Row gutter={16} style={{ marginTop: '16px' }}>
|
<Row gutter={16} style={{ marginTop: '16px' }}>
|
||||||
<Col xs={24} sm={12} md={6}>
|
<Col xs={24} sm={12} md={6}>
|
||||||
<div className="filter-item">
|
<div className="filter-item">
|
||||||
<Text style={{ fontSize: '12px', color: '#666' }}>Plant Sub Section</Text>
|
<Text style={{ fontSize: '12px', color: '#666' }}>
|
||||||
|
Plant Sub Section
|
||||||
|
</Text>
|
||||||
<Select
|
<Select
|
||||||
value={plantSubSection}
|
value={plantSubSection}
|
||||||
onChange={setPlantSubSection}
|
onChange={setPlantSubSection}
|
||||||
@@ -155,7 +169,7 @@ const IndexTrending = memo(function IndexTrending() {
|
|||||||
options={[
|
options={[
|
||||||
{ value: 'Semua Plant', label: 'Semua Plant' },
|
{ value: 'Semua Plant', label: 'Semua Plant' },
|
||||||
{ value: 'Plant 1', label: 'Plant 1' },
|
{ value: 'Plant 1', label: 'Plant 1' },
|
||||||
{ value: 'Plant 2', label: 'Plant 2' }
|
{ value: 'Plant 2', label: 'Plant 2' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -192,7 +206,7 @@ const IndexTrending = memo(function IndexTrending() {
|
|||||||
options={[
|
options={[
|
||||||
{ value: 'Harian', label: 'Harian' },
|
{ value: 'Harian', label: 'Harian' },
|
||||||
{ value: 'Mingguan', label: 'Mingguan' },
|
{ value: 'Mingguan', label: 'Mingguan' },
|
||||||
{ value: 'Bulanan', label: 'Bulanan' }
|
{ value: 'Bulanan', label: 'Bulanan' },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -221,7 +235,7 @@ const IndexTrending = memo(function IndexTrending() {
|
|||||||
</Row>
|
</Row>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Statistics Cards */}
|
{/* Charts Section */}
|
||||||
{!canViewData ? (
|
{!canViewData ? (
|
||||||
<Card style={{ marginTop: '24px', textAlign: 'center', padding: '40px' }}>
|
<Card style={{ marginTop: '24px', textAlign: 'center', padding: '40px' }}>
|
||||||
<Text style={{ fontSize: '16px', color: '#999' }}>
|
<Text style={{ fontSize: '16px', color: '#999' }}>
|
||||||
@@ -232,66 +246,16 @@ const IndexTrending = memo(function IndexTrending() {
|
|||||||
</Card>
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<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' }}>
|
<Row gutter={16} style={{ marginTop: '24px' }}>
|
||||||
{/* Line Chart */}
|
{/* Line Chart */}
|
||||||
<Col xs={24} lg={12}>
|
<Col xs={24}>
|
||||||
<Card className="chart-card">
|
<Card className="chart-card">
|
||||||
<div className="chart-header">
|
<div className="chart-header">
|
||||||
<Text strong style={{ fontSize: '14px' }}>☰ Trending Error per Plant Sub Section</Text>
|
<Text strong style={{ fontSize: '14px' }}>
|
||||||
|
☰ Trending Error per Plant Sub Section
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ height: '300px', marginTop: '16px' }}>
|
<div style={{ height: '500px', marginTop: '16px' }}>
|
||||||
<ResponsiveLine
|
<ResponsiveLine
|
||||||
data={lineChartData}
|
data={lineChartData}
|
||||||
margin={{ top: 20, right: 20, bottom: 50, left: 50 }}
|
margin={{ top: 20, right: 20, bottom: 50, left: 50 }}
|
||||||
@@ -301,12 +265,12 @@ const IndexTrending = memo(function IndexTrending() {
|
|||||||
axisBottom={{
|
axisBottom={{
|
||||||
tickSize: 5,
|
tickSize: 5,
|
||||||
tickPadding: 5,
|
tickPadding: 5,
|
||||||
tickRotation: 0
|
tickRotation: 0,
|
||||||
}}
|
}}
|
||||||
axisLeft={{
|
axisLeft={{
|
||||||
tickSize: 5,
|
tickSize: 5,
|
||||||
tickPadding: 5,
|
tickPadding: 5,
|
||||||
tickRotation: 0
|
tickRotation: 0,
|
||||||
}}
|
}}
|
||||||
colors={{ datum: 'color' }}
|
colors={{ datum: 'color' }}
|
||||||
pointSize={6}
|
pointSize={6}
|
||||||
@@ -328,110 +292,16 @@ const IndexTrending = memo(function IndexTrending() {
|
|||||||
itemHeight: 20,
|
itemHeight: 20,
|
||||||
itemOpacity: 0.75,
|
itemOpacity: 0.75,
|
||||||
symbolSize: 12,
|
symbolSize: 12,
|
||||||
symbolShape: 'circle'
|
symbolShape: 'circle',
|
||||||
}
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</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>
|
</Row>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user