bnu
2 years ago
5 changed files with 551 additions and 1 deletions
@ -0,0 +1,106 @@ |
|||||||
|
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: ['Pengadaan Barang dan Jasa','Kontruksi', 'FTTH'], |
||||||
|
datasets: [ |
||||||
|
{ |
||||||
|
label: '# of Votes', |
||||||
|
data: [1, 1, 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} />; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,198 @@ |
|||||||
|
import React, {useEffect, useState} from 'react'; |
||||||
|
import { Row, Col, Select } from 'antd'; |
||||||
|
import TableDashboard from './tableDashboard'; |
||||||
|
import TableDashboardV1 from './tableDashboardv1'; |
||||||
|
import { BarChart, DoughnutChart } from './chartDashboard'; |
||||||
|
import axios from 'axios' |
||||||
|
import { NotificationContainer, NotificationManager } from 'react-notifications'; |
||||||
|
import { BASE_OSPRO } from '../../const/ApiConst'; |
||||||
|
|
||||||
|
function BoxDashboard({ value, title, secondaryTitle, icon, bgColor }) { |
||||||
|
return ( |
||||||
|
<div style={{ backgroundColor: bgColor }} className='box-dashboard-pm'> |
||||||
|
<Row> |
||||||
|
<Col span={7}> |
||||||
|
<div className='box-icon'> |
||||||
|
{icon} |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
<Col span={17}> |
||||||
|
<div className='box-content'> |
||||||
|
<div style={{ fontSize: '1.4rem' }} className='text-box'>{value}</div> |
||||||
|
<div style={{ fontSize: '0.7rem' }} className='text-box'>{title.toUpperCase()}</div> |
||||||
|
<div style={{ fontSize: '0.6rem' }} className='text-box-secondary'>{secondaryTitle && secondaryTitle.toUpperCase()}</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
const { Option } = Select; |
||||||
|
|
||||||
|
const DashbaoardPM = () => { |
||||||
|
const [PROJECTCOUNT, SET_PROJECTCOUNT] = useState([]) |
||||||
|
const [PROJECTBUDGETTOTAL, SET_PROJECTBUDGETTOTAL] = useState([]) |
||||||
|
const [PROJECTMANPOWER, SET_PROJETMANPOWER] = useState([]) |
||||||
|
const [PROJECTACTUALCOSTTOTAL, SET_PROJECTACTUALCOSTTOTAL] = useState([]) |
||||||
|
|
||||||
|
const token = localStorage.getItem("token") |
||||||
|
const HEADER = { |
||||||
|
headers: { |
||||||
|
"Content-Type": "application/json", |
||||||
|
"Authorization": `Bearer ${token}` |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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) { |
||||||
|
NotificationManager.error('Belum ada data proyek!', 'Failed'); |
||||||
|
} |
||||||
|
console.log("res ", result.data.data) |
||||||
|
// setDataTable(result.data.data);
|
||||||
|
SET_PROJECTCOUNT(result.data.totalRecord) |
||||||
|
SET_PROJECTACTUALCOSTTOTAL(result.data.totalActualCost) |
||||||
|
SET_PROJECTBUDGETTOTAL(result.data.totalPlannedCost) |
||||||
|
} |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
getProjects(); |
||||||
|
}, []) |
||||||
|
|
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<Row> |
||||||
|
<Col span={4}><h6 style={{ textAlign: 'center' }}>PORTOFOLIO DASHBOARD</h6></Col> |
||||||
|
{/* <Col span={5}> |
||||||
|
<Select |
||||||
|
style={{ width: '100%', padding: '0 5px' }} |
||||||
|
showSearch |
||||||
|
placeholder="Department" |
||||||
|
optionFilterProp="children" |
||||||
|
onChange={onChange} |
||||||
|
onSearch={onSearch} |
||||||
|
filterOption={(input, option) => |
||||||
|
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 |
||||||
|
} |
||||||
|
> |
||||||
|
<Option value="jack">Jack</Option> |
||||||
|
<Option value="lucy">Lucy</Option> |
||||||
|
<Option value="tom">Tom</Option> |
||||||
|
</Select> |
||||||
|
</Col> |
||||||
|
<Col span={5}> |
||||||
|
<Select |
||||||
|
style={{ width: '100%', padding: '0 5px' }} |
||||||
|
showSearch |
||||||
|
placeholder="Project Type" |
||||||
|
optionFilterProp="children" |
||||||
|
onChange={onChange} |
||||||
|
onSearch={onSearch} |
||||||
|
filterOption={(input, option) => |
||||||
|
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 |
||||||
|
} |
||||||
|
> |
||||||
|
{['Manned guarding ', 'C&T', 'CMS ', 'ESS', 'RSO'].map(res => ( |
||||||
|
<Option value={res}>{res}</Option> |
||||||
|
))} |
||||||
|
|
||||||
|
</Select> |
||||||
|
</Col> |
||||||
|
<Col span={5}> |
||||||
|
<Select |
||||||
|
style={{ width: '100%', padding: '0 5px' }} |
||||||
|
showSearch |
||||||
|
placeholder="Project Owner" |
||||||
|
optionFilterProp="children" |
||||||
|
onChange={onChange} |
||||||
|
onSearch={onSearch} |
||||||
|
filterOption={(input, option) => |
||||||
|
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 |
||||||
|
} |
||||||
|
> |
||||||
|
<Option value="jack">Jack</Option> |
||||||
|
<Option value="lucy">Lucy</Option> |
||||||
|
<Option value="tom">Tom</Option> |
||||||
|
</Select> |
||||||
|
</Col> |
||||||
|
<Col span={5}> |
||||||
|
<Select |
||||||
|
style={{ width: '100%', padding: '0 5px' }} |
||||||
|
showSearch |
||||||
|
placeholder="Governance Phase" |
||||||
|
optionFilterProp="children" |
||||||
|
onChange={onChange} |
||||||
|
onSearch={onSearch} |
||||||
|
filterOption={(input, option) => |
||||||
|
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 |
||||||
|
} |
||||||
|
> |
||||||
|
{['Initiation ', 'Excecution', 'Closing '].map(res => ( |
||||||
|
<Option value={res}>{res}</Option> |
||||||
|
))} |
||||||
|
</Select> |
||||||
|
</Col> */} |
||||||
|
</Row> |
||||||
|
|
||||||
|
<div style={{ paddingTop: 0 }}> |
||||||
|
<Row> |
||||||
|
<Col span={4}> |
||||||
|
<BoxDashboard |
||||||
|
icon={<i style={{ color: '#fff' }} class="zmdi zmdi-assignment zmdi-hc-3x"></i>} |
||||||
|
value={PROJECTCOUNT} |
||||||
|
bgColor="teal" |
||||||
|
title="Project Count" /> |
||||||
|
{/* <BoxDashboard |
||||||
|
icon={<i style={{ color: '#fff' }} class="zmdi zmdi-money-box zmdi-hc-3x"></i>} |
||||||
|
value="19 M" |
||||||
|
bgColor="#059669" |
||||||
|
title="Project Cost" |
||||||
|
secondaryTitle="(dollars)" /> */} |
||||||
|
<BoxDashboard |
||||||
|
icon={<i style={{ color: '#fff' }} class="zmdi zmdi-money zmdi-hc-3x"></i>} |
||||||
|
value={PROJECTBUDGETTOTAL} |
||||||
|
bgColor="#047857" |
||||||
|
title="Budget Project Total" |
||||||
|
secondaryTitle="(dollars)" /> |
||||||
|
<BoxDashboard |
||||||
|
icon={<i style={{ color: '#fff' }} class="zmdi zmdi-accounts-alt zmdi-hc-3x"></i>} |
||||||
|
value="2000" |
||||||
|
bgColor="#0284c7" |
||||||
|
title="Project Manpower" |
||||||
|
/> |
||||||
|
<BoxDashboard |
||||||
|
icon={<i style={{ color: '#fff' }} class="zmdi zmdi-account zmdi-hc-3x"></i>} |
||||||
|
value="540" |
||||||
|
bgColor="#0369a1" |
||||||
|
title="Manpower Variance" |
||||||
|
/> |
||||||
|
<BoxDashboard |
||||||
|
icon={<i style={{ color: '#fff' }} class="zmdi zmdi-alert-circle zmdi-hc-3x"></i>} |
||||||
|
value="80" |
||||||
|
bgColor="#b30000" |
||||||
|
title="Active Car" /> |
||||||
|
</Col> |
||||||
|
<Col span={20}> |
||||||
|
<div style={{ padding: '20px 0 20px 5px' }}> |
||||||
|
<Row> |
||||||
|
<Col span={12}> |
||||||
|
<BarChart /> |
||||||
|
</Col> |
||||||
|
<Col span={12}> |
||||||
|
<DoughnutChart /> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
<div style={{ paddingLeft: 5, }}> |
||||||
|
<TableDashboardV1 /> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default DashbaoardPM; |
@ -0,0 +1,130 @@ |
|||||||
|
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; |
@ -0,0 +1,115 @@ |
|||||||
|
import '../../../node_modules/react-grid-layout/css/styles.css'; |
||||||
|
import '../../../node_modules/react-resizable/css/styles.css'; |
||||||
|
import './../DashboardPMO/Dashboard.css'; |
||||||
|
// import PieChart from './PieChart';
|
||||||
|
import { BASE_OSPRO } from '../../const/ApiConst'; |
||||||
|
import ContentLoader from "react-content-loader" |
||||||
|
import React, { useEffect, useState } from 'react'; |
||||||
|
import axios from 'axios' |
||||||
|
import moment from 'moment'; |
||||||
|
import numeral from 'numeral'; |
||||||
|
import { Table, Tree, Row, Col, Space, Card} from 'antd'; |
||||||
|
import { Pie } from '@ant-design/plots'; |
||||||
|
import { NotificationContainer, NotificationManager } from 'react-notifications'; |
||||||
|
import { formatRibuanDecimal, DATE_TIME_FORMAT } from '../../const/CustomFunc.js'; |
||||||
|
import { Badge } from 'reactstrap'; |
||||||
|
import { Link } from "react-router-dom"; |
||||||
|
|
||||||
|
const token = localStorage.getItem("token") |
||||||
|
const HEADER = { |
||||||
|
headers: { |
||||||
|
"Content-Type": "application/json", |
||||||
|
"Authorization": `Bearer ${token}`, |
||||||
|
"Access-Control-Allow-Origin": "*" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const TableDashboardV1 = () => { |
||||||
|
const token = localStorage.getItem("token") |
||||||
|
const HEADER = { |
||||||
|
headers: { |
||||||
|
"Content-Type": "application/json", |
||||||
|
"Authorization": `Bearer ${token}` |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const [dataTable, setDataTable] = useState([]) |
||||||
|
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) { |
||||||
|
NotificationManager.error('Belum ada data proyek!', 'Failed'); |
||||||
|
} |
||||||
|
// console.log("res ", result.data.data)
|
||||||
|
setDataTable(result.data.data); |
||||||
|
} |
||||||
|
|
||||||
|
const columns = [ |
||||||
|
{ |
||||||
|
title: 'Project', |
||||||
|
dataIndex: 'nama', |
||||||
|
key: 'nama', |
||||||
|
render: (text, record) => <Link to={`/dashboard-project/${record.id}/${record.lastGanttId}`}>{text}</Link>, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Planned Interval', |
||||||
|
dataIndex: 'plannedInterval', |
||||||
|
key: 'plannedInterval', |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Budget Project', |
||||||
|
dataIndex: 'plannedCost', |
||||||
|
key: 'plannedCost', |
||||||
|
render: (text) => <a>{ formatRibuanDecimal(text) }</a>, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Actual Cost', |
||||||
|
dataIndex: 'actualCost', |
||||||
|
key: 'actualCost', |
||||||
|
render: (text) => <a>{ formatRibuanDecimal(text) }</a>, |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Cost Variance', |
||||||
|
dataIndex: 'costVariance', |
||||||
|
key: 'costVariance', |
||||||
|
render: (text) => <a>{ formatRibuanDecimal(text) }</a>, |
||||||
|
}, |
||||||
|
{ |
||||||
|
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> |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
title: 'Progress', |
||||||
|
dataIndex: 'progress', |
||||||
|
key: 'progress', |
||||||
|
}, |
||||||
|
]; |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
getProjects(); |
||||||
|
}, []) |
||||||
|
|
||||||
|
|
||||||
|
return ( |
||||||
|
<> |
||||||
|
<Row gutter={[16, 16]}> |
||||||
|
<Col span={24}> |
||||||
|
<Table columns={columns} dataSource={dataTable} /> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</> |
||||||
|
|
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default TableDashboardV1; |
Loading…
Reference in new issue