ardhi
2 years ago
11 changed files with 2006 additions and 500 deletions
@ -0,0 +1,30 @@ |
|||||||
|
.bottommodal-window-button-container { |
||||||
|
float: right; |
||||||
|
right: 10px; |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
|
||||||
|
.bottommodal-close, |
||||||
|
.bottommodal-maximize, |
||||||
|
.bottommodal-minimize { |
||||||
|
cursor: pointer; |
||||||
|
padding: 4px; |
||||||
|
} |
||||||
|
|
||||||
|
.bottommodal-header { |
||||||
|
margin-bottom: -10px; |
||||||
|
} |
||||||
|
|
||||||
|
.bottommodal-title { |
||||||
|
font-size: 16px; |
||||||
|
font-weight: 750; |
||||||
|
text-align: left; |
||||||
|
margin-left: 20px; |
||||||
|
color: #FFFFFF; |
||||||
|
} |
||||||
|
|
||||||
|
.bottommodal-close:hover, |
||||||
|
.bottommodal-maximize:hover, |
||||||
|
.bottommodal-minimize:hover { |
||||||
|
color: #20a8d8; |
||||||
|
} |
@ -0,0 +1,173 @@ |
|||||||
|
import React, { useMemo, useState } from 'react' |
||||||
|
import { Resizable } from 're-resizable' |
||||||
|
import './BottomModal.css' |
||||||
|
import { Card, CardBody, CardHeader, Col, Row, Table } from 'reactstrap' |
||||||
|
import Icon from '@iconify/react' |
||||||
|
import closeCircleOutline from '@iconify/icons-ion/close-circle-outline'; |
||||||
|
import removeCircleOutline from '@iconify/icons-ion/remove-circle-outline'; |
||||||
|
import windowMaximaze from '@iconify/icons-mdi/window-maximize'; |
||||||
|
|
||||||
|
const BottomModal = ({title, tableHeader, tableData, closeModal}) => { |
||||||
|
const [tableHeight, setTableHeight] = useState(150) |
||||||
|
const [resizableWidth, setResizableWidth] = useState("100%") |
||||||
|
const [resizableHeight, setResizableHeight] = useState(300) |
||||||
|
const [maximizeTitle, setMaximizeTitle] = useState("maximize") |
||||||
|
|
||||||
|
const setBottomTableWindow = (type) => { |
||||||
|
if (type === 'min') { |
||||||
|
// minimize
|
||||||
|
setResizableHeight(55); |
||||||
|
setTableHeight(150) |
||||||
|
} |
||||||
|
else if (type === 'max') { |
||||||
|
if (resizableHeight === '100vh') { |
||||||
|
// restore
|
||||||
|
setResizableHeight(300) |
||||||
|
setTableHeight(150) |
||||||
|
setMaximizeTitle("maximize"); |
||||||
|
} |
||||||
|
else { |
||||||
|
// maximize
|
||||||
|
// setResizableHeight(600)
|
||||||
|
// setTableHeight(950)
|
||||||
|
setResizableHeight('100vh'); |
||||||
|
setTableHeight('90vh'); |
||||||
|
setMaximizeTitle("restore"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const renderTableRow = (item) => { |
||||||
|
let row = []; |
||||||
|
// looping through its object keys (item is an object)
|
||||||
|
Object.keys(item).map(key => { |
||||||
|
// if (key !== 'id') {
|
||||||
|
row.push(<td>{item[key]}</td>) |
||||||
|
// }
|
||||||
|
}) |
||||||
|
return row; |
||||||
|
} |
||||||
|
|
||||||
|
const RenderTable = useMemo(() => { |
||||||
|
if (tableData && tableData.length > 0) { |
||||||
|
return ( |
||||||
|
<Table size='sm'> |
||||||
|
<thead style={{margin: 0, backgroundColor: '#888888', color: '#FFFFFF', fontSize: 12}}> |
||||||
|
<tr> |
||||||
|
<th>#</th> |
||||||
|
{tableHeader && tableHeader.length > 0 && |
||||||
|
tableHeader.map((item, idx) => ( |
||||||
|
<th key={idx}>{item.title}</th> |
||||||
|
)) |
||||||
|
} |
||||||
|
</tr> |
||||||
|
</thead> |
||||||
|
<tbody style={{backgroundColor: '#EEEEEE'}}> |
||||||
|
{tableData && tableData.length > 0 && |
||||||
|
tableData.map((item, index) => { |
||||||
|
return (<tr key={index}> |
||||||
|
{renderTableRow(item, tableHeader)} |
||||||
|
</tr>) |
||||||
|
}) |
||||||
|
} |
||||||
|
</tbody> |
||||||
|
</Table> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
// <Table>
|
||||||
|
// <tr>
|
||||||
|
// <td style={{ fontWeight: "bold" }} colSpan="4" align="center">No Data Available</td>
|
||||||
|
// </tr>
|
||||||
|
// </Table>
|
||||||
|
<div style={{justifyContent: 'center', textAlign: 'center', color: 'red', marginTop: 50, marginBottom: 50}}> |
||||||
|
No Data Available |
||||||
|
</div> |
||||||
|
) |
||||||
|
}, [tableData]) |
||||||
|
|
||||||
|
return ( |
||||||
|
<Resizable |
||||||
|
style={{ |
||||||
|
position: "fixed", |
||||||
|
width: "100%", |
||||||
|
bottom: 0, |
||||||
|
left: 0, |
||||||
|
right: 0, |
||||||
|
background: "#222222", |
||||||
|
borderTopLeftRadius: 10, |
||||||
|
borderTopRightRadius: 10, |
||||||
|
zIndex: 9999 |
||||||
|
}} |
||||||
|
size={{ |
||||||
|
width: resizableWidth, |
||||||
|
height: resizableHeight |
||||||
|
}} |
||||||
|
maxHeight="100vh" |
||||||
|
minWidth="100%" |
||||||
|
enable={{ top: true, right: false, bottom: false, left: false, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false }} |
||||||
|
onResizeStop={(e, direction, ref, d) => { |
||||||
|
setResizableWidth("100%"); |
||||||
|
setResizableHeight(resizableHeight + d.height); |
||||||
|
if (e.screenY < 100) { // max
|
||||||
|
setResizableHeight('100vh'); |
||||||
|
setTableHeight(450) |
||||||
|
} |
||||||
|
else if (e.screenY < 150) { |
||||||
|
setTableHeight(400) |
||||||
|
} |
||||||
|
else if (e.screenY < 200) { |
||||||
|
setTableHeight(350) |
||||||
|
} |
||||||
|
else if (e.screenY < 250) { |
||||||
|
setTableHeight(300) |
||||||
|
} |
||||||
|
else if (e.screenY < 300) { |
||||||
|
setTableHeight(250) |
||||||
|
} |
||||||
|
else if (e.screenY < 400) { |
||||||
|
setTableHeight(200) |
||||||
|
} |
||||||
|
else if (e.screenY < 600) { |
||||||
|
setTableHeight(150) // default
|
||||||
|
} |
||||||
|
else if (e.screenY >= 600) { |
||||||
|
setBottomTableWindow('min'); |
||||||
|
} |
||||||
|
}} |
||||||
|
> |
||||||
|
<div style={{ paddingTop: 10, position: "relative", height: "100%" }}> |
||||||
|
<Row className="bottommodal-header"> |
||||||
|
<Col md={8}> |
||||||
|
<p className="bottommodal-title">{title}</p> |
||||||
|
</Col> |
||||||
|
<Col md={4} className="bottommodal-window-button-container"> |
||||||
|
<span className="bottommodal-minimize" title="minimize" |
||||||
|
onClick={() => setBottomTableWindow("min")} |
||||||
|
> |
||||||
|
<Icon icon={removeCircleOutline} width={30} height={30} color="#FFFFFF" /> |
||||||
|
</span> |
||||||
|
<span className="bottommodal-maximize" title={maximizeTitle} |
||||||
|
onClick={() => setBottomTableWindow("max")} |
||||||
|
> |
||||||
|
<Icon icon={windowMaximaze} width={30} height={30} color="#FFFFFF" /> |
||||||
|
</span> |
||||||
|
<span className="bottommodal-close" title="close" |
||||||
|
onClick={closeModal} |
||||||
|
> |
||||||
|
<Icon icon={closeCircleOutline} width={30} height={30} color="#FFFFFF" /> |
||||||
|
</span> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<Card style={{ margin: 10 }}> |
||||||
|
<CardBody style={{ margin: 0, padding: 0, borderRadius: 5, backgroundColor: '#EEEEEE', overflowY: 'auto', height: resizableHeight === 300 ? 240 : 540 }}> |
||||||
|
{RenderTable} |
||||||
|
</CardBody> |
||||||
|
</Card> |
||||||
|
</div> |
||||||
|
</Resizable> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export default BottomModal; |
@ -0,0 +1,7 @@ |
|||||||
|
{ |
||||||
|
"name": "BottomModal", |
||||||
|
"version": "0.0.0", |
||||||
|
"private": true, |
||||||
|
"main": "./BottomModal.js" |
||||||
|
} |
||||||
|
|
@ -0,0 +1,361 @@ |
|||||||
|
import React, { useEffect, useState } from 'react'; |
||||||
|
import { |
||||||
|
Chart as ChartJS, |
||||||
|
CategoryScale, |
||||||
|
LineController, |
||||||
|
LineElement, |
||||||
|
LinearScale, |
||||||
|
BarController, |
||||||
|
BarElement, |
||||||
|
PieController, |
||||||
|
Title, |
||||||
|
Tooltip, |
||||||
|
Legend, |
||||||
|
ArcElement |
||||||
|
} from 'chart.js'; |
||||||
|
// import Chart from 'chart.js/auto';
|
||||||
|
import { Chart, Bar, Line, Pie } from 'react-chartjs-2'; |
||||||
|
import ChartDataLabels from 'chartjs-plugin-datalabels'; |
||||||
|
import ContentLoader from 'react-content-loader'; |
||||||
|
|
||||||
|
ChartJS.register( |
||||||
|
CategoryScale, |
||||||
|
LineController, |
||||||
|
LineElement, |
||||||
|
LinearScale, |
||||||
|
BarController, |
||||||
|
BarElement, |
||||||
|
PieController, |
||||||
|
Title, |
||||||
|
Tooltip, |
||||||
|
Legend, |
||||||
|
ArcElement, |
||||||
|
ChartDataLabels |
||||||
|
); |
||||||
|
|
||||||
|
const styles = { |
||||||
|
cardContainer: { backgroundColor: '#F8F8F8', margin: 2, paddingLeft: 20, paddingRight: 20, paddingTop: 10, height: '30vh' }, |
||||||
|
cardHeaderContainer: { display: 'flex', flexDirection: 'row', marginBottom: 10 }, |
||||||
|
cardChartContainer: { position: 'relative', height: '20vh', margin: 'auto', paddingBottom: 10, justifyContent: 'center' }, |
||||||
|
cardTitle: { color: '#444444', fontSize: 14, fontWeight: 'bold' }, |
||||||
|
cardSubtitle: { color: '#888888', fontSize: 12 } |
||||||
|
} |
||||||
|
|
||||||
|
const verticalBarChartOption = { |
||||||
|
elements: { |
||||||
|
bar: { |
||||||
|
borderWidth: 2, |
||||||
|
}, |
||||||
|
}, |
||||||
|
indexAxis: 'x', |
||||||
|
responsive: true, |
||||||
|
maintainAspectRatio: false, |
||||||
|
plugins: { |
||||||
|
legend: { |
||||||
|
display: true, |
||||||
|
position: 'right', |
||||||
|
labels: { |
||||||
|
boxWidth: 10 |
||||||
|
} |
||||||
|
}, |
||||||
|
datalabels: { |
||||||
|
color: '#FFFFFF' |
||||||
|
} |
||||||
|
}, |
||||||
|
// scales: {
|
||||||
|
// yAxes: [{
|
||||||
|
// afterFit: function(scaleInstance) {
|
||||||
|
// scaleInstance.width = 100; // sets the width to 100px
|
||||||
|
// }
|
||||||
|
// }]
|
||||||
|
// }
|
||||||
|
} |
||||||
|
|
||||||
|
const horizontalBarChartOption = { |
||||||
|
indexAxis: 'y', |
||||||
|
elements: { |
||||||
|
bar: { |
||||||
|
borderWidth: 2, |
||||||
|
}, |
||||||
|
}, |
||||||
|
responsive: true, |
||||||
|
maintainAspectRatio: false, |
||||||
|
plugins: { |
||||||
|
legend: { |
||||||
|
display: false, |
||||||
|
}, |
||||||
|
datalabels: { |
||||||
|
color: '#FFFFFF' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const pieChartOption = { |
||||||
|
elements: { |
||||||
|
pie: { |
||||||
|
borderWidth: 2, |
||||||
|
}, |
||||||
|
}, |
||||||
|
responsive: true, |
||||||
|
maintainAspectRatio: false, |
||||||
|
plugins: { |
||||||
|
legend: { |
||||||
|
display: true, |
||||||
|
position: 'right', |
||||||
|
labels: { |
||||||
|
boxWidth: 10 |
||||||
|
} |
||||||
|
}, |
||||||
|
datalabels: { |
||||||
|
color: '#FFFFFF' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const lineChartOption = { |
||||||
|
elements: { |
||||||
|
line: { |
||||||
|
borderWidth: 2, |
||||||
|
}, |
||||||
|
}, |
||||||
|
responsive: false, |
||||||
|
plugins: { |
||||||
|
datalabels: { |
||||||
|
color: '#FFFFFF' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const optionsExpenditure = { |
||||||
|
indexAxis: 'y', |
||||||
|
elements: { |
||||||
|
bar: { |
||||||
|
borderWidth: 2, |
||||||
|
}, |
||||||
|
}, |
||||||
|
responsive: true, |
||||||
|
maintainAspectRatio: false, |
||||||
|
plugins: { |
||||||
|
legend: { |
||||||
|
display: false, |
||||||
|
position: 'right', |
||||||
|
labels: { |
||||||
|
boxWidth: 10 |
||||||
|
} |
||||||
|
}, |
||||||
|
datalabels: { |
||||||
|
color: '#FFFFFF' |
||||||
|
} |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
const optionsScheduleHealthPerDivision = { |
||||||
|
elements: { |
||||||
|
bar: { |
||||||
|
borderWidth: 2, |
||||||
|
}, |
||||||
|
}, |
||||||
|
indexAxis: 'x', |
||||||
|
responsive: true, |
||||||
|
maintainAspectRatio: false, |
||||||
|
plugins: { |
||||||
|
legend: { |
||||||
|
display: true, |
||||||
|
position: 'top', |
||||||
|
labels: { |
||||||
|
boxWidth: 10, |
||||||
|
font: { |
||||||
|
size: 10 |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
datalabels: { |
||||||
|
color: '#FFFFFF' |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export const ContentLoaderChart = ({type}) => { |
||||||
|
if (type === 'vertical-bar') { |
||||||
|
return ( |
||||||
|
<ContentLoader |
||||||
|
speed={2} |
||||||
|
width="100%" |
||||||
|
height="100%" |
||||||
|
viewBox="0 0 500 200" |
||||||
|
backgroundColor="#f3f3f3" |
||||||
|
foregroundColor="#ecebeb" |
||||||
|
> |
||||||
|
{/* <rect x="6" y="88" rx="4" ry="4" width="18" height="105" />
|
||||||
|
<rect x="36" y="64" rx="4" ry="4" width="18" height="129" />
|
||||||
|
<rect x="64" y="130" rx="4" ry="4" width="18" height="62" />
|
||||||
|
<rect x="91" y="87" rx="4" ry="4" width="18" height="105" />
|
||||||
|
<rect x="118" y="107" rx="4" ry="4" width="18" height="85" />
|
||||||
|
<rect x="147" y="92" rx="4" ry="4" width="18" height="100" />
|
||||||
|
<rect x="176" y="120" rx="4" ry="4" width="18" height="72" />
|
||||||
|
<rect x="207" y="54" rx="4" ry="4" width="18" height="138" />
|
||||||
|
<rect x="267" y="94" rx="4" ry="4" width="18" height="98" />
|
||||||
|
<rect x="235" y="56" rx="4" ry="4" width="18" height="136" />
|
||||||
|
<rect x="298" y="68" rx="4" ry="4" width="18" height="124" />
|
||||||
|
<rect x="330" y="77" rx="4" ry="4" width="18" height="114" />
|
||||||
|
<rect x="360" y="106" rx="4" ry="4" width="18" height="84" />
|
||||||
|
<rect x="388" y="155" rx="4" ry="4" width="18" height="35" />
|
||||||
|
<rect x="417" y="108" rx="4" ry="4" width="18" height="81" />
|
||||||
|
<rect x="450" y="82" rx="4" ry="4" width="18" height="107" /> */} |
||||||
|
{/* <rect x="13" y="47" rx="4" ry="4" width="39" height="140" />
|
||||||
|
<rect x="61" y="9" rx="4" ry="4" width="39" height="178" />
|
||||||
|
<rect x="112" y="28" rx="4" ry="4" width="39" height="160" />
|
||||||
|
<rect x="186" y="45" rx="4" ry="4" width="39" height="140" />
|
||||||
|
<rect x="234" y="7" rx="4" ry="4" width="39" height="178" />
|
||||||
|
<rect x="284" y="27" rx="4" ry="4" width="39" height="160" />
|
||||||
|
<rect x="358" y="45" rx="4" ry="4" width="39" height="140" />
|
||||||
|
<rect x="406" y="7" rx="4" ry="4" width="39" height="178" />
|
||||||
|
<rect x="456" y="27" rx="4" ry="4" width="39" height="160" /> */} |
||||||
|
<rect x="13" y="87" rx="4" ry="4" width="39" height="106" />
|
||||||
|
<rect x="61" y="21" rx="4" ry="4" width="39" height="171" />
|
||||||
|
<rect x="112" y="61" rx="4" ry="4" width="39" height="131" />
|
||||||
|
<rect x="185" y="87" rx="4" ry="4" width="39" height="106" />
|
||||||
|
<rect x="233" y="21" rx="4" ry="4" width="39" height="171" />
|
||||||
|
<rect x="284" y="61" rx="4" ry="4" width="39" height="131" />
|
||||||
|
<rect x="354" y="88" rx="4" ry="4" width="39" height="106" />
|
||||||
|
<rect x="402" y="22" rx="4" ry="4" width="39" height="171" />
|
||||||
|
<rect x="453" y="62" rx="4" ry="4" width="39" height="131" /> |
||||||
|
</ContentLoader> |
||||||
|
) |
||||||
|
} |
||||||
|
else if (type === 'pie') { |
||||||
|
return ( |
||||||
|
<ContentLoader |
||||||
|
speed={2} |
||||||
|
width="100%" |
||||||
|
height="100%" |
||||||
|
viewBox="0 0 200 100" |
||||||
|
backgroundColor="#f3f3f3" |
||||||
|
foregroundColor="#ecebeb" |
||||||
|
> |
||||||
|
<circle cx="45" cy="41" r="39" />
|
||||||
|
<rect x="98" y="15" rx="8" ry="8" width="82" height="13" /> |
||||||
|
<rect x="98" y="35" rx="8" ry="8" width="82" height="13" />
|
||||||
|
<rect x="98" y="56" rx="8" ry="8" width="82" height="13" /> |
||||||
|
</ContentLoader> |
||||||
|
) |
||||||
|
} |
||||||
|
else { |
||||||
|
return ( |
||||||
|
<ContentLoader |
||||||
|
speed={2} |
||||||
|
width="100%" |
||||||
|
height="100%" |
||||||
|
viewBox="0 0 200 100" |
||||||
|
backgroundColor="#f3f3f3" |
||||||
|
foregroundColor="#ecebeb" |
||||||
|
> |
||||||
|
<rect x="0" y="0" rx="10" ry="10" width="25%" height="20" /> |
||||||
|
<rect x="0" y="25" rx="10" ry="10" width="75%" height="20" /> |
||||||
|
<rect x="0" y="50" rx="10" ry="10" width="100%" height="20" /> |
||||||
|
<rect x="0" y="75" rx="10" ry="10" width="45%" height="20" /> |
||||||
|
</ContentLoader> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// export const FullCard =
|
||||||
|
|
||||||
|
export const CardDashboard = ({ title, subtitle, chartType, chartData, chartOption, isReady }) => { |
||||||
|
let chart = null; |
||||||
|
if (chartType === 'vertical-bar') { |
||||||
|
chart = <Bar |
||||||
|
options={chartOption ? chartOption : verticalBarChartOption} |
||||||
|
data={chartData ? chartData : null} |
||||||
|
/> |
||||||
|
} |
||||||
|
else if (chartType === 'horizontal-bar') { |
||||||
|
chart = <Bar |
||||||
|
options={chartOption ? chartOption : horizontalBarChartOption} |
||||||
|
data={chartData ? chartData : null} |
||||||
|
/> |
||||||
|
} |
||||||
|
else if (chartType === 'pie') { |
||||||
|
chart = <Pie |
||||||
|
options={chartOption ? chartOption : pieChartOption} |
||||||
|
data={chartData ? chartData : null} |
||||||
|
/> |
||||||
|
} |
||||||
|
else if (chartType === 'line') { |
||||||
|
chart = <Line |
||||||
|
options={chartOption ? chartOption : lineChartOption} |
||||||
|
data={chartData ? chartData : null} |
||||||
|
/> |
||||||
|
} |
||||||
|
|
||||||
|
return ( |
||||||
|
<div style={styles.cardContainer}> |
||||||
|
<div style={styles.cardHeaderContainer}> |
||||||
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
||||||
|
<div style={styles.cardTitle}>{title}</div> |
||||||
|
<div style={styles.cardSubtitle}>{subtitle}</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div style={styles.cardChartContainer}> |
||||||
|
{ isReady ?
|
||||||
|
chartData ? chart : <NoDataChart />
|
||||||
|
: |
||||||
|
<ContentLoaderChart type={chartType}/> |
||||||
|
} |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
// export const CardExpenditure = ({ title, subtitle, chartData, chartOption }) => {
|
||||||
|
// return (
|
||||||
|
// <div style={styles.cardContainer}>
|
||||||
|
// <div style={styles.cardHeaderContainer}>
|
||||||
|
// <div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}>
|
||||||
|
// <div style={styles.cardTitle}>{title}</div>
|
||||||
|
// <div style={styles.cardSubtitle}>{subtitle}</div>
|
||||||
|
// </div>
|
||||||
|
// <div style={{ flex: 6, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||||||
|
// <div style={{ backgroundColor: '#DDDDDD', color: '#4C4747', borderRadius: 5, padding: 4, fontWeight: 500, cursor: 'pointer', fontSize: 12, textAlign: 'center', lineHeight: 'normal' }}>Detailed View</div>
|
||||||
|
// </div>
|
||||||
|
// </div>
|
||||||
|
|
||||||
|
// <div style={styles.cardChartContainer}>
|
||||||
|
// <Bar options={chartOption ? chartOption : optionsExpenditure} data={chartData ? chartData : null} />
|
||||||
|
// </div>
|
||||||
|
|
||||||
|
// </div>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
export const CardScheduleHealthPerDivision = ({ isReady, title, subtitle, mode, changeMode, chartData, chartOption, chartType }) => { |
||||||
|
return ( |
||||||
|
<div style={{ backgroundColor: '#F8F8F8', margin: 2, paddingLeft: 20, paddingRight: 20, paddingTop: 10, height: '30vh' }}> |
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: 10 }}> |
||||||
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
||||||
|
<div style={styles.cardTitle}>{title}</div> |
||||||
|
<div style={styles.cardSubtitle}>{subtitle}</div> |
||||||
|
</div> |
||||||
|
<div style={{ flex: 6, display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center' }}> |
||||||
|
<div style={{ width: '100%', textAlign: 'center', height: 24, fontSize: 12, verticalAlign: 'middle', cursor: 'pointer', color: '#FFFFFF', backgroundColor: mode === 'budget' ? '#52AC0B' : '#777777' }} onClick={() => changeMode('budget')}>Budget</div> |
||||||
|
<div style={{ width: '100%', textAlign: 'center', height: 24, fontSize: 12, verticalAlign: 'middle', cursor: 'pointer', color: '#FFFFFF', backgroundColor: mode === 'schedule' ? '#52AC0B' : '#777777' }} onClick={() => changeMode('schedule')}>Schedule</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div style={{ position: 'relative', height: '20vh', margin: 'auto', paddingBottom: 10, justifyContent: 'center' }}> |
||||||
|
{isReady ? chartData ? <Bar options={chartOption ? chartOption : optionsScheduleHealthPerDivision} data={chartData ? chartData : null} /> : <NoDataChart /> : <ContentLoaderChart type={chartType}/> } |
||||||
|
</div> |
||||||
|
|
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
export const NoDataChart = ({message}) => { |
||||||
|
return ( |
||||||
|
<div style={{textAlign: 'center', marginTop: 50, alignSelf: 'center', alignItems: 'center', justifyContent: 'center', color: 'red'}}> |
||||||
|
{message ? message : "No Data Available"} |
||||||
|
</div> |
||||||
|
) |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
{ |
||||||
|
"name": "CardDashboard", |
||||||
|
"version": "0.0.0", |
||||||
|
"private": true, |
||||||
|
"main": "./CardDashboard.js" |
||||||
|
} |
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,244 @@ |
|||||||
|
import React, { useEffect, useMemo, useRef, useState } from 'react'; |
||||||
|
import axios from 'axios' |
||||||
|
import { Row, Col } from 'antd'; |
||||||
|
import { CardDashboard, CardExpenditure, CardScheduleHealthPerDivision } from '../../components/CardDashboard/CardDashboard'; |
||||||
|
import L from 'leaflet'; |
||||||
|
import { useParams } from 'react-router-dom'; |
||||||
|
import '../../assets/css/customscroll.css' |
||||||
|
import moment from 'moment'; |
||||||
|
import { BASE_OSPRO } from '../../const/ApiConst'; |
||||||
|
|
||||||
|
const styles = { |
||||||
|
cardContainer: { backgroundColor: '#F8F8F8', margin: 2, paddingLeft: 20, paddingRight: 20, paddingTop: 10 }, |
||||||
|
cardHeaderContainer: { display: 'flex', flexDirection: 'row', marginBottom: 10 }, |
||||||
|
cardChartContainer: { position: 'relative', height: '21vh', margin: 'auto', paddingBottom: 10, justifyContent: 'center' }, |
||||||
|
cardTitle: { color: '#444444', fontSize: 16, fontWeight: 'bold' }, |
||||||
|
cardSubtitle: { color: '#888888', fontSize: 12 } |
||||||
|
} |
||||||
|
|
||||||
|
const center = { |
||||||
|
lat: -6.200000, |
||||||
|
lng: 106.816666 |
||||||
|
} |
||||||
|
|
||||||
|
const DashboardCustomer = () => { |
||||||
|
const token = localStorage.getItem("token") |
||||||
|
const HEADER = { |
||||||
|
headers: { |
||||||
|
"Content-Type": "application/json", |
||||||
|
"Authorization": `Bearer ${token}` |
||||||
|
} |
||||||
|
} |
||||||
|
const { PROJECT_ID, GANTT_ID } = useParams(); |
||||||
|
// const URL_GANTT = `http://103.73.125.81:8446/index.html?base_url=http://103.73.125.81:8444/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`;
|
||||||
|
const URL_GANTT = `http://103.73.125.81:8446/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`; |
||||||
|
const mapRef = useRef() |
||||||
|
const [projectName, setProjectName] = useState("Project Tower ABC"); |
||||||
|
const [customerName, setCustomerName] = useState("Jaya Gedung Group"); |
||||||
|
const [plannedStart, setPlannedStart] = useState("2019-03-04") |
||||||
|
const [plannedFinish, setPlannedFinish] = useState("2020-05-28") |
||||||
|
const [actualStart, setActualStart] = useState("2019-03-04") |
||||||
|
const [actualFinish, setActualFinish] = useState("2020-05-28") |
||||||
|
const [estimatedFinish, setEstimatedFinish] = useState("2020-05-28") |
||||||
|
const [mymap, setMymap] = useState(null); |
||||||
|
const [activeTabIdx, setActiveTabIdx] = useState(0); |
||||||
|
const [activeTabCommentIdx, setActiveTabCommentIdx] = useState(0); |
||||||
|
const [planningProgress, setPlanningProgress] = useState(79); |
||||||
|
const [actualProgress, setActualProgress] = useState(50); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('URL_GANTT', URL_GANTT); |
||||||
|
return () => { |
||||||
|
console.log('unmount RenderMap'); |
||||||
|
} |
||||||
|
}, []) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (activeTabIdx === 1) { |
||||||
|
initMap(); |
||||||
|
} |
||||||
|
}, [activeTabIdx]); |
||||||
|
|
||||||
|
const initMap = () => { |
||||||
|
let mymap = L.map('map-area', { |
||||||
|
center: center, |
||||||
|
zoom: 13 |
||||||
|
}) |
||||||
|
|
||||||
|
setMymap(mymap); |
||||||
|
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}).addTo(mymap); |
||||||
|
} |
||||||
|
|
||||||
|
const RenderGantt = useMemo(() => ( |
||||||
|
<iframe |
||||||
|
id="frame-gantt" |
||||||
|
src={URL_GANTT} |
||||||
|
style={{ |
||||||
|
width: '100%', |
||||||
|
height: '100%', |
||||||
|
}} |
||||||
|
scrolling="no" |
||||||
|
frameBorder="0" |
||||||
|
allow="fullscreen" |
||||||
|
></iframe> |
||||||
|
), [activeTabIdx]) |
||||||
|
|
||||||
|
const Comment = ({name, division, message}) => ( |
||||||
|
<div style={{backgroundColor: '#EEEEEE', border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 5, marginBottom: 5, marginRight: 2, marginLeft: 2}}> |
||||||
|
<div style={{fontWeight: 'bold'}}>{name}</div> |
||||||
|
<div>{division}</div> |
||||||
|
<div style={{color: '#E80053', textAlign: 'right', fontWeight: 500, fontSize: 12}}>{message}</div> |
||||||
|
</div> |
||||||
|
) |
||||||
|
|
||||||
|
return ( |
||||||
|
<div style={{ marginLeft: -25, marginRight: -25 }}> |
||||||
|
<Row> |
||||||
|
<Col span={18}> |
||||||
|
<Row> |
||||||
|
<Col span={12}> |
||||||
|
{/* <div style={styles.cardContainer}> |
||||||
|
<div style={styles.cardHeaderContainer}> |
||||||
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
||||||
|
<div style={styles.cardTitle}>Project</div> |
||||||
|
<div style={styles.cardSubtitle}>Project Tower ABC</div> |
||||||
|
</div> |
||||||
|
<div style={{ flex: 6, display: 'flex', justifyContent: 'center', alignItems: 'center' }}> |
||||||
|
<i className="fa fa-check-square" style={{fontSize: 28}}></i> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> */} |
||||||
|
<div style={{border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 10, margin: 2}}> |
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: 10 }}> |
||||||
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
||||||
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Project</div> |
||||||
|
<div style={{fontSize: 14}}>{projectName}</div> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<i className="fa fa-check-square" style={{fontSize: 28}}></i> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
</Col> |
||||||
|
<Col span={12}> |
||||||
|
<div style={{border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 10, margin: 2}}> |
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: 10 }}> |
||||||
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
||||||
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Customer</div> |
||||||
|
<div style={{fontSize: 14}}>{customerName}</div> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<i className="fa fa-home" style={{fontSize: 28}}></i> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={24}> |
||||||
|
<div style={{border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 10, margin: 2}}> |
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', marginBottom: 10 }}> |
||||||
|
<div> |
||||||
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Schedule</div> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<Row> |
||||||
|
<Col span={4} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Planned Start</Col> |
||||||
|
<Col span={4}>{plannedStart ? moment(plannedStart).format('D MMMM YYYY') : '-'}</Col> |
||||||
|
<Col span={4} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Planned Finish</Col> |
||||||
|
<Col span={4}>{plannedFinish ? moment(plannedFinish).format('D MMMM YYYY') : '-'}</Col> |
||||||
|
<Col span={4} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Actual Finish</Col> |
||||||
|
<Col span={4}>{actualFinish ? moment(actualFinish).format('D MMMM YYYY') : '-'}</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={4} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Actual Start</Col> |
||||||
|
<Col span={4}>{actualStart ? moment(actualStart).format('D MMMM YYYY') : '-'}</Col> |
||||||
|
<Col span={4} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Estimated Finish</Col> |
||||||
|
<Col span={4}>{estimatedFinish ? moment(estimatedFinish).format('D MMMM YYYY') : '-'}</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<div style={{margin: 2, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD'}}> |
||||||
|
<Row> |
||||||
|
<Col span={12}> |
||||||
|
<div style={{backgroundColor: activeTabIdx === 0 ? '#FFFFFF' : '#D9D9D9', padding: 8, fontWeight: 'bold', cursor: 'pointer', color: activeTabIdx === 0 ? '#000000' : '#4E4C4C'}} onClick={() => setActiveTabIdx(0)}>S Curve</div> |
||||||
|
</Col> |
||||||
|
<Col span={12}> |
||||||
|
<div style={{backgroundColor: activeTabIdx === 1 ? '#FFFFFF' : '#D9D9D9', padding: 8, fontWeight: 'bold', cursor: 'pointer', color: activeTabIdx === 1 ? '#000000' : '#4E4C4C'}} onClick={() => setActiveTabIdx(1)}>Maps</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={24}> |
||||||
|
<div style={{maxHeight: '57vh'}}> |
||||||
|
{ activeTabIdx === 0 && <div style={{height: '56vh', width: '100%'}}>{RenderGantt}</div> } |
||||||
|
{ activeTabIdx === 1 && <div id="map-area" style={{height: '56vh'}} ref={mapRef}></div> } |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
<Col span={6}> |
||||||
|
<div style={{margin: 2}}> |
||||||
|
<Row> |
||||||
|
<Col span={24}> |
||||||
|
<div style={{backgroundColor: '#222222', padding: 10, marginBottom: 5}}> |
||||||
|
<div style={{color: '#FFFFFF', textAlign: 'center', marginBottom: 10, fontWeight: 'bold'}}>Progress</div> |
||||||
|
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> |
||||||
|
<div style={{backgroundColor: '#7209B7', width: `${planningProgress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: planningProgress < 100 ? 0 : 10, borderBottomRightRadius: planningProgress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500}}>Planning : {planningProgress}%</div> |
||||||
|
</div> |
||||||
|
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> |
||||||
|
<div style={{backgroundColor: '#0059C9', width: `${actualProgress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: actualProgress < 100 ? 0 : 10, borderBottomRightRadius: actualProgress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500}}>{actualProgress && actualProgress < 50 ? `${actualProgress}%` : `Actual : ${actualProgress}%` }</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={24}> |
||||||
|
<div style={{backgroundColor: '#FFFFFF', padding: 10, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', marginBottom: 5}}> |
||||||
|
<div style={{color: '#000000', textAlign: 'center', marginBottom: 10, fontWeight: 'bold'}}>Health By Schedule</div> |
||||||
|
<div style={{backgroundColor: '#E80053', color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10}}> |
||||||
|
<div style={{textAlign: 'center', fontWeight: 500}}>Behind Schedule</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={24}> |
||||||
|
<div style={{border: 'solid', borderWidth: 1, borderColor: '#DDDDDD'}}> |
||||||
|
{/* <div style={{display: 'flex', alignItems: 'center'}}> |
||||||
|
<div style={{flex: 1, margin: 'auto', cursor: 'pointer', textAlign: 'center', backgroundColor: activeTabCommentIdx === 0 ? '#FFFFFF' : '#D9D9D9', color: activeTabCommentIdx === 0 ? '#000000' : '#FFFFFF', fontWeight: 500, padding: 2}} onClick={() => setActiveTabCommentIdx(0)}>Behind Task</div> |
||||||
|
<div style={{flex: 1, margin: 'auto', cursor: 'pointer', textAlign: 'center', backgroundColor: activeTabCommentIdx === 1 ? '#FFFFFF' : '#D9D9D9', color: activeTabCommentIdx === 1 ? '#000000' : '#FFFFFF', fontWeight: 500, padding: 2, fontSize: 12}} onClick={() => setActiveTabCommentIdx(1)}>Comment From Customer</div> |
||||||
|
</div> */} |
||||||
|
<Row style={{alignItems: 'center', marginBottom: 5}}> |
||||||
|
<Col span={12}> |
||||||
|
<div style={{backgroundColor: activeTabCommentIdx === 0 ? '#FFFFFF' : '#D9D9D9', fontWeight: 500, textAlign: 'center', fontSize: 12, padding: 2, cursor: 'pointer', color: activeTabCommentIdx === 0 ? '#000000' : '#4E4C4C'}} onClick={() => setActiveTabCommentIdx(0)}>Behind Task</div> |
||||||
|
</Col> |
||||||
|
<Col span={12}> |
||||||
|
<div style={{backgroundColor: activeTabCommentIdx === 1 ? '#FFFFFF' : '#D9D9D9', fontWeight: 500, textAlign: 'center', fontSize: 12, padding: 2, cursor: 'pointer', color: activeTabCommentIdx === 1 ? '#000000' : '#4E4C4C'}} onClick={() => setActiveTabCommentIdx(1)}>Comment From Customer</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<div className='custom-scroll' style={{maxHeight: '52vh', overflow: 'auto'}}> |
||||||
|
<Comment name="Dani" division="Interior Office" message="Overdue by 3 days"/> |
||||||
|
<Comment name="Roland" division="Interior Office" message="Overdue by 3 days"/> |
||||||
|
<Comment name="John" division="Interior Office" message="Overdue by 3 days"/> |
||||||
|
<Comment name="Logan" division="Interior Office" message="Overdue by 3 days"/> |
||||||
|
<Comment name="Doe" division="Interior Office" message="Overdue by 3 days"/> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default DashboardCustomer; |
@ -0,0 +1,323 @@ |
|||||||
|
import React, { useEffect, useMemo, useRef, useState } from 'react'; |
||||||
|
import axios from 'axios' |
||||||
|
import { Row, Col } from 'antd'; |
||||||
|
import { CardDashboard, CardExpenditure, CardScheduleHealthPerDivision } from '../../components/CardDashboard/CardDashboard'; |
||||||
|
import L from 'leaflet'; |
||||||
|
import { useParams } from 'react-router-dom'; |
||||||
|
import '../../assets/css/customscroll.css' |
||||||
|
import moment from 'moment'; |
||||||
|
import { renderFormatRupiah } from '../../const/CustomFunc'; |
||||||
|
|
||||||
|
const styles = { |
||||||
|
cardContainer: { backgroundColor: '#F8F8F8', margin: 2, paddingLeft: 20, paddingRight: 20, paddingTop: 10 }, |
||||||
|
cardHeaderContainer: { display: 'flex', flexDirection: 'row', marginBottom: 10 }, |
||||||
|
cardChartContainer: { position: 'relative', height: '21vh', margin: 'auto', paddingBottom: 10, justifyContent: 'center' }, |
||||||
|
cardTitle: { color: '#444444', fontSize: 16, fontWeight: 'bold' }, |
||||||
|
cardSubtitle: { color: '#888888', fontSize: 12 } |
||||||
|
} |
||||||
|
|
||||||
|
const center = { |
||||||
|
lat: -6.200000, |
||||||
|
lng: 106.816666 |
||||||
|
} |
||||||
|
|
||||||
|
const DashboardProject = () => { |
||||||
|
const token = localStorage.getItem("token") |
||||||
|
const HEADER = { |
||||||
|
headers: { |
||||||
|
"Content-Type": "application/json", |
||||||
|
"Authorization": `Bearer ${token}` |
||||||
|
} |
||||||
|
} |
||||||
|
const { PROJECT_ID, GANTT_ID } = useParams(); |
||||||
|
const URL_GANTT = `http://103.73.125.81:8446/index.html?base_url=http://103.73.125.81:8444/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`; |
||||||
|
const mapRef = useRef() |
||||||
|
const [projectName, setProjectName] = useState("Project Tower ABC"); |
||||||
|
const [projectManagerName, setProjectManagerName] = useState("John Doe"); |
||||||
|
const [customerName, setCustomerName] = useState("Jaya Gedung Group"); |
||||||
|
const [plannedStart, setPlannedStart] = useState("2019-03-04") |
||||||
|
const [plannedFinish, setPlannedFinish] = useState("2020-05-28") |
||||||
|
const [actualStart, setActualStart] = useState("2019-03-04") |
||||||
|
const [actualFinish, setActualFinish] = useState("2020-05-28") |
||||||
|
const [estimatedFinish, setEstimatedFinish] = useState("2020-05-28") |
||||||
|
const [mymap, setMymap] = useState(null); |
||||||
|
const [activeTabIdx, setActiveTabIdx] = useState(0); |
||||||
|
const [activeTabCommentIdx, setActiveTabCommentIdx] = useState(0); |
||||||
|
const [planningProgress, setPlanningProgress] = useState(79); |
||||||
|
const [actualProgress, setActualProgress] = useState(43); |
||||||
|
const [currentBudget, setCurrentBudget] = useState("168011727772"); |
||||||
|
const [addCostToComplete, setAddCostToComplete] = useState("0"); |
||||||
|
const [actualToDate, setactualToDate] = useState("43256751714");
|
||||||
|
const [estAtCompletion, setEstAtCompletion] = useState("168011727772"); |
||||||
|
const [bcwp, setBcwp] = useState("26658794618"); |
||||||
|
const [costDeviation, setCostDeviation] = useState("0"); |
||||||
|
const [remToComplete, setRemToComplete] = useState("124754976058"); |
||||||
|
const [totalInvoice, setTotalInvoice] = useState("10000000000"); |
||||||
|
const [cashIn, setCashIn] = useState("8000000000"); |
||||||
|
const [outstandingBalance, setOutstandingBalance] = useState("2000000000"); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
console.log('URL_GANTT', URL_GANTT); |
||||||
|
return () => { |
||||||
|
console.log('unmount RenderMap'); |
||||||
|
} |
||||||
|
}, []) |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (activeTabIdx === 1) { |
||||||
|
initMap(); |
||||||
|
} |
||||||
|
}, [activeTabIdx]); |
||||||
|
|
||||||
|
const initMap = () => { |
||||||
|
let mymap = L.map('map-area', { |
||||||
|
center: center, |
||||||
|
zoom: 13 |
||||||
|
}) |
||||||
|
|
||||||
|
setMymap(mymap); |
||||||
|
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}).addTo(mymap); |
||||||
|
} |
||||||
|
|
||||||
|
const RenderGantt = useMemo(() => ( |
||||||
|
<iframe |
||||||
|
id="frame-gantt" |
||||||
|
src={URL_GANTT} |
||||||
|
style={{ |
||||||
|
width: '100%', |
||||||
|
height: '100%', |
||||||
|
}} |
||||||
|
scrolling="no" |
||||||
|
frameBorder="0" |
||||||
|
allow="fullscreen" |
||||||
|
></iframe> |
||||||
|
), [activeTabIdx]) |
||||||
|
|
||||||
|
const Comment = ({name, division, message}) => ( |
||||||
|
<div style={{backgroundColor: '#EEEEEE', border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 5, marginBottom: 5, marginRight: 2, marginLeft: 2}}> |
||||||
|
<div style={{fontWeight: 'bold'}}>{name}</div> |
||||||
|
<div>{division}</div> |
||||||
|
<div style={{color: '#E80053', textAlign: 'right', fontWeight: 500, fontSize: 12}}>{message}</div> |
||||||
|
</div> |
||||||
|
) |
||||||
|
|
||||||
|
return ( |
||||||
|
<div style={{ marginLeft: -25, marginRight: -25 }}> |
||||||
|
<Row> |
||||||
|
<Col span={18}> |
||||||
|
<Row> |
||||||
|
<Col span={8}> |
||||||
|
<div style={{border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 10, margin: 2}}> |
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: 10 }}> |
||||||
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
||||||
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Project</div> |
||||||
|
<div style={{fontSize: 14}}>{projectName}</div> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<i className="fa fa-check-square" style={{fontSize: 28}}></i> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
<Col span={8}> |
||||||
|
<div style={{border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 10, margin: 2}}> |
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: 10 }}> |
||||||
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
||||||
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Project Manager</div> |
||||||
|
<div style={{fontSize: 14}}>{projectManagerName}</div> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<i className="fa fa-user" style={{fontSize: 28}}></i> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
<Col span={8}> |
||||||
|
<div style={{border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 10, margin: 2}}> |
||||||
|
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: 10 }}> |
||||||
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
||||||
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Customer</div> |
||||||
|
<div style={{fontSize: 14}}>{customerName}</div> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<i className="fa fa-home" style={{fontSize: 28}}></i> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={8}> |
||||||
|
<div style={{border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 10, margin: 2}}> |
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', marginBottom: 10 }}> |
||||||
|
<div> |
||||||
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Schedule</div> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<Row> |
||||||
|
<Col span={12} style={{fontSize: 11, fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Planned Start</Col> |
||||||
|
<Col span={12} style={{fontSize: 11}}>{plannedStart ? moment(plannedStart).format('D MMMM YYYY') : '-'}</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={12} style={{fontSize: 11, fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Acutal Start</Col> |
||||||
|
<Col span={12} style={{fontSize: 11}}>{plannedStart ? moment(actualStart).format('D MMMM YYYY') : '-'}</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={12} style={{fontSize: 11, fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Planned Finish</Col> |
||||||
|
<Col span={12} style={{fontSize: 11}}>{plannedStart ? moment(plannedFinish).format('D MMMM YYYY') : '-'}</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={12} style={{fontSize: 11, fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Estimated Finish</Col> |
||||||
|
<Col span={12} style={{fontSize: 11}}>{plannedStart ? moment(estimatedFinish).format('D MMMM YYYY') : '-'}</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
<Col span={16}> |
||||||
|
<div style={{border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 10, margin: 2}}> |
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', marginBottom: 10 }}> |
||||||
|
<div> |
||||||
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Financials</div> |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
<Row> |
||||||
|
<Col span={7} style={{fontSize: 11, fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Current Budget</Col> |
||||||
|
<Col span={5} style={{fontSize: 11}}>{currentBudget ? renderFormatRupiah(currentBudget, 'Rp.') : '-' }</Col> |
||||||
|
<Col span={7} style={{fontSize: 11, fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Add Cost to Complete</Col> |
||||||
|
<Col span={5} style={{fontSize: 11}}>{addCostToComplete ? renderFormatRupiah(addCostToComplete, 'Rp.') : '-' }</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={7} style={{fontSize: 11, fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Actual to Date</Col> |
||||||
|
<Col span={5} style={{fontSize: 11}}>{actualToDate ? renderFormatRupiah(actualToDate, 'Rp.') : '-' }</Col> |
||||||
|
<Col span={7} style={{fontSize: 11, fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Est. at Completion</Col> |
||||||
|
<Col span={5} style={{fontSize: 11}}>{estAtCompletion ? renderFormatRupiah(estAtCompletion, 'Rp.') : '-' }</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={7} style={{fontSize: 11, fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>BCWP (cost vs perform)</Col> |
||||||
|
<Col span={5} style={{fontSize: 11}}>{bcwp ? renderFormatRupiah(bcwp, 'Rp.') : '-' }</Col> |
||||||
|
<Col span={7} style={{fontSize: 11, fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Cost Deviation</Col> |
||||||
|
<Col span={5} style={{fontSize: 11}}>{costDeviation ? renderFormatRupiah(costDeviation, 'Rp.') : '-' }</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={7} style={{fontSize: 11, fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Rem. to Complete</Col> |
||||||
|
<Col span={5} style={{fontSize: 11}}>{remToComplete ? renderFormatRupiah(remToComplete, 'Rp.') : '-' }</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<div style={{margin: 2, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD'}}> |
||||||
|
<Row> |
||||||
|
<Col span={12}> |
||||||
|
<div style={{backgroundColor: activeTabIdx === 0 ? '#FFFFFF' : '#D9D9D9', padding: 8, fontWeight: 'bold', cursor: 'pointer', color: activeTabIdx === 0 ? '#000000' : '#4E4C4C'}} onClick={() => setActiveTabIdx(0)}>S Curve</div> |
||||||
|
</Col> |
||||||
|
<Col span={12}> |
||||||
|
<div style={{backgroundColor: activeTabIdx === 1 ? '#FFFFFF' : '#D9D9D9', padding: 8, fontWeight: 'bold', cursor: 'pointer', color: activeTabIdx === 1 ? '#000000' : '#4E4C4C'}} onClick={() => setActiveTabIdx(1)}>Maps</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={24}> |
||||||
|
<div style={{maxHeight: '55vh'}}> |
||||||
|
{ activeTabIdx === 0 && <div style={{height: '52vh', width: '100%'}}>{RenderGantt}</div> } |
||||||
|
{ activeTabIdx === 1 && <div id="map-area" style={{height: '52vh'}} ref={mapRef}></div> } |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
<Col span={6}> |
||||||
|
<div style={{margin: 2}}> |
||||||
|
<Row> |
||||||
|
<Col span={24}> |
||||||
|
<div style={{backgroundColor: '#222222', padding: 10, marginBottom: 5}}> |
||||||
|
<div style={{color: '#FFFFFF', textAlign: 'center', marginBottom: 10, fontWeight: 'bold'}}>Progress</div> |
||||||
|
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> |
||||||
|
<div style={{backgroundColor: '#7209B7', width: `${planningProgress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: planningProgress < 100 ? 0 : 10, borderBottomRightRadius: planningProgress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500}}>Planning : {planningProgress}%</div> |
||||||
|
</div> |
||||||
|
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> |
||||||
|
<div style={{backgroundColor: '#0059C9', width: `${actualProgress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: actualProgress < 100 ? 0 : 10, borderBottomRightRadius: actualProgress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500}}>Actual : {actualProgress}%</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={12}> |
||||||
|
<div style={{backgroundColor: '#FFFFFF', padding: 10, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', marginBottom: 5, marginRight: 2}}> |
||||||
|
<div style={{color: '#000000', textAlign: 'center', marginBottom: 10, fontWeight: 'bold', fontSize: 12}}>Health By Schedule</div> |
||||||
|
<div style={{backgroundColor: '#52AC0B', color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10}}> |
||||||
|
<div style={{textAlign: 'center', fontWeight: 500, fontSize: 12}}>On Budget</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
<Col span={12}> |
||||||
|
<div style={{backgroundColor: '#FFFFFF', padding: 10, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', marginBottom: 5, marginLeft: 2}}> |
||||||
|
<div style={{color: '#000000', textAlign: 'center', marginBottom: 10, fontWeight: 'bold', fontSize: 12}}>Health By Schedule</div> |
||||||
|
<div style={{backgroundColor: '#E80053', color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10}}> |
||||||
|
<div style={{textAlign: 'center', fontWeight: 500, fontSize: 12}}>Behind Schedule</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={24}> |
||||||
|
<div style={{backgroundColor: '#FFFFFF', padding: 10, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', marginBottom: 5, marginRight: 2}}> |
||||||
|
{/* <div style={{color: '#000000', textAlign: 'center', marginBottom: 10, fontWeight: 'bold', fontSize: 12}}>Health By Schedule</div> |
||||||
|
<div style={{backgroundColor: '#52AC0B', color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10}}> |
||||||
|
<div style={{textAlign: 'center', fontWeight: 500, fontSize: 12}}>On Budget</div> |
||||||
|
</div> */} |
||||||
|
<Row> |
||||||
|
<Col span={12}>Total Invoice</Col> |
||||||
|
<Col span={12} style={{textAlign: 'right'}}>{totalInvoice ? renderFormatRupiah(totalInvoice, 'Rp.') : '-' }</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={12}>Cash In</Col> |
||||||
|
<Col span={12} style={{textAlign: 'right'}}>{cashIn ? renderFormatRupiah(cashIn, 'Rp.') : '-' }</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={24}><hr style={{margin: 2}}/></Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={12}>Outstanding Balance</Col> |
||||||
|
<Col span={12} style={{textAlign: 'right', color: '#E80053', fontWeight: 500}}>{outstandingBalance ? renderFormatRupiah(outstandingBalance, 'Rp.') : '-' }</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<Row> |
||||||
|
<Col span={24}> |
||||||
|
<div style={{border: 'solid', borderWidth: 1, borderColor: '#DDDDDD'}}> |
||||||
|
{/* <div style={{display: 'flex', alignItems: 'center'}}> |
||||||
|
<div style={{flex: 1, margin: 'auto', cursor: 'pointer', textAlign: 'center', backgroundColor: activeTabCommentIdx === 0 ? '#FFFFFF' : '#D9D9D9', color: activeTabCommentIdx === 0 ? '#000000' : '#FFFFFF', fontWeight: 500, padding: 2}} onClick={() => setActiveTabCommentIdx(0)}>Behind Task</div> |
||||||
|
<div style={{flex: 1, margin: 'auto', cursor: 'pointer', textAlign: 'center', backgroundColor: activeTabCommentIdx === 1 ? '#FFFFFF' : '#D9D9D9', color: activeTabCommentIdx === 1 ? '#000000' : '#FFFFFF', fontWeight: 500, padding: 2, fontSize: 12}} onClick={() => setActiveTabCommentIdx(1)}>Comment From Customer</div> |
||||||
|
</div> */} |
||||||
|
<Row style={{alignItems: 'center', marginBottom: 5}}> |
||||||
|
<Col span={12}> |
||||||
|
<div style={{backgroundColor: activeTabCommentIdx === 0 ? '#FFFFFF' : '#D9D9D9', fontWeight: 500, textAlign: 'center', fontSize: 12, padding: 2, cursor: 'pointer', color: activeTabCommentIdx === 0 ? '#000000' : '#4E4C4C'}} onClick={() => setActiveTabCommentIdx(0)}>Behind Task</div> |
||||||
|
</Col> |
||||||
|
<Col span={12}> |
||||||
|
<div style={{backgroundColor: activeTabCommentIdx === 1 ? '#FFFFFF' : '#D9D9D9', fontWeight: 500, textAlign: 'center', fontSize: 12, padding: 2, cursor: 'pointer', color: activeTabCommentIdx === 1 ? '#000000' : '#4E4C4C'}} onClick={() => setActiveTabCommentIdx(1)}>Comment From Customer</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
<div className='custom-scroll' style={{maxHeight: '37vh', overflow: 'auto'}}> |
||||||
|
<Comment name="Dani" division="Interior Office" message="Overdue by 3 days"/> |
||||||
|
<Comment name="Roland" division="Interior Office" message="Overdue by 3 days"/> |
||||||
|
<Comment name="John" division="Interior Office" message="Overdue by 3 days"/> |
||||||
|
<Comment name="Logan" division="Interior Office" message="Overdue by 3 days"/> |
||||||
|
<Comment name="Doe" division="Interior Office" message="Overdue by 3 days"/> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
</Col> |
||||||
|
</Row> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
export default DashboardProject; |
Loading…
Reference in new issue