Muhammad Sulaiman Yusuf
2 years ago
19 changed files with 260 additions and 2163 deletions
@ -0,0 +1,20 @@ |
|||||||
|
import React, { useEffect, useState } from 'react'; |
||||||
|
import axios from 'axios' |
||||||
|
|
||||||
|
const DashboardPMO = () => { |
||||||
|
const token = localStorage.getItem("token") |
||||||
|
const HEADER = { |
||||||
|
headers: { |
||||||
|
"Content-Type": "application/json", |
||||||
|
"Authorization": `Bearer ${token}` |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<div></div> |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default DashboardPMO; |
@ -1,128 +0,0 @@ |
|||||||
.number-asset { |
|
||||||
font-size: 50px; |
|
||||||
font-weight: bold; |
|
||||||
text-align: center; |
|
||||||
} |
|
||||||
|
|
||||||
.text-bold { |
|
||||||
font-weight: bold; |
|
||||||
} |
|
||||||
|
|
||||||
.view-rectangle { |
|
||||||
display: flex; |
|
||||||
flex-direction: row; |
|
||||||
width: 100%; |
|
||||||
justify-content: space-between; |
|
||||||
} |
|
||||||
|
|
||||||
.view1 { |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
margin-bottom: -20px; |
|
||||||
|
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
background-color: #008B8B; |
|
||||||
} |
|
||||||
|
|
||||||
.view2 { |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
|
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
background-color: #8B008B; |
|
||||||
} |
|
||||||
|
|
||||||
.view3 { |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
|
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
background-color: #7CFC00; |
|
||||||
} |
|
||||||
|
|
||||||
.view4 { |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
|
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
background-color: #FF0000; |
|
||||||
} |
|
||||||
|
|
||||||
.view5 { |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
|
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
background-color: #4682B4; |
|
||||||
} |
|
||||||
|
|
||||||
.number-style { |
|
||||||
font-size: 30px; |
|
||||||
color: black; |
|
||||||
font-weight: bold; |
|
||||||
margin-bottom:15px; |
|
||||||
} |
|
||||||
|
|
||||||
.number-style1 { |
|
||||||
font-size: 30px; |
|
||||||
color: #FFFFFF; |
|
||||||
font-weight: bold; |
|
||||||
margin-bottom:15px; |
|
||||||
} |
|
||||||
|
|
||||||
.daily-info-card { |
|
||||||
min-width: 130px; |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
margin: 4px; |
|
||||||
border-radius: 8px; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
.dashboard-container { |
|
||||||
overflow-x: auto; |
|
||||||
} |
|
||||||
|
|
||||||
.maptable-window-button-container { |
|
||||||
float: right; |
|
||||||
right: 0px; |
|
||||||
} |
|
||||||
|
|
||||||
.maptable-close, .maptable-maximize, .maptable-minimize { |
|
||||||
cursor: pointer; |
|
||||||
padding: 4px; |
|
||||||
} |
|
||||||
|
|
||||||
.maptable-header { |
|
||||||
margin-bottom: -10px; |
|
||||||
} |
|
||||||
|
|
||||||
.maptable-title { |
|
||||||
font-size: 24px; |
|
||||||
font-weight: 700; |
|
||||||
text-align: left; |
|
||||||
margin-left: 20px; |
|
||||||
} |
|
||||||
|
|
||||||
.maptable-close:hover, .maptable-maximize:hover, .maptable-minimize:hover { |
|
||||||
color: #20a8d8; |
|
||||||
} |
|
@ -1,41 +0,0 @@ |
|||||||
import React, { useState, useEffect } from 'react'; |
|
||||||
import ReactDOM from 'react-dom'; |
|
||||||
import { Pie } from '@ant-design/plots'; |
|
||||||
|
|
||||||
const PieChart = () => { |
|
||||||
const data = [ |
|
||||||
{ |
|
||||||
type: 'FTTH', |
|
||||||
value: 27, |
|
||||||
}, |
|
||||||
{ |
|
||||||
type: 'Konstruksi', |
|
||||||
value: 25, |
|
||||||
}, |
|
||||||
]; |
|
||||||
const config = { |
|
||||||
appendPadding: 10, |
|
||||||
data, |
|
||||||
angleField: 'value', |
|
||||||
colorField: 'type', |
|
||||||
radius: 0.9, |
|
||||||
label: { |
|
||||||
type: 'inner', |
|
||||||
offset: '-30%', |
|
||||||
content: ({ percent }) => `${(percent * 100).toFixed(0)}%`, |
|
||||||
style: { |
|
||||||
fontSize: 14, |
|
||||||
textAlign: 'center', |
|
||||||
}, |
|
||||||
}, |
|
||||||
interactions: [ |
|
||||||
{ |
|
||||||
type: 'element-active', |
|
||||||
}, |
|
||||||
], |
|
||||||
}; |
|
||||||
|
|
||||||
return <Pie {...config} />; |
|
||||||
}; |
|
||||||
|
|
||||||
export default PieChart; |
|
@ -1,106 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
import { Doughnut, Bar } from 'react-chartjs-2'; |
|
||||||
import faker from '@faker-js/faker'; |
|
||||||
|
|
||||||
export const optionsDoughnut = { |
|
||||||
title: { |
|
||||||
display: true, |
|
||||||
text: 'PROJECT BY TYPE' |
|
||||||
}, |
|
||||||
}; |
|
||||||
|
|
||||||
export const data = { |
|
||||||
labels: ['Manned guarding ', 'C&T', 'CMS ', 'ESS', 'RSO'], |
|
||||||
datasets: [ |
|
||||||
{ |
|
||||||
label: '# of Votes', |
|
||||||
data: [12, 19, 3, 5, 2], |
|
||||||
backgroundColor: [ |
|
||||||
'rgba(255, 99, 132, 0.2)', |
|
||||||
'rgba(54, 162, 235, 0.2)', |
|
||||||
'rgba(255, 206, 86, 0.2)', |
|
||||||
'rgba(75, 192, 192, 0.2)', |
|
||||||
'rgba(153, 102, 255, 0.2)', |
|
||||||
// 'rgba(255, 159, 64, 0.2)',
|
|
||||||
], |
|
||||||
borderColor: [ |
|
||||||
'rgba(255, 99, 132, 1)', |
|
||||||
'rgba(54, 162, 235, 1)', |
|
||||||
'rgba(255, 206, 86, 1)', |
|
||||||
'rgba(75, 192, 192, 1)', |
|
||||||
'rgba(153, 102, 255, 1)', |
|
||||||
// 'rgba(255, 159, 64, 1)',
|
|
||||||
], |
|
||||||
borderWidth: 1, |
|
||||||
}, |
|
||||||
], |
|
||||||
}; |
|
||||||
|
|
||||||
export const DoughnutChart = () => { |
|
||||||
return <Doughnut data={data} options={optionsDoughnut} />; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
export const options = { |
|
||||||
title: { |
|
||||||
display: true, |
|
||||||
text: 'PROJECT BY GOVERNANCE PHASE' |
|
||||||
}, |
|
||||||
legend: { |
|
||||||
position: 'right', |
|
||||||
}, |
|
||||||
responsive: true, |
|
||||||
scales: { |
|
||||||
yAxes: [{ |
|
||||||
display: true, |
|
||||||
|
|
||||||
// position: 'right',
|
|
||||||
stacked: true, |
|
||||||
}], |
|
||||||
xAxes: [{ |
|
||||||
display: true, |
|
||||||
// position: 'right',
|
|
||||||
stacked: true, |
|
||||||
}], |
|
||||||
}, |
|
||||||
}; |
|
||||||
|
|
||||||
const labels = ['']; |
|
||||||
|
|
||||||
export const dataBar = { |
|
||||||
labels, |
|
||||||
datasets: [ |
|
||||||
{ |
|
||||||
indexAxis: 'y', |
|
||||||
label: 'Initiation', |
|
||||||
data: labels.map(() => faker.datatype.number({ min: 0, max: 10 })), |
|
||||||
backgroundColor: 'rgb(255, 99, 132)', |
|
||||||
}, |
|
||||||
{ |
|
||||||
indexAxis: 'y', |
|
||||||
label: 'Execution', |
|
||||||
data: labels.map(() => faker.datatype.number({ min: 0, max: 10 })), |
|
||||||
backgroundColor: 'rgb(75, 192, 192)', |
|
||||||
}, |
|
||||||
{ |
|
||||||
indexAxis: 'y', |
|
||||||
label: 'Closing', |
|
||||||
data: labels.map(() => faker.datatype.number({ min: 0, max: 10 })), |
|
||||||
backgroundColor: 'rgb(53, 162, 235)', |
|
||||||
}, |
|
||||||
// {
|
|
||||||
// indexAxis: 'y',
|
|
||||||
// label: 'Excecution',
|
|
||||||
// data: labels.map(() => faker.datatype.number({ min: 0, max: 10 })),
|
|
||||||
// backgroundColor: 'rgba(255, 159, 64, 1)',
|
|
||||||
// },
|
|
||||||
], |
|
||||||
}; |
|
||||||
|
|
||||||
export function BarChart() { |
|
||||||
return <Bar indexAxis='y' options={options} data={dataBar} />; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,130 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
import { Table, Tag, Space } from 'antd'; |
|
||||||
|
|
||||||
const columns = [ |
|
||||||
{ |
|
||||||
title: 'Project Name', |
|
||||||
dataIndex: 'project', |
|
||||||
key: 'project', |
|
||||||
fixed: 'left', |
|
||||||
width: 200, |
|
||||||
// render: text => <a>{text}</a>,
|
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Project Owner', |
|
||||||
dataIndex: 'owner', |
|
||||||
key: 'owner', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Start Date', |
|
||||||
dataIndex: 'startDate', |
|
||||||
key: 'startDate', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'End Date', |
|
||||||
dataIndex: 'endDate', |
|
||||||
key: 'endDate', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Cost', |
|
||||||
dataIndex: 'cost', |
|
||||||
key: 'cost', |
|
||||||
render: text => `Rp. ${text.toLocaleString()}`, |
|
||||||
align: 'right' |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Cost Health', |
|
||||||
dataIndex: 'costHealth', |
|
||||||
key: 'costHealth', |
|
||||||
render: text => |
|
||||||
text === 'good' ? <div className='health-status-good' /> : |
|
||||||
text === 'danger' ? <div className='health-status-danger' /> : |
|
||||||
text === 'warning' ? <div className='health-status-warning' /> : <div className='health-status-default' /> |
|
||||||
, |
|
||||||
align: 'center', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Work Health', |
|
||||||
dataIndex: 'workHealth', |
|
||||||
key: 'workHealth', |
|
||||||
render: text => |
|
||||||
text === 'good' ? <div className='health-status-good' /> : |
|
||||||
text === 'danger' ? <div className='health-status-danger' /> : |
|
||||||
text === 'warning' ? <div className='health-status-warning' /> : <div className='health-status-default' />, |
|
||||||
align: 'center', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Schedule Health', |
|
||||||
dataIndex: 'scheduleHealth', |
|
||||||
key: 'scheduleHealth', |
|
||||||
render: text => |
|
||||||
text === 'good' ? <div className='health-status-good' /> : |
|
||||||
text === 'danger' ? <div className='health-status-danger' /> : |
|
||||||
text === 'warning' ? <div className='health-status-warning' /> : <div className='health-status-default' />, |
|
||||||
align: 'center', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: '% Complete', |
|
||||||
dataIndex: 'complete', |
|
||||||
key: 'complete', |
|
||||||
render: text => `${text}%`, |
|
||||||
align: 'right' |
|
||||||
}, |
|
||||||
// {
|
|
||||||
// title: 'Action',
|
|
||||||
// key: 'action',
|
|
||||||
// render: (text, record) => (
|
|
||||||
// <Space size="middle">
|
|
||||||
// <a>Invite {record.name}</a>
|
|
||||||
// <a>Delete</a>
|
|
||||||
// </Space>
|
|
||||||
// ),
|
|
||||||
// },
|
|
||||||
]; |
|
||||||
|
|
||||||
const data = [ |
|
||||||
{ |
|
||||||
key: '1', |
|
||||||
project: 'Bay Plaza', |
|
||||||
owner: 'John Brown', |
|
||||||
startDate: '1/22/2022', |
|
||||||
endDate: '1/22/2023', |
|
||||||
complete: 20, |
|
||||||
cost: 40000000, |
|
||||||
costHealth: 'good', |
|
||||||
workHealth: 'default', |
|
||||||
scheduleHealth: 'warning' |
|
||||||
}, |
|
||||||
{ |
|
||||||
key: '2', |
|
||||||
project: 'Jambi Bridge', |
|
||||||
owner: 'Jim Green', |
|
||||||
startDate: '4/22/2022', |
|
||||||
endDate: '11/22/2023', |
|
||||||
complete: 65, |
|
||||||
cost: 20000000, |
|
||||||
costHealth: 'good', |
|
||||||
workHealth: 'default', |
|
||||||
scheduleHealth: 'danger' |
|
||||||
}, |
|
||||||
{ |
|
||||||
key: '3', |
|
||||||
project: 'Banten International Airport', |
|
||||||
owner: 'Joe Black', |
|
||||||
startDate: '1/22/2022', |
|
||||||
endDate: '1/22/2025', |
|
||||||
complete: 50, |
|
||||||
cost: 200000000, |
|
||||||
costHealth: 'default', |
|
||||||
workHealth: 'default', |
|
||||||
scheduleHealth: 'default' |
|
||||||
}, |
|
||||||
]; |
|
||||||
|
|
||||||
const TableDashboard = () => { |
|
||||||
return ( |
|
||||||
<Table size="small" columns={columns} dataSource={data} scroll={{ x: 1300, y: 300 }} /> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
export default TableDashboard; |
|
@ -1,44 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
import { Doughnut } from 'react-chartjs-2'; |
|
||||||
|
|
||||||
const options = { |
|
||||||
title: { |
|
||||||
display: true, |
|
||||||
text: 'PROJECT BY TYPE' |
|
||||||
}, |
|
||||||
}; |
|
||||||
|
|
||||||
export const ProjectTypeChart = ({ projectTypes, projectsByType }) => { |
|
||||||
let total = [] |
|
||||||
|
|
||||||
for (var j = 0; j < projectTypes.length; j++) { |
|
||||||
total.push(projectsByType[j].total) |
|
||||||
} |
|
||||||
|
|
||||||
let dataSets = [{ |
|
||||||
label: "#", |
|
||||||
data: total, |
|
||||||
backgroundColor: [ |
|
||||||
'rgba(255, 99, 132, 0.2)', |
|
||||||
'rgba(54, 162, 235, 0.2)', |
|
||||||
'rgba(255, 206, 86, 0.2)', |
|
||||||
'rgba(75, 192, 192, 0.2)', |
|
||||||
], |
|
||||||
borderColor: [ |
|
||||||
'rgba(255, 99, 132, 1)', |
|
||||||
'rgba(54, 162, 235, 1)', |
|
||||||
'rgba(255, 206, 86, 1)', |
|
||||||
'rgba(75, 192, 192, 1)', |
|
||||||
'rgba(153, 102, 255, 1)', |
|
||||||
], |
|
||||||
borderWidth: 1, |
|
||||||
}] |
|
||||||
|
|
||||||
const data = { |
|
||||||
labels: projectTypes, |
|
||||||
datasets: dataSets, |
|
||||||
}; |
|
||||||
|
|
||||||
return <Doughnut data={data} options={options} />; |
|
||||||
} |
|
||||||
|
|
@ -1,181 +0,0 @@ |
|||||||
import React, { useEffect, useState } from 'react'; |
|
||||||
import { Row, Col, Collapse } from 'antd'; |
|
||||||
import TableDashboardV1 from './tableDashboardv1'; |
|
||||||
import { ProjectTypeChart } from './chartDashboard'; |
|
||||||
import { ProjectPhaseChart } from './projectPhaseChart'; |
|
||||||
import ProjectMap from './projectMap'; |
|
||||||
import axios from 'axios' |
|
||||||
import { BASE_OSPRO } from '../../const/ApiConst'; |
|
||||||
import { formatRibuanDecimal } from '../../const/CustomFunc.js'; |
|
||||||
|
|
||||||
const { Panel } = Collapse; |
|
||||||
|
|
||||||
function BoxDashboardNoIcon({ value, title, secondaryTitle, bgColor, valFSize = '1.0rem', fSize = '0.7rem' }) { |
|
||||||
return ( |
|
||||||
<> |
|
||||||
<div style={{ backgroundColor: bgColor, height: 60 }} class='box-dashboard-pm'> |
|
||||||
<Row> |
|
||||||
<Col span={24}> |
|
||||||
<div class='box-content'> |
|
||||||
<div style={{ fontSize: valFSize }} class='text-box'>{value}</div> |
|
||||||
<div style={{ fontSize: fSize }} class='text-box'>{title}</div> |
|
||||||
<div style={{ fontSize: '0.6rem' }} class='text-box-secondary'>{secondaryTitle && secondaryTitle.toUpperCase()}</div> |
|
||||||
</div> |
|
||||||
</Col> |
|
||||||
</Row> |
|
||||||
</div> |
|
||||||
</> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
const DashboardPM = () => { |
|
||||||
const [PROJECTCOUNT, SET_PROJECTCOUNT] = useState(0) |
|
||||||
const [PROJECTBUDGETTOTAL, SET_PROJECTBUDGETTOTAL] = useState(0) |
|
||||||
const [PROJECTMANPOWER, SET_PROJETMANPOWER] = useState(0) |
|
||||||
const [PROJECTACTUALCOSTTOTAL, SET_PROJECTACTUALCOSTTOTAL] = useState(0) |
|
||||||
const [PROJECTCOSTTOTAL, SET_PROJECTCOSTTOTAL] = useState(0) |
|
||||||
const [TOTALREVENUE, SET_TOTALREVENUE] = useState(0) |
|
||||||
const [MANPOWERS, SET_MANPOWERS] = useState(0) |
|
||||||
const [PROJECTPHASES, SET_PROJECTPHASES] = useState([]) |
|
||||||
const [PROJECTSBYPHASE, SET_PROJECTSBYPHASE] = useState([]) |
|
||||||
const [PROJECTSONDANGER, SET_PROJECTSONDANGER] = useState(0) |
|
||||||
const [PROJECTLOCATIONS, SET_PROJECTLOCATIONS] = useState([]) |
|
||||||
|
|
||||||
const [PROJECTTYPES, SET_PROJECTTYPES] = useState([]) |
|
||||||
const [PROJECTSBYTYPE, SET_PROJECTSBYTYPE] = useState([]) |
|
||||||
|
|
||||||
const token = localStorage.getItem("token") |
|
||||||
const HEADER = { |
|
||||||
headers: { |
|
||||||
"Content-Type": "application/json", |
|
||||||
"Authorization": `Bearer ${token}` |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const getProjectInfos = async () => { |
|
||||||
const URL = `${BASE_OSPRO}/api/project/list` |
|
||||||
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) |
|
||||||
let markers = [] |
|
||||||
|
|
||||||
SET_PROJECTCOUNT(result.data.totalRecord) |
|
||||||
result.data.totalActualCost != undefined ? SET_PROJECTCOSTTOTAL(result.data.totalActualCost) : SET_PROJECTCOSTTOTAL(0) |
|
||||||
result.data.totalPlannedCost != undefined ? SET_PROJECTBUDGETTOTAL(result.data.totalPlannedCost) : SET_PROJECTBUDGETTOTAL(0) |
|
||||||
result.data.totalRevenue != undefined ? SET_TOTALREVENUE(result.data.totalRevenue) : SET_TOTALREVENUE(0) |
|
||||||
result.data.manpowers != undefined ? SET_MANPOWERS(result.data.manpowers) : SET_MANPOWERS(0) |
|
||||||
result.data.projectPhases != undefined ? SET_PROJECTPHASES(result.data.projectPhases) : SET_PROJECTPHASES([]) |
|
||||||
result.data.projectsByPhase != undefined ? SET_PROJECTSBYPHASE(result.data.projectsByPhase) : SET_PROJECTSBYPHASE([]) |
|
||||||
result.data.projectTypes != undefined ? SET_PROJECTTYPES(result.data.projectTypes) : SET_PROJECTTYPES([]) |
|
||||||
result.data.projectsByType != undefined ? SET_PROJECTSBYTYPE(result.data.projectsByType) : SET_PROJECTSBYTYPE([]) |
|
||||||
result.data.projectsOnDanger != undefined ? SET_PROJECTSONDANGER(result.data.projectsOnDanger) : SET_PROJECTSONDANGER(0) |
|
||||||
|
|
||||||
for (let i = 0; i < result.data.totalRecord; i++) { |
|
||||||
if (typeof (result.data.data[i].geolocation) == 'object' && result.data.data[i].geolocation.length > 0) { |
|
||||||
markers.push({ |
|
||||||
lat: result.data.data[i].geolocation[0].lat, |
|
||||||
lon: result.data.data[i].geolocation[0].lon, |
|
||||||
label: result.data.data[i].area_kerja + "\n" + result.data.data[i].nama |
|
||||||
}) |
|
||||||
} |
|
||||||
} |
|
||||||
SET_PROJECTLOCATIONS(markers) |
|
||||||
} |
|
||||||
|
|
||||||
useEffect(() => { |
|
||||||
getProjectInfos(); |
|
||||||
}, []) |
|
||||||
|
|
||||||
return ( |
|
||||||
<> |
|
||||||
<div> |
|
||||||
<Row |
|
||||||
gutter={{ |
|
||||||
xs: 8, |
|
||||||
sm: 16, |
|
||||||
md: 24, |
|
||||||
lg: 32, |
|
||||||
}} |
|
||||||
> |
|
||||||
<Col span={2} style={{ margin: '' }}> |
|
||||||
<BoxDashboardNoIcon |
|
||||||
value={PROJECTCOUNT} |
|
||||||
bgColor="#0287c7" |
|
||||||
title="Projects" |
|
||||||
/> |
|
||||||
</Col> |
|
||||||
<Col span={3} style={{ margin: '' }}> |
|
||||||
<BoxDashboardNoIcon |
|
||||||
value={PROJECTSONDANGER} |
|
||||||
bgColor="#f51d1d" |
|
||||||
title="Projects on Danger" |
|
||||||
fSize="0.7rem" |
|
||||||
/> |
|
||||||
</Col> |
|
||||||
<Col span={5} style={{ margin: '' }}> |
|
||||||
<BoxDashboardNoIcon |
|
||||||
value={`${formatRibuanDecimal(PROJECTBUDGETTOTAL)}`} |
|
||||||
bgColor="#cf6102" |
|
||||||
title="Budget" |
|
||||||
valFSize="1rem" |
|
||||||
/> |
|
||||||
</Col> |
|
||||||
<Col span={5} style={{ margin: '' }}> |
|
||||||
<BoxDashboardNoIcon |
|
||||||
value={`${formatRibuanDecimal(PROJECTCOSTTOTAL)}`} |
|
||||||
bgColor="#f51d1d" |
|
||||||
title="Project Expanditure" |
|
||||||
valFSize="1rem" |
|
||||||
/> |
|
||||||
</Col> |
|
||||||
<Col span={5} style={{ margin: '' }}> |
|
||||||
<BoxDashboardNoIcon |
|
||||||
value={`${formatRibuanDecimal(TOTALREVENUE)}`} |
|
||||||
bgColor="#077857" |
|
||||||
title="Revenue" |
|
||||||
valFSize="1rem" |
|
||||||
/> |
|
||||||
</Col> |
|
||||||
<Col span={2} style={{ margin: '' }}> |
|
||||||
<BoxDashboardNoIcon |
|
||||||
value={MANPOWERS} |
|
||||||
bgColor="#0287c7" |
|
||||||
title="Manpower" |
|
||||||
/> |
|
||||||
</Col> |
|
||||||
<Col span={2} style={{ margin: '' }}> |
|
||||||
<BoxDashboardNoIcon |
|
||||||
value={<i style={{ color: '#fff' }} class="zmdi zmdi-truck zmdi-hc-2x"></i>} |
|
||||||
bgColor="#0287c7" |
|
||||||
title="OSLOG" |
|
||||||
/> |
|
||||||
</Col> |
|
||||||
</Row> |
|
||||||
<Row style={{ marginTop: '15px' }}> |
|
||||||
<Col span={24} style={{ margin: '0px 0px 0px 0px' }}> |
|
||||||
<Collapse> |
|
||||||
<Panel header="More Information" key="1"> |
|
||||||
<Row style={{ margin: '10px 10px 0px 10px' }}> |
|
||||||
<Col span={8} > |
|
||||||
<ProjectMap markers={PROJECTLOCATIONS} /> |
|
||||||
</Col> |
|
||||||
<Col span={8}> |
|
||||||
<ProjectPhaseChart projectPhases={PROJECTPHASES} projectsByPhase={PROJECTSBYPHASE} /> |
|
||||||
</Col> |
|
||||||
<Col span={8}> |
|
||||||
<ProjectTypeChart projectTypes={PROJECTTYPES} projectsByType={PROJECTSBYTYPE} /> |
|
||||||
</Col> |
|
||||||
</Row> |
|
||||||
</Panel> |
|
||||||
</Collapse> |
|
||||||
</Col> |
|
||||||
</Row> |
|
||||||
<Row style={{ marginTop: '15px' }}> |
|
||||||
<Col span={24}> |
|
||||||
<TableDashboardV1 /> |
|
||||||
</Col> |
|
||||||
</Row> |
|
||||||
</div> |
|
||||||
</> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
export default DashboardPM; |
|
@ -1,40 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
|
|
||||||
import icon from 'leaflet/dist/images/marker-icon.png'; |
|
||||||
import iconShadow from 'leaflet/dist/images/marker-shadow.png'; |
|
||||||
import L from 'leaflet'; |
|
||||||
import { MapContainer, Marker, TileLayer, Popup } from 'react-leaflet' |
|
||||||
import 'leaflet/dist/leaflet.css'; |
|
||||||
|
|
||||||
const defaultMapPosition = [-6.1753924, 106.8271528] |
|
||||||
|
|
||||||
const DefaultIcon = L.icon({ |
|
||||||
iconUrl: icon, |
|
||||||
shadowUrl: iconShadow, |
|
||||||
iconSize: [15, 25], |
|
||||||
shadowSize: [0, 0], |
|
||||||
}); |
|
||||||
L.Marker.prototype.options.icon = DefaultIcon; |
|
||||||
|
|
||||||
const ProjectMap = ({ markers }) => { |
|
||||||
return ( |
|
||||||
<MapContainer |
|
||||||
center={defaultMapPosition} |
|
||||||
zoom={5} |
|
||||||
> |
|
||||||
<TileLayer |
|
||||||
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' |
|
||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" |
|
||||||
/> |
|
||||||
{markers.map(({ lat, lon, label }) => ( |
|
||||||
<Marker position={[lat, lon]}> |
|
||||||
<Popup> |
|
||||||
{label} |
|
||||||
</Popup> |
|
||||||
</Marker> |
|
||||||
))} |
|
||||||
</MapContainer> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
export default ProjectMap; |
|
@ -1,37 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
import { HorizontalBar } from 'react-chartjs-2'; |
|
||||||
|
|
||||||
const options = { |
|
||||||
responsive: true, |
|
||||||
title: { |
|
||||||
display: true, |
|
||||||
text: 'PROJECT BY PHASE' |
|
||||||
} |
|
||||||
}; |
|
||||||
|
|
||||||
export const ProjectPhaseChart = ({ projectPhases, projectsByPhase }) => { |
|
||||||
let dataSets = [] |
|
||||||
let total = [] |
|
||||||
|
|
||||||
for (var i = 0; i < projectsByPhase.length; i++) { |
|
||||||
total = [] |
|
||||||
for (var j = 0; j < projectPhases.length; j++) { |
|
||||||
if (projectPhases[j] != projectsByPhase[i].name) { |
|
||||||
total.push(0) |
|
||||||
} else { |
|
||||||
total.push(projectsByPhase[i].total) |
|
||||||
} |
|
||||||
} |
|
||||||
dataSets.push({ |
|
||||||
label: projectsByPhase[i].name, |
|
||||||
data: total, |
|
||||||
backgroundColor: projectsByPhase[i].color, |
|
||||||
borderColor: '#000000', |
|
||||||
}); |
|
||||||
} |
|
||||||
const data = { |
|
||||||
datasets: dataSets, |
|
||||||
labels: projectPhases, |
|
||||||
}; |
|
||||||
return <HorizontalBar options={options} data={data} />; |
|
||||||
} |
|
@ -1,130 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
import { Table, Tag, Space } from 'antd'; |
|
||||||
|
|
||||||
const columns = [ |
|
||||||
{ |
|
||||||
title: 'Project Name', |
|
||||||
dataIndex: 'project', |
|
||||||
key: 'project', |
|
||||||
fixed: 'left', |
|
||||||
width: 200, |
|
||||||
render: text => <a>{text}</a>, |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Project Owner', |
|
||||||
dataIndex: 'owner', |
|
||||||
key: 'owner', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Start Date', |
|
||||||
dataIndex: 'startDate', |
|
||||||
key: 'startDate', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'End Date', |
|
||||||
dataIndex: 'endDate', |
|
||||||
key: 'endDate', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Cost', |
|
||||||
dataIndex: 'cost', |
|
||||||
key: 'cost', |
|
||||||
render: text => `Rp. ${text.toLocaleString()}`, |
|
||||||
align: 'right' |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Cost Health', |
|
||||||
dataIndex: 'costHealth', |
|
||||||
key: 'costHealth', |
|
||||||
render: text => |
|
||||||
text === 'good' ? <div className='health-status-good' /> : |
|
||||||
text === 'danger' ? <div className='health-status-danger' /> : |
|
||||||
text === 'warning' ? <div className='health-status-warning' /> : <div className='health-status-default' /> |
|
||||||
, |
|
||||||
align: 'center', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Work Health', |
|
||||||
dataIndex: 'workHealth', |
|
||||||
key: 'workHealth', |
|
||||||
render: text => |
|
||||||
text === 'good' ? <div className='health-status-good' /> : |
|
||||||
text === 'danger' ? <div className='health-status-danger' /> : |
|
||||||
text === 'warning' ? <div className='health-status-warning' /> : <div className='health-status-default' />, |
|
||||||
align: 'center', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Schedule Health', |
|
||||||
dataIndex: 'scheduleHealth', |
|
||||||
key: 'scheduleHealth', |
|
||||||
render: text => |
|
||||||
text === 'good' ? <div className='health-status-good' /> : |
|
||||||
text === 'danger' ? <div className='health-status-danger' /> : |
|
||||||
text === 'warning' ? <div className='health-status-warning' /> : <div className='health-status-default' />, |
|
||||||
align: 'center', |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: '% Complete', |
|
||||||
dataIndex: 'complete', |
|
||||||
key: 'complete', |
|
||||||
render: text => `${text}%`, |
|
||||||
align: 'right' |
|
||||||
}, |
|
||||||
// {
|
|
||||||
// title: 'Action',
|
|
||||||
// key: 'action',
|
|
||||||
// render: (text, record) => (
|
|
||||||
// <Space size="middle">
|
|
||||||
// <a>Invite {record.name}</a>
|
|
||||||
// <a>Delete</a>
|
|
||||||
// </Space>
|
|
||||||
// ),
|
|
||||||
// },
|
|
||||||
]; |
|
||||||
|
|
||||||
const data = [ |
|
||||||
{ |
|
||||||
key: '1', |
|
||||||
project: 'Bay Plaza', |
|
||||||
owner: 'John Brown', |
|
||||||
startDate: '1/22/2022', |
|
||||||
endDate: '1/22/2023', |
|
||||||
complete: 20, |
|
||||||
cost: 40000000, |
|
||||||
costHealth: 'good', |
|
||||||
workHealth: 'default', |
|
||||||
scheduleHealth: 'warning' |
|
||||||
}, |
|
||||||
{ |
|
||||||
key: '2', |
|
||||||
project: 'Jambi Bridge', |
|
||||||
owner: 'Jim Green', |
|
||||||
startDate: '4/22/2022', |
|
||||||
endDate: '11/22/2023', |
|
||||||
complete: 65, |
|
||||||
cost: 20000000, |
|
||||||
costHealth: 'good', |
|
||||||
workHealth: 'default', |
|
||||||
scheduleHealth: 'danger' |
|
||||||
}, |
|
||||||
{ |
|
||||||
key: '3', |
|
||||||
project: 'Banten International Airport', |
|
||||||
owner: 'Joe Black', |
|
||||||
startDate: '1/22/2022', |
|
||||||
endDate: '1/22/2025', |
|
||||||
complete: 50, |
|
||||||
cost: 200000000, |
|
||||||
costHealth: 'default', |
|
||||||
workHealth: 'default', |
|
||||||
scheduleHealth: 'default' |
|
||||||
}, |
|
||||||
]; |
|
||||||
|
|
||||||
const TableDashboard = () => { |
|
||||||
return ( |
|
||||||
<Table size="small" columns={columns} dataSource={data} scroll={{ x: 1300, y: 300 }} /> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
export default TableDashboard; |
|
@ -1,376 +0,0 @@ |
|||||||
import '../../../node_modules/react-grid-layout/css/styles.css'; |
|
||||||
import '../../../node_modules/react-resizable/css/styles.css'; |
|
||||||
import './../DashboardPMO/Dashboard.css'; |
|
||||||
import { BASE_OSPRO } from '../../const/ApiConst'; |
|
||||||
import React, { useEffect, useState, useRef } from 'react'; |
|
||||||
import axios from 'axios' |
|
||||||
import { Table, Row, Col, Button, Input, InputNumber, Space } from 'antd'; |
|
||||||
import { formatRibuanDecimal } from '../../const/CustomFunc.js'; |
|
||||||
import { Badge } from 'reactstrap'; |
|
||||||
import { Link } from "react-router-dom"; |
|
||||||
import { SearchOutlined } from '@ant-design/icons'; |
|
||||||
import Highlighter from 'react-highlight-words'; |
|
||||||
import _ from "lodash"; |
|
||||||
import Slider from "antd/lib/slider"; |
|
||||||
import numeral from "numeral"; |
|
||||||
|
|
||||||
const filterData = data => formatter => data.map(item => ({ |
|
||||||
text: formatter(item), |
|
||||||
value: formatter(item) |
|
||||||
})) |
|
||||||
|
|
||||||
const TableDashboardV1 = () => { |
|
||||||
const token = localStorage.getItem("token") |
|
||||||
const HEADER = { |
|
||||||
headers: { |
|
||||||
"Content-Type": "application/json", |
|
||||||
"Authorization": `Bearer ${token}`, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const [MANPOWERS, SET_MANPOWERS] = useState(0) |
|
||||||
const [MAXPLANNEDCOST, SET_MAXPLANNEDCOST] = useState(0) |
|
||||||
const [MAXACTUALCOST, SET_MAXACTUALCOST] = useState(0) |
|
||||||
const [MAXCOSTVARIANCE, SET_MAXCOSTVARIANCE] = useState(0) |
|
||||||
const [dataTable, setDataTable] = useState([]) |
|
||||||
const [searchText, setSearchText] = useState('') |
|
||||||
const [searchedColumn, setSearchedColumn] = useState('') |
|
||||||
const searchInput = useRef(null) |
|
||||||
|
|
||||||
const handleSearch = (selectedKeys, confirm, dataIndex) => { |
|
||||||
confirm() |
|
||||||
setSearchText(selectedKeys[0]) |
|
||||||
setSearchedColumn(dataIndex) |
|
||||||
}; |
|
||||||
|
|
||||||
const handleSlide = (dataIndex, value, confirm) => { |
|
||||||
confirm({ closeDropdown: false }); |
|
||||||
}; |
|
||||||
|
|
||||||
const slider = (dataIndex, maxSlider) => ({ |
|
||||||
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => { |
|
||||||
return ( |
|
||||||
<div |
|
||||||
className="custom-filter-dropdown ant-table-filter-dropdown" |
|
||||||
style={{ minWidth: "20rem", padding: "0.5rem 1rem" }} |
|
||||||
> |
|
||||||
<Row> |
|
||||||
<Col span={4}> |
|
||||||
<div style={{ display: "flex", flexDirection: "column" }}> |
|
||||||
<div> |
|
||||||
<strong>Min: </strong> |
|
||||||
</div> |
|
||||||
<div>0</div> |
|
||||||
</div> |
|
||||||
</Col> |
|
||||||
<Col span={16}> |
|
||||||
<Slider |
|
||||||
range={true} |
|
||||||
min={0} |
|
||||||
max={maxSlider} |
|
||||||
defaultValue={[0, maxSlider]} |
|
||||||
onAfterChange={(value) => { |
|
||||||
setSelectedKeys([value]) |
|
||||||
handleSlide(dataIndex, value, confirm)} |
|
||||||
} |
|
||||||
/> |
|
||||||
</Col> |
|
||||||
<Col span={4}> |
|
||||||
<div style={{ display: "flex", flexDirection: "column" }}> |
|
||||||
<div> |
|
||||||
<strong>Max:</strong> |
|
||||||
</div> |
|
||||||
<div style={{ whitespace: "nowrap", overflow: "hidden" }} ellipsis={true}>{dataIndex == 'manpower' || dataIndex == 'progress' ? maxSlider : formatRibuanDecimal(maxSlider)}</div> |
|
||||||
</div> |
|
||||||
</Col> |
|
||||||
</Row> |
|
||||||
</div> |
|
||||||
)}, |
|
||||||
onFilter: (value, record) => { |
|
||||||
return (record[dataIndex] >= value[0] && record[dataIndex] <= value[1]) |
|
||||||
}, |
|
||||||
onFilterDropdownVisibleChange: (visible) => { |
|
||||||
if (visible) { |
|
||||||
setTimeout(() => searchInput.current?.select(), 100); |
|
||||||
} |
|
||||||
}, |
|
||||||
}); |
|
||||||
|
|
||||||
const handleReset = (clearFilters) => { |
|
||||||
clearFilters(); |
|
||||||
setSearchText(''); |
|
||||||
}; |
|
||||||
|
|
||||||
const getColumnSearchProps = (dataIndex) => ({ |
|
||||||
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => { |
|
||||||
return ( |
|
||||||
<div |
|
||||||
style={{ |
|
||||||
padding: 8, |
|
||||||
}} |
|
||||||
> |
|
||||||
<Input |
|
||||||
ref={searchInput} |
|
||||||
placeholder={`Search ${dataIndex}`} |
|
||||||
value={selectedKeys[0]} |
|
||||||
onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : [])} |
|
||||||
onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)} |
|
||||||
style={{ |
|
||||||
marginBottom: 8, |
|
||||||
display: 'block', |
|
||||||
}} |
|
||||||
/> |
|
||||||
<Space> |
|
||||||
<Button |
|
||||||
type="primary" |
|
||||||
onClick={() => handleSearch(selectedKeys, confirm, dataIndex)} |
|
||||||
icon={<SearchOutlined />} |
|
||||||
size="small" |
|
||||||
style={{ |
|
||||||
width: 90, |
|
||||||
}} |
|
||||||
> |
|
||||||
Search |
|
||||||
</Button> |
|
||||||
<Button |
|
||||||
onClick={() => clearFilters && handleReset(clearFilters)} |
|
||||||
size="small" |
|
||||||
style={{ |
|
||||||
width: 90, |
|
||||||
}} |
|
||||||
> |
|
||||||
Reset |
|
||||||
</Button> |
|
||||||
<Button |
|
||||||
type="link" |
|
||||||
size="small" |
|
||||||
onClick={() => { |
|
||||||
confirm({ |
|
||||||
closeDropdown: false, |
|
||||||
}); |
|
||||||
setSearchText(selectedKeys[0]); |
|
||||||
setSearchedColumn(dataIndex); |
|
||||||
}} |
|
||||||
> |
|
||||||
</Button> |
|
||||||
</Space> |
|
||||||
</div> |
|
||||||
)}, |
|
||||||
filterIcon: (filtered) => ( |
|
||||||
<SearchOutlined |
|
||||||
style={{ |
|
||||||
color: filtered ? '#1890ff' : undefined, |
|
||||||
}} |
|
||||||
/> |
|
||||||
), |
|
||||||
onFilter: (value, record) => { |
|
||||||
return record[dataIndex].toString().toLowerCase().includes(value.toLowerCase()) |
|
||||||
}, |
|
||||||
onFilterDropdownVisibleChange: (visible) => { |
|
||||||
if (visible) { |
|
||||||
setTimeout(() => searchInput.current?.select(), 100); |
|
||||||
} |
|
||||||
}, |
|
||||||
render: (text, record) => |
|
||||||
searchedColumn === dataIndex ? ( |
|
||||||
<Highlighter |
|
||||||
highlightStyle={{ |
|
||||||
backgroundColor: '#ffc069', |
|
||||||
padding: 0, |
|
||||||
}} |
|
||||||
searchWords={[searchText]} |
|
||||||
autoEscape |
|
||||||
textToHighlight={text ? text.toString() : ''} |
|
||||||
/> |
|
||||||
) : ( |
|
||||||
<> |
|
||||||
<Link to={`/dashboard-project/${record.id}/${record.lastGanttId}`}> |
|
||||||
{record.kode_sortname} <br /> {text} |
|
||||||
</Link> |
|
||||||
</> |
|
||||||
), |
|
||||||
}); |
|
||||||
|
|
||||||
|
|
||||||
const filterDate = (dataIndex) => ({ |
|
||||||
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => { |
|
||||||
return ( |
|
||||||
<div |
|
||||||
style={{ |
|
||||||
padding: 8, |
|
||||||
}} |
|
||||||
> |
|
||||||
<InputNumber |
|
||||||
ref={searchInput} |
|
||||||
placeholder={`Year`} |
|
||||||
value={selectedKeys[0]} |
|
||||||
min={2020} |
|
||||||
max={2050} |
|
||||||
//onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : [])}
|
|
||||||
onChange={(e) => console.log(e)} |
|
||||||
style={{ |
|
||||||
marginBottom: 8, |
|
||||||
display: 'block', |
|
||||||
}} |
|
||||||
/> |
|
||||||
<Space> |
|
||||||
<Button |
|
||||||
type="primary" |
|
||||||
onClick={() => handleSearch(selectedKeys, confirm, dataIndex)} |
|
||||||
icon={<SearchOutlined />} |
|
||||||
size="small" |
|
||||||
style={{ |
|
||||||
width: 90, |
|
||||||
}} |
|
||||||
> |
|
||||||
Search |
|
||||||
</Button> |
|
||||||
<Button |
|
||||||
onClick={() => clearFilters && handleReset(clearFilters)} |
|
||||||
size="small" |
|
||||||
style={{ |
|
||||||
width: 90, |
|
||||||
}} |
|
||||||
> |
|
||||||
Reset |
|
||||||
</Button> |
|
||||||
<Button |
|
||||||
type="link" |
|
||||||
size="small" |
|
||||||
onClick={() => { |
|
||||||
confirm({ |
|
||||||
closeDropdown: false, |
|
||||||
}); |
|
||||||
setSearchText(selectedKeys[0]); |
|
||||||
setSearchedColumn(dataIndex); |
|
||||||
}} |
|
||||||
> |
|
||||||
</Button> |
|
||||||
</Space> |
|
||||||
</div> |
|
||||||
)}, |
|
||||||
onFilter: (value, record) => { |
|
||||||
return record[dataIndex].toString().toLowerCase().includes(value.toLowerCase()) |
|
||||||
}, |
|
||||||
onFilterDropdownVisibleChange: (visible) => { |
|
||||||
if (visible) { |
|
||||||
setTimeout(() => searchInput.current?.select(), 100); |
|
||||||
} |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
const getProjects = async () => { |
|
||||||
const URL = `${BASE_OSPRO}/api/project/list` |
|
||||||
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) |
|
||||||
|
|
||||||
if (result.data.code == 200) { |
|
||||||
setDataTable(result.data.data); |
|
||||||
result.data.manpowers != undefined ? SET_MANPOWERS(result.data.manpowers) : SET_MANPOWERS(0) |
|
||||||
SET_MAXPLANNEDCOST(Math.max(...result.data.data.map(o => o.plannedCost))) |
|
||||||
SET_MAXACTUALCOST(Math.max(...result.data.data.map(o => o.actualCost))) |
|
||||||
SET_MAXCOSTVARIANCE(Math.max(...result.data.data.map(o => o.costVariance))) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const columns = [ |
|
||||||
{ |
|
||||||
title: 'Project', |
|
||||||
dataIndex: 'nama', |
|
||||||
key: 'nama', |
|
||||||
render: (text, record) => |
|
||||||
<Link to={`/dashboard-project/${record.id}/${record.lastGanttId}`}> |
|
||||||
{record.kode_sortname} <br /> {text} |
|
||||||
</Link>, |
|
||||||
...getColumnSearchProps('nama'), |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Project Manager', |
|
||||||
dataIndex: 'projectManager', |
|
||||||
key: 'projectManager', |
|
||||||
filters: _.uniqWith(filterData(dataTable)(i => i.projectManager), _.isEqual), |
|
||||||
onFilter: (value, record) => record.projectManager.indexOf(value) === 0, |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Planned Interval', |
|
||||||
dataIndex: 'plannedInterval', |
|
||||||
key: 'plannedInterval', |
|
||||||
...filterDate('plannedInterval'), |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Manpower', |
|
||||||
dataIndex: 'manpower', |
|
||||||
key: 'manpower', |
|
||||||
render: (text) => { |
|
||||||
return `${text}/${MANPOWERS}` |
|
||||||
}, |
|
||||||
...slider('manpower', MANPOWERS) |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Budget Project', |
|
||||||
dataIndex: 'plannedCost', |
|
||||||
key: 'plannedCost', |
|
||||||
render: (text) => <a>{formatRibuanDecimal(text)}</a>, |
|
||||||
...slider('plannedCost', MAXPLANNEDCOST) |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Actual Cost', |
|
||||||
dataIndex: 'actualCost', |
|
||||||
key: 'actualCost', |
|
||||||
render: (text) => <a>{formatRibuanDecimal(text)}</a>, |
|
||||||
...slider('actualCost', MAXACTUALCOST) |
|
||||||
|
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Cost Variance', |
|
||||||
dataIndex: 'costVariance', |
|
||||||
key: 'costVariance', |
|
||||||
render: (text) => <a>{formatRibuanDecimal(text)}</a>, |
|
||||||
...slider('costVariance', MAXCOSTVARIANCE) |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Cost Health', |
|
||||||
dataIndex: 'costHealth', |
|
||||||
key: 'costHealth', |
|
||||||
render: (text) => { |
|
||||||
if (text == "on-budget") { |
|
||||||
return <Badge color="success">On Budget</Badge> |
|
||||||
} else if (text == "warning") { |
|
||||||
return <Badge color="warning">Warning</Badge> |
|
||||||
} else { |
|
||||||
return <Badge color="danger">Danger</Badge> |
|
||||||
} |
|
||||||
}, |
|
||||||
filters: _.uniqWith(filterData(dataTable)(i => i.costHealth), _.isEqual), |
|
||||||
onFilter: (value, record) => record.costHealth.indexOf(value) === 0, |
|
||||||
}, |
|
||||||
{ |
|
||||||
title: 'Progress', |
|
||||||
dataIndex: 'progress', |
|
||||||
key: 'progress', |
|
||||||
render: (text) => { |
|
||||||
return <Badge color={parseInt(text) > 74 ? 'success' : text > 49 ? 'warning' : 'danger'}>{text}%</Badge> |
|
||||||
}, |
|
||||||
...slider('progress', 100) |
|
||||||
}, |
|
||||||
]; |
|
||||||
|
|
||||||
useEffect(() => { |
|
||||||
getProjects(); |
|
||||||
}, []) |
|
||||||
|
|
||||||
|
|
||||||
return ( |
|
||||||
<> |
|
||||||
<Row gutter={[16, 16]}> |
|
||||||
<Col span={24}> |
|
||||||
<Table |
|
||||||
columns={columns} |
|
||||||
dataSource={dataTable} |
|
||||||
/> |
|
||||||
</Col> |
|
||||||
</Row> |
|
||||||
</> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
export default TableDashboardV1; |
|
@ -1,49 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
import { Bar } from 'react-chartjs-2'; |
|
||||||
|
|
||||||
const data = { |
|
||||||
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'], |
|
||||||
datasets: [ |
|
||||||
{ |
|
||||||
label: '# of Votes', |
|
||||||
data: [12, 19, 3, 5, 2, 3], |
|
||||||
backgroundColor: [ |
|
||||||
'rgba(255, 99, 132, 0.2)', |
|
||||||
'rgba(54, 162, 235, 0.2)', |
|
||||||
'rgba(255, 206, 86, 0.2)', |
|
||||||
'rgba(75, 192, 192, 0.2)', |
|
||||||
'rgba(153, 102, 255, 0.2)', |
|
||||||
'rgba(255, 159, 64, 0.2)', |
|
||||||
], |
|
||||||
borderColor: [ |
|
||||||
'rgba(255, 99, 132, 1)', |
|
||||||
'rgba(54, 162, 235, 1)', |
|
||||||
'rgba(255, 206, 86, 1)', |
|
||||||
'rgba(75, 192, 192, 1)', |
|
||||||
'rgba(153, 102, 255, 1)', |
|
||||||
'rgba(255, 159, 64, 1)', |
|
||||||
], |
|
||||||
borderWidth: 1, |
|
||||||
}, |
|
||||||
], |
|
||||||
}; |
|
||||||
|
|
||||||
const options = { |
|
||||||
scales: { |
|
||||||
yAxes: [ |
|
||||||
{ |
|
||||||
ticks: { |
|
||||||
beginAtZero: true, |
|
||||||
}, |
|
||||||
}, |
|
||||||
], |
|
||||||
}, |
|
||||||
}; |
|
||||||
|
|
||||||
const BarChart = () => ( |
|
||||||
<> |
|
||||||
<Bar data={data} options={options} /> |
|
||||||
</> |
|
||||||
); |
|
||||||
|
|
||||||
export default BarChart; |
|
@ -1,128 +0,0 @@ |
|||||||
.number-asset { |
|
||||||
font-size: 50px; |
|
||||||
font-weight: bold; |
|
||||||
text-align: center; |
|
||||||
} |
|
||||||
|
|
||||||
.text-bold { |
|
||||||
font-weight: bold; |
|
||||||
} |
|
||||||
|
|
||||||
.view-rectangle { |
|
||||||
display: flex; |
|
||||||
flex-direction: row; |
|
||||||
width: 100%; |
|
||||||
justify-content: space-between; |
|
||||||
} |
|
||||||
|
|
||||||
.view1 { |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
margin-bottom: -20px; |
|
||||||
|
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
background-color: #008B8B; |
|
||||||
} |
|
||||||
|
|
||||||
.view2 { |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
|
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
background-color: #8B008B; |
|
||||||
} |
|
||||||
|
|
||||||
.view3 { |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
|
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
background-color: #7CFC00; |
|
||||||
} |
|
||||||
|
|
||||||
.view4 { |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
|
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
background-color: #FF0000; |
|
||||||
} |
|
||||||
|
|
||||||
.view5 { |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
|
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
background-color: #4682B4; |
|
||||||
} |
|
||||||
|
|
||||||
.number-style { |
|
||||||
font-size: 30px; |
|
||||||
color: black; |
|
||||||
font-weight: bold; |
|
||||||
margin-bottom:15px; |
|
||||||
} |
|
||||||
|
|
||||||
.number-style1 { |
|
||||||
font-size: 30px; |
|
||||||
color: #FFFFFF; |
|
||||||
font-weight: bold; |
|
||||||
margin-bottom:15px; |
|
||||||
} |
|
||||||
|
|
||||||
.daily-info-card { |
|
||||||
min-width: 130px; |
|
||||||
width: 220px; |
|
||||||
height: 100px; |
|
||||||
padding: 10px; |
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
text-align: center; |
|
||||||
margin: 4px; |
|
||||||
border-radius: 8px; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
.dashboard-container { |
|
||||||
overflow-x: auto; |
|
||||||
} |
|
||||||
|
|
||||||
.maptable-window-button-container { |
|
||||||
float: right; |
|
||||||
right: 0px; |
|
||||||
} |
|
||||||
|
|
||||||
.maptable-close, .maptable-maximize, .maptable-minimize { |
|
||||||
cursor: pointer; |
|
||||||
padding: 4px; |
|
||||||
} |
|
||||||
|
|
||||||
.maptable-header { |
|
||||||
margin-bottom: -10px; |
|
||||||
} |
|
||||||
|
|
||||||
.maptable-title { |
|
||||||
font-size: 24px; |
|
||||||
font-weight: 700; |
|
||||||
text-align: left; |
|
||||||
margin-left: 20px; |
|
||||||
} |
|
||||||
|
|
||||||
.maptable-close:hover, .maptable-maximize:hover, .maptable-minimize:hover { |
|
||||||
color: #20a8d8; |
|
||||||
} |
|
@ -1,18 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
import { Line } from 'react-chartjs-2'; |
|
||||||
|
|
||||||
const MultiAxisLine = ({ data, handleClickChart, optionsAdded }) => { |
|
||||||
const options = { ...optionsAdded }; |
|
||||||
|
|
||||||
return ( |
|
||||||
<> |
|
||||||
<Line |
|
||||||
data={data} |
|
||||||
options={options} |
|
||||||
{...handleClickChart && { getElementAtEvent: handleClickChart }} |
|
||||||
/> |
|
||||||
</> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
export default MultiAxisLine; |
|
@ -1,23 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
import { Pie } from 'react-chartjs-2'; |
|
||||||
|
|
||||||
const options = { |
|
||||||
responsive: true, |
|
||||||
maintainAspectRatio: false, |
|
||||||
title: { |
|
||||||
display: true, |
|
||||||
text: 'Status Proyek' |
|
||||||
}, |
|
||||||
legend: { |
|
||||||
display: true, |
|
||||||
position: 'bottom' |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
const PieChart = ({data}) => ( |
|
||||||
<> |
|
||||||
<Pie data={data} options={options} /> |
|
||||||
</> |
|
||||||
); |
|
||||||
|
|
||||||
export default PieChart; |
|
@ -1,308 +0,0 @@ |
|||||||
import '../../../node_modules/react-grid-layout/css/styles.css'; |
|
||||||
import '../../../node_modules/react-resizable/css/styles.css'; |
|
||||||
import '../Map/CustomScroll.css'; |
|
||||||
import './Dashboard.css'; |
|
||||||
import { Chart } from 'chart.js'; |
|
||||||
import { ChartDataLabels } from 'chartjs-plugin-datalabels'; |
|
||||||
import ContentLoader from "react-content-loader" |
|
||||||
import LineChart from './LineChart'; |
|
||||||
import PieChart from './PieChart'; |
|
||||||
import React, { useEffect, useState } from 'react'; |
|
||||||
import SiopasMap from '../Map/Map_16.js'; |
|
||||||
import axios from 'axios' |
|
||||||
import moment from 'moment'; |
|
||||||
import numeral from 'numeral'; |
|
||||||
import { BASE_SIMPRO } from '../../const/ApiConst'; |
|
||||||
import { DatePicker } from 'antd'; |
|
||||||
import { NotificationContainer, NotificationManager } from 'react-notifications'; |
|
||||||
import { PLANNING_REALISASI_SEARCH, PLANNING_SEARCH, PROYEK_SEARCH_DETAIL, PROYEK_EDIT } from '../../const/ApiConst.js'; |
|
||||||
import { Pagination, Tooltip, Tree, List, Checkbox } from 'antd'; |
|
||||||
import { getChildrenTree, formatRupiah, DATE_TIME_FORMAT } from '../../const/CustomFunc.js'; |
|
||||||
import { projectTreeConst } from '../../const/LayerTreeConst.js'; |
|
||||||
import { |
|
||||||
Card, Modal, ModalHeader, ModalBody, ModalFooter, Button, |
|
||||||
CardBody, CardHeader, Col, Dropdown, DropdownItem, DropdownMenu, DropdownToggle, Row |
|
||||||
} from 'reactstrap'; |
|
||||||
import { |
|
||||||
DONE_COLOR, IZIN_COLOR, NOT_YET_COLOR, |
|
||||||
PRESENT_COLOR, TOTAL_COLOR, |
|
||||||
RED_COLOR, |
|
||||||
ORANGE_COLOR, |
|
||||||
GREEN_COLOR, |
|
||||||
DARK_GREY_COLOR, |
|
||||||
BLUE_COLOR, |
|
||||||
PURPLE_COLOR |
|
||||||
} from '../../const/AppConst.js'; |
|
||||||
const id_org = window.localStorage.getItem('id_org'); |
|
||||||
const roleName = window.localStorage.getItem('role_name'); |
|
||||||
|
|
||||||
Chart.plugins.register(ChartDataLabels); |
|
||||||
const { RangePicker } = DatePicker; |
|
||||||
|
|
||||||
let menu = [ |
|
||||||
{ |
|
||||||
"id": 3, |
|
||||||
"title": "PANIC BUTTON", |
|
||||||
"key": "absent", |
|
||||||
"color": IZIN_COLOR |
|
||||||
}, |
|
||||||
{ |
|
||||||
"id": 4, |
|
||||||
"title": "WASPANG ACTIVE", |
|
||||||
"key": "karyawan telat", |
|
||||||
"color": GREEN_COLOR |
|
||||||
}, |
|
||||||
{ |
|
||||||
"id": 5, |
|
||||||
"title": "WASPANG ABSENT", |
|
||||||
"key": "karyawan tanpa keterangan", |
|
||||||
"color": ORANGE_COLOR |
|
||||||
} |
|
||||||
] |
|
||||||
|
|
||||||
const defaultPersentaseProyek = { |
|
||||||
labels: [], |
|
||||||
datasets: [ |
|
||||||
{ |
|
||||||
label: 'Progress', |
|
||||||
data: [], |
|
||||||
fill: false, |
|
||||||
backgroundColor: 'rgb(54, 162, 235)', |
|
||||||
borderColor: 'rgba(54, 162, 235, 0.2)', |
|
||||||
yAxisID: 'y-axis-1', |
|
||||||
}, |
|
||||||
], |
|
||||||
}; |
|
||||||
|
|
||||||
const defaultCostProyek = { |
|
||||||
labels: [], |
|
||||||
datasets: [ |
|
||||||
{ |
|
||||||
label: 'Perencanaan', |
|
||||||
data: [], |
|
||||||
fill: false, |
|
||||||
backgroundColor: 'rgb(255, 99, 132)', |
|
||||||
borderColor: 'rgba(255, 99, 132, 0.2)', |
|
||||||
yAxisID: 'y-axis-1', |
|
||||||
}, |
|
||||||
{ |
|
||||||
label: 'Realisasi', |
|
||||||
data: [], |
|
||||||
fill: false, |
|
||||||
backgroundColor: 'rgb(54, 162, 235)', |
|
||||||
borderColor: 'rgba(54, 162, 235, 0.2)', |
|
||||||
yAxisID: 'y-axis-1', |
|
||||||
}, |
|
||||||
], |
|
||||||
}; |
|
||||||
|
|
||||||
const defaultStatusProyek = { |
|
||||||
labels: ['Aman', 'Alert', 'Critical'], |
|
||||||
datasets: [ |
|
||||||
{ |
|
||||||
label: '# of Votes', |
|
||||||
data: [], |
|
||||||
backgroundColor: [ |
|
||||||
'rgba(54, 162, 235, 0.2)', |
|
||||||
'rgba(255, 206, 86, 0.2)', |
|
||||||
'rgba(255, 99, 132, 0.2)', |
|
||||||
], |
|
||||||
borderColor: [ |
|
||||||
'rgba(54, 162, 235, 1)', |
|
||||||
'rgba(255, 206, 86, 1)', |
|
||||||
'rgba(255, 99, 132, 1)', |
|
||||||
], |
|
||||||
borderWidth: 1, |
|
||||||
}, |
|
||||||
], |
|
||||||
}; |
|
||||||
|
|
||||||
const DashboardSimpro = () => { |
|
||||||
const token = localStorage.getItem("token") |
|
||||||
const HEADER = { |
|
||||||
headers: { |
|
||||||
"Content-Type": "application/json", |
|
||||||
"Authorization": `Bearer ${token}` |
|
||||||
} |
|
||||||
} |
|
||||||
const [openPlanRealisasi, setopenPlanRealisasi] = useState(false); |
|
||||||
const [openCostPlanRealisasi, setopenCostPlanRealisasi] = useState(false); |
|
||||||
const [dataChart, setDataChart] = useState([]); |
|
||||||
const [projectTree, setProjectTree] = useState([]); |
|
||||||
const [allProyek, setAllProyek] = useState(true); |
|
||||||
const [dataStatusProyek, setDataStatusProyek] = useState(null); |
|
||||||
const [dataPersentaseProyek, setDataPersentaseProyek] = useState(null); |
|
||||||
const [dataCostProyek, setdataCostProyek] = useState(null); |
|
||||||
const [projectTreeVisible, setProjectTreeVisible] = useState(false); |
|
||||||
const [checkedKeysProjectTree, setCheckedKeysProjectTree] = useState([]); |
|
||||||
const [openModalTable, setOpenModalTable] = useState(false); |
|
||||||
const [dataDashboard, setDataDashboard] = useState(null); |
|
||||||
|
|
||||||
const handleGetDataDashboard = async () => { |
|
||||||
const URL = `${BASE_SIMPRO}/dashboard-proyek/search` |
|
||||||
const payload = { |
|
||||||
"columns": [ |
|
||||||
{ "name": "created_at", "logic_operator": "range", "value": "2021-11-06 00:00:00", "value1": "2021-11-06 23:59:59", "operator": "AND" } |
|
||||||
], |
|
||||||
"paging": { "start": 0, "length": -1 } |
|
||||||
} |
|
||||||
const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response) |
|
||||||
|
|
||||||
if (result.data.code !== 200) { |
|
||||||
NotificationManager.error('Gaga Menambah Data!!', 'Failed'); |
|
||||||
} |
|
||||||
|
|
||||||
setDataDashboard(result.data.data); |
|
||||||
} |
|
||||||
|
|
||||||
const handleGetDataDashboardChart = async () => { |
|
||||||
const URL = `${BASE_SIMPRO}/dashboard-status/search` |
|
||||||
let str = '' |
|
||||||
|
|
||||||
checkedKeysProjectTree.map((res, idx) => { |
|
||||||
if (idx == 0) str += `${res}` |
|
||||||
if (idx != 0) str += `,${res}` |
|
||||||
}) |
|
||||||
|
|
||||||
const payload = { |
|
||||||
"columns": [ |
|
||||||
{ "name": "id", "logic_operator": "in", "value": str ? str : "0", "operator": "AND" } |
|
||||||
], |
|
||||||
"orders": { "columns": ["nama"], "ascending": true } |
|
||||||
} |
|
||||||
|
|
||||||
const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response) |
|
||||||
|
|
||||||
if (result.data.code !== 200) { |
|
||||||
NotificationManager.error('Gaga Menambah Data!!', 'Failed'); |
|
||||||
} |
|
||||||
|
|
||||||
const { persentase_progress, progress_cost_planning, progress_cost_realisasi, status_proyek } = result.data.data |
|
||||||
const labelPersentaseProyek = persentase_progress ? persentase_progress.map(res => res.label) : [] |
|
||||||
const valuePersentaseProyek = persentase_progress ? persentase_progress.map(res => res.total) : [] |
|
||||||
const persentaseProyek = { |
|
||||||
labels: labelPersentaseProyek, |
|
||||||
datasets: [ |
|
||||||
{ |
|
||||||
label: 'Progress', |
|
||||||
data: valuePersentaseProyek, |
|
||||||
fill: false, |
|
||||||
backgroundColor: 'rgb(54, 162, 235)', |
|
||||||
borderColor: 'rgba(54, 162, 235, 0.2)', |
|
||||||
yAxisID: 'y-axis-1', |
|
||||||
}, |
|
||||||
], |
|
||||||
}; |
|
||||||
setDataPersentaseProyek(persentaseProyek) |
|
||||||
const labelCostPlaning = progress_cost_planning ? progress_cost_planning.map(res => res.label) : [] |
|
||||||
const valueCostPlaning = progress_cost_planning ? progress_cost_planning.map(res => res.total) : [] |
|
||||||
const valueCostRealisasi = progress_cost_realisasi ? progress_cost_realisasi.map(res => res.total) : [] |
|
||||||
|
|
||||||
const costProyek = { |
|
||||||
labels: labelCostPlaning, |
|
||||||
datasets: [ |
|
||||||
{ |
|
||||||
label: 'Perencanaan', |
|
||||||
data: valueCostPlaning, |
|
||||||
fill: false, |
|
||||||
backgroundColor: 'rgb(255, 99, 132)', |
|
||||||
borderColor: 'rgba(255, 99, 132, 0.2)', |
|
||||||
yAxisID: 'y-axis-1', |
|
||||||
}, |
|
||||||
{ |
|
||||||
label: 'Realisasi', |
|
||||||
data: valueCostRealisasi, |
|
||||||
fill: false, |
|
||||||
backgroundColor: 'rgb(54, 162, 235)', |
|
||||||
borderColor: 'rgba(54, 162, 235, 0.2)', |
|
||||||
yAxisID: 'y-axis-1', |
|
||||||
}, |
|
||||||
], |
|
||||||
}; |
|
||||||
setdataCostProyek(costProyek) |
|
||||||
|
|
||||||
const valueStatusProyek = status_proyek ? status_proyek.map(res => res.total) : [] |
|
||||||
|
|
||||||
const statusProyek = { |
|
||||||
labels: ['Aman', 'Alert', 'Critical'], |
|
||||||
datasets: [ |
|
||||||
{ |
|
||||||
label: '# of Votes', |
|
||||||
data: valueStatusProyek, |
|
||||||
backgroundColor: [ |
|
||||||
'rgba(54, 162, 235, 0.2)', |
|
||||||
'rgba(255, 206, 86, 0.2)', |
|
||||||
'rgba(255, 99, 132, 0.2)', |
|
||||||
], |
|
||||||
borderColor: [ |
|
||||||
'rgba(54, 162, 235, 1)', |
|
||||||
'rgba(255, 206, 86, 1)', |
|
||||||
'rgba(255, 99, 132, 1)', |
|
||||||
], |
|
||||||
borderWidth: 1, |
|
||||||
}, |
|
||||||
], |
|
||||||
}; |
|
||||||
|
|
||||||
setDataStatusProyek(statusProyek) |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
const getProyek = async () => { |
|
||||||
const URL = `${BASE_SIMPRO}/proyek/list?start=0&length=-1&orderby=nama&asc=true` |
|
||||||
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) |
|
||||||
if (result.data.code !== 200) { |
|
||||||
NotificationManager.error('Gaga Menambah Data!!', 'Failed'); |
|
||||||
} |
|
||||||
setProjectTree(result.data.data); |
|
||||||
const arr = result.data.data.map(res => res.id) |
|
||||||
setCheckedKeysProjectTree(arr) |
|
||||||
} |
|
||||||
|
|
||||||
const renderDailyInfo = () => { |
|
||||||
return ( |
|
||||||
<> |
|
||||||
<div className="daily-info-card" style={{ backgroundColor: IZIN_COLOR, cursor: 'pointer' }} > |
|
||||||
<p className="number-style1">{dataDashboard ? dataDashboard.panic_button : 0}</p> |
|
||||||
<p style={{ color: "#FFFFFF", fontWeight: "bold" }}>PANIC BUTTON</p> |
|
||||||
</div> |
|
||||||
<div className="daily-info-card" style={{ backgroundColor: GREEN_COLOR, cursor: 'pointer' }} > |
|
||||||
<p className="number-style1">{dataDashboard ? dataDashboard.waspang_status.presensi : 0}</p> |
|
||||||
<p style={{ color: "#FFFFFF", fontWeight: "bold" }}>WASPANG ACTIVE</p> |
|
||||||
</div> |
|
||||||
<div className="daily-info-card" style={{ backgroundColor: ORANGE_COLOR, cursor: 'pointer' }} > |
|
||||||
<p className="number-style1">{dataDashboard ? dataDashboard.waspang_status.absensi : 0}</p> |
|
||||||
<p style={{ color: "#FFFFFF", fontWeight: "bold" }}>WASPANG ABSENT</p> |
|
||||||
</div> |
|
||||||
</> |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
const handleClickChart = param => { |
|
||||||
if (!param.length) return; |
|
||||||
const { _datasetIndex, _index } = param[0]; |
|
||||||
const data = dataPersentaseProyek.datasets[_datasetIndex].label |
|
||||||
setOpenModalTable(true) |
|
||||||
} |
|
||||||
|
|
||||||
const handleClickProyek = id => { |
|
||||||
const arr = [...checkedKeysProjectTree] |
|
||||||
const idx = arr.indexOf(id) |
|
||||||
if (idx == -1) { |
|
||||||
arr.push(id) |
|
||||||
} else { |
|
||||||
arr.splice(idx, 1) |
|
||||||
} |
|
||||||
setCheckedKeysProjectTree(arr) |
|
||||||
} |
|
||||||
|
|
||||||
return ( |
|
||||||
<> |
|
||||||
<SiopasMap /> |
|
||||||
</> |
|
||||||
|
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
export default DashboardSimpro; |
|
Loading…
Reference in new issue