Browse Source

fix(dashboard): change view chart in dashboard BOD

pull/1/head
farhantock 7 months ago
parent
commit
a4186d5e41
  1. 41
      src/components/CardDashboard/CardDashboard.js
  2. 74
      src/views/Dashboard/Components/index.js
  3. 71
      src/views/Dashboard/DashboardBOD.js
  4. 4
      src/views/Dashboard/DashboardProject.js

41
src/components/CardDashboard/CardDashboard.js

@ -158,6 +158,35 @@ const optionsScheduleHealthPerDivision = {
indexAxis: 'x', indexAxis: 'x',
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
scales: {
x: {
ticks: {
font: {
size: 9,
},
callback: function (value) {
const label = this.getLabelForValue(value);
const maxLabelLength = 5;
if (label.length <= maxLabelLength) {
return label;
}
const words = label.split(' ');
let lines = [];
let currentLine = '';
words.forEach(word => {
if ((currentLine + word).length <= maxLabelLength) {
currentLine += word + ' ';
} else {
lines.push(currentLine.trim());
currentLine = word + ' ';
}
});
lines.push(currentLine.trim());
return lines;
},
},
}
},
plugins: { plugins: {
legend: { legend: {
display: true, display: true,
@ -175,7 +204,7 @@ const optionsScheduleHealthPerDivision = {
} }
} }
export const ContentLoaderChart = ({type}) => { export const ContentLoaderChart = ({ type }) => {
if (type === 'vertical-bar') { if (type === 'vertical-bar') {
return ( return (
<ContentLoader <ContentLoader
@ -298,10 +327,10 @@ export const CardDashboard = ({ title, subtitle, chartType, chartData, chartOpti
</div> </div>
<div style={styles.cardChartContainer}> <div style={styles.cardChartContainer}>
{ isReady ? {isReady ?
chartData ? chart : <NoDataChart /> chartData ? chart : <NoDataChart />
: :
<ContentLoaderChart type={chartType}/> <ContentLoaderChart type={chartType} />
} }
</div> </div>
@ -345,16 +374,16 @@ export const CardScheduleHealthPerDivision = ({ isReady, title, subtitle, mode,
</div> </div>
<div style={{ position: 'relative', height: '20vh', margin: 'auto', paddingBottom: 10, justifyContent: 'center' }}> <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}/> } {isReady ? chartData ? <Bar options={chartOption ? chartOption : optionsScheduleHealthPerDivision} data={chartData ? chartData : null} /> : <NoDataChart /> : <ContentLoaderChart type={chartType} />}
</div> </div>
</div> </div>
) )
} }
export const NoDataChart = ({message}) => { export const NoDataChart = ({ message }) => {
return ( return (
<div style={{textAlign: 'center', marginTop: 50, alignSelf: 'center', alignItems: 'center', justifyContent: 'center', color: 'red'}}> <div style={{ textAlign: 'center', marginTop: 50, alignSelf: 'center', alignItems: 'center', justifyContent: 'center', color: 'red' }}>
{message ? message : "No Data Available"} {message ? message : "No Data Available"}
</div> </div>
) )

74
src/views/Dashboard/Components/index.js

@ -2,7 +2,7 @@ import React from 'react'
import moment from "moment" import moment from "moment"
import ContentLoader from "react-content-loader" import ContentLoader from "react-content-loader"
export const SingleTextLoader = ({width, height}) => ( export const SingleTextLoader = ({ width, height }) => (
<ContentLoader <ContentLoader
speed={1} speed={1}
width={width ? width : 200} width={width ? width : 200}
@ -19,111 +19,111 @@ export const ListLoader = () => (
// <div style={{color: 'grey', flex: 1, textAlign: 'center', marginTop: 50, marginBottom: 50}}>Loading overdue activities...</div> // <div style={{color: 'grey', flex: 1, textAlign: 'center', marginTop: 50, marginBottom: 50}}>Loading overdue activities...</div>
<div> <div>
<SingleTextLoader width={"100%"} height={50} /> <SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div> <div style={{ marginBottom: 5 }}></div>
<SingleTextLoader width={"100%"} height={50} /> <SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div> <div style={{ marginBottom: 5 }}></div>
<SingleTextLoader width={"100%"} height={50} /> <SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div> <div style={{ marginBottom: 5 }}></div>
<SingleTextLoader width={"100%"} height={50} /> <SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div> <div style={{ marginBottom: 5 }}></div>
<SingleTextLoader width={"100%"} height={50} /> <SingleTextLoader width={"100%"} height={50} />
</div> </div>
) )
export const Comment = ({name, comment, created_at}) => ( export const Comment = ({ name, comment, created_at }) => (
<div style={{backgroundColor: '#EEEEEE', border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 5, marginBottom: 5, marginRight: 2, marginLeft: 2}}> <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 style={{ fontWeight: 'bold' }}>{name}</div>
<div style={{color: '#E80053', fontWeight: 100, fontSize: 10}}>{created_at ? moment(created_at).format('D MMMM YYYY HH:mm:ss') : '-'}</div> <div style={{ color: '#E80053', fontWeight: 100, fontSize: 10 }}>{created_at ? moment(created_at).format('D MMMM YYYY HH:mm:ss') : '-'}</div>
<div>{comment}</div> <div>{comment}</div>
</div> </div>
) )
export const BehindTaskItem = ({name, message}) => ( export const BehindTaskItem = ({ name, message }) => (
<div style={{backgroundColor: '#EEEEEE', border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 5, marginBottom: 5, marginRight: 2, marginLeft: 2}}> <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 style={{ fontWeight: 'bold' }}>{name}</div>
<div style={{color: '#E80053'}}>{message}</div> <div style={{ color: '#E80053' }}>{message}</div>
{/* <div style={{color: '#E80053', textAlign: 'right', fontWeight: 500, fontSize: 12}}>{division}</div> */} {/* <div style={{color: '#E80053', textAlign: 'right', fontWeight: 500, fontSize: 12}}>{division}</div> */}
</div> </div>
) )
export const ProgressPlanningBar = ({progress}) => { export const ProgressPlanningBar = ({ progress }) => {
if (progress) { if (progress) {
if (progress > 30) { if (progress > 30) {
return ( return (
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> <div style={{ backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10 }}>
<div style={{ backgroundColor: '#0000cc', width: `${progress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: progress < 100 ? 0 : 10, borderBottomRightRadius: progress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500 }}>Planning : {progress}%</div> <div style={{ backgroundColor: '#0000cc', width: `${progress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: progress < 100 ? 0 : 10, borderBottomRightRadius: progress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500 }}>Planning : {progress}%</div>
</div> </div>
) )
} }
else { else {
return ( return (
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> <div style={{ backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10 }}>
<div style={{ backgroundColor: '#0000cc', width: `${progress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: progress < 100 ? 0 : 10, borderBottomRightRadius: progress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500, height: 30 }}></div> <div style={{ backgroundColor: '#0000cc', width: `${progress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: progress < 100 ? 0 : 10, borderBottomRightRadius: progress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500, height: 30 }}></div>
<span style={{textAlign: 'center', width: '100%', float: 'left', marginTop: -25, color: '#4e4c4c', fontWeight: 500}}>Planning : {progress}%</span> <span style={{ textAlign: 'center', width: '100%', float: 'left', marginTop: -25, color: '#4e4c4c', fontWeight: 500 }}>Planning : {progress}%</span>
</div> </div>
) )
} }
} }
else { else {
return ( return (
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> <div style={{ backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10 }}>
<div style={{ backgroundColor: '#0000cc', width: `0%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: 0 < 100 ? 0 : 10, borderBottomRightRadius: 0 < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500, height: 30 }}></div> <div style={{ backgroundColor: '#0000cc', width: `0%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: 0 < 100 ? 0 : 10, borderBottomRightRadius: 0 < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500, height: 30 }}></div>
<span style={{textAlign: 'center', width: '100%', float: 'left', marginTop: -25, color: '#4e4c4c', fontWeight: 500}}>Planning : 0%</span> <span style={{ textAlign: 'center', width: '100%', float: 'left', marginTop: -25, color: '#4e4c4c', fontWeight: 500 }}>Planning : 0%</span>
</div> </div>
) )
} }
} }
export const ProgressActualBar = ({progress}) => { export const ProgressActualBar = ({ progress }) => {
if (progress) { if (progress) {
if (progress > 30) { if (progress > 30) {
return ( return (
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> <div style={{ backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10 }}>
<div style={{ backgroundColor: '#006600', width: `${progress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: progress < 100 ? 0 : 10, borderBottomRightRadius: progress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500 }}>Actual : {progress}%</div> <div style={{ backgroundColor: '#006600', width: `${progress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: progress < 100 ? 0 : 10, borderBottomRightRadius: progress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500 }}>Actual : {progress}%</div>
</div> </div>
) )
} }
else { else {
return ( return (
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> <div style={{ backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10 }}>
<div style={{ backgroundColor: '#006600', width: `${progress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: progress < 100 ? 0 : 10, borderBottomRightRadius: progress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500, height: 30 }}></div> <div style={{ backgroundColor: '#006600', width: `${progress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: progress < 100 ? 0 : 10, borderBottomRightRadius: progress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500, height: 30 }}></div>
<span style={{textAlign: 'center', width: '100%', float: 'left', marginTop: -25, color: '#4e4c4c', fontWeight: 500}}>Actual : {progress}%</span> <span style={{ textAlign: 'center', width: '100%', float: 'left', marginTop: -25, color: '#4e4c4c', fontWeight: 500 }}>Actual : {progress}%</span>
</div> </div>
) )
} }
} }
else { else {
return ( return (
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> <div style={{ backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10 }}>
<div style={{ backgroundColor: '#006600', width: `0%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: 0 < 100 ? 0 : 10, borderBottomRightRadius: 0 < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500, height: 30 }}></div> <div style={{ backgroundColor: '#006600', width: `0%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: 0 < 100 ? 0 : 10, borderBottomRightRadius: 0 < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500, height: 30 }}></div>
<span style={{textAlign: 'center', width: '100%', float: 'left', marginTop: -25, color: '#4e4c4c', fontWeight: 500}}>Actual : 0%</span> <span style={{ textAlign: 'center', width: '100%', float: 'left', marginTop: -25, color: '#4e4c4c', fontWeight: 500 }}>Actual : 0%</span>
</div> </div>
) )
} }
} }
export const HealthByBudget = ({status}) => { export const HealthByBudget = ({ status }) => {
let bgColor = '#52AC0B'; // on-budget let bgColor = '#52AC0B'; // on-budget
if (status && status !== '-') { if (status && status !== '-') {
if (status === 'warning') bgColor = '#ED7014'; if (status === 'warning') bgColor = '#ED7014';
else if (status === 'overrun') bgColor = '#D0312D'; else if (status === 'overrun') bgColor = '#D0312D';
} }
return ( return (
<div style={{backgroundColor: bgColor, color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10, minHeight: 25}}> <div style={{ backgroundColor: bgColor, color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10, minHeight: 25 }}>
<div style={{ textAlign: 'center', fontWeight: 500, fontSize: 12 }}>Health By Budget</div> <div style={{ textAlign: 'center', fontWeight: 500, fontSize: 12 }}>Health By Budget</div>
</div> </div>
) )
} }
export const HealthBySchedule = ({status}) => { export const HealthBySchedule = ({ status }) => {
// let bgColor = '#E80053'; // pink // let bgColor = '#E80053'; // pink
let bgColor = '#52AC0B'; // green let bgColor = '#52AC0B'; // green
if (status && status !== '-') { if (status && status !== '-') {
if (status === 'warning') bgColor = '#ED7014'; if (status === 'warning') bgColor = '#ED7014';
else if (status === 'danger') bgColor = '#D0312D'; else if (status === 'behind-schedule') bgColor = '#D0312D';
} }
return ( return (
<div style={{backgroundColor: bgColor, color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10, minHeight: 25}}> <div style={{ backgroundColor: bgColor, color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10, minHeight: 25 }}>
<div style={{ textAlign: 'center', fontWeight: 500, fontSize: 12 }}>Health By Schedule</div> <div style={{ textAlign: 'center', fontWeight: 500, fontSize: 12 }}>Health By Schedule</div>
</div> </div>
) )

71
src/views/Dashboard/DashboardBOD.js

@ -242,7 +242,7 @@ const DashboardBOD = (props) => {
} }
if (result.status == 200 && result.data.data) { if (result.status == 200 && result.data.data) {
SET_PROJECT_SCHEDULE_HEALTH_PER_DIVISION(result.data.data[0]); SET_PROJECT_SCHEDULE_HEALTH_PER_DIVISION(result.data.data);
} }
SET_READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION(true); SET_READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION(true);
} }
@ -348,25 +348,6 @@ const DashboardBOD = (props) => {
if (result.data.data.length > 0) { if (result.data.data.length > 0) {
let tableData = []; let tableData = [];
result.data.data.map((item, idx) => { result.data.data.map((item, idx) => {
let statusHealthBySchedule = 'on-schedule';
let planningProgress = 0;
let actualProgress = 0;
let selisihProgress = 0;
if (item.scurve && item.scurve[0]) {
planningProgress = item.scurve[0].data.percentagePlan[item.scurve[0].data.percentagePlan.length - 1]
actualProgress = item.scurve[0].data.percentageReal[item.scurve[0].data.percentageReal.length - 1]
}
selisihProgress = planningProgress - actualProgress
if (selisihProgress > 0 && selisihProgress <= 5) {
statusHealthBySchedule = 'warning'
}
else if (selisihProgress > 5) {
statusHealthBySchedule = 'danger'
}
let outstanding_balance = 0; let outstanding_balance = 0;
if (item.invoice) { if (item.invoice) {
outstanding_balance = item.invoice.invoiced - item.invoice.paid; outstanding_balance = item.invoice.invoiced - item.invoice.paid;
@ -394,7 +375,7 @@ const DashboardBOD = (props) => {
"cash_in": item.invoice?.paid ? toRupiah(item.invoice.paid) : '-', "cash_in": item.invoice?.paid ? toRupiah(item.invoice.paid) : '-',
"outstanding_balance": outstanding_balance, "outstanding_balance": outstanding_balance,
"budget": <HealthByBudget status={item.budget_health} />, "budget": <HealthByBudget status={item.budget_health} />,
"schedule": <HealthBySchedule status={statusHealthBySchedule} /> "schedule": <HealthBySchedule status={item.schedule_health} />
}) })
}); });
SET_DATA_DETAIL_EXPENDITURE(tableData); SET_DATA_DETAIL_EXPENDITURE(tableData);
@ -495,7 +476,12 @@ const DashboardBOD = (props) => {
y: { y: {
ticks: { ticks: {
autoSkip: false, autoSkip: false,
stepSize: 2 stepSize: 2,
// callback: function (value) {
// if (value === 0 || value === null || value === undefined) {
// return '';
// }
// }
} }
} }
}, },
@ -527,11 +513,11 @@ const DashboardBOD = (props) => {
label: '', label: '',
data: [ data: [
PROJECT_EXPENDITURE?.total_budget || 0, PROJECT_EXPENDITURE?.total_budget || 0,
PROJECT_EXPENDITURE?.total_expenditure || 1000000000, PROJECT_EXPENDITURE?.total_expenditure || 0,
PROJECT_EXPENDITURE?.total_invoice ? Math.floor(PROJECT_EXPENDITURE.total_invoice) : 1000000000, PROJECT_EXPENDITURE?.total_invoice ? Math.floor(PROJECT_EXPENDITURE.total_invoice) : 0,
PROJECT_EXPENDITURE?.total_paid_invoice || 1000000000, PROJECT_EXPENDITURE?.total_paid_invoice || 0,
PROJECT_EXPENDITURE?.total_paid_invoice || 1000000000, PROJECT_EXPENDITURE?.total_paid_invoice || 0,
PROJECT_EXPENDITURE?.total_paid_invoice || 1000000000 PROJECT_EXPENDITURE?.total_paid_invoice || 0
], ],
borderColor: [ borderColor: [
PROJECT_EXPENDITURE_COLOR?.total_budget || '#480ca8', PROJECT_EXPENDITURE_COLOR?.total_budget || '#480ca8',
@ -849,7 +835,36 @@ const DashboardBOD = (props) => {
font: { font: {
size: 9, size: 9,
}, },
} maxRotation: 0,
minRotation: 0,
callback: function (value) {
const label = this.getLabelForValue(value);
const words = label.split(' ');
const maxWordLength = 7;
const splitLongWords = (word) => {
if (word.length <= maxWordLength) {
return [word];
}
const chunks = [];
for (let i = 0; i < word.length; i += maxWordLength) {
chunks.push(word.substring(i, i + maxWordLength));
}
return chunks;
};
const lines = [];
words.forEach(word => {
if (word.length > maxWordLength) {
lines.push(...splitLongWords(word));
} else {
lines.push(word);
}
});
return lines;
},
},
}, },
y: { y: {
ticks: { ticks: {

4
src/views/Dashboard/DashboardProject.js

@ -436,10 +436,10 @@ const DashboardProject = (props) => {
setActualProgress(actualProgress); setActualProgress(actualProgress);
} }
selisihProgress = planningProgress - actualProgress; selisihProgress = planningProgress - actualProgress;
if (selisihProgress > 0 && selisihProgress <= 5) { if (selisihProgress > 0 && selisihProgress <= 20) {
statusHealthBySchedule = "warning"; statusHealthBySchedule = "warning";
} else if (selisihProgress > 5) { } else if (selisihProgress > 5) {
statusHealthBySchedule = "danger"; statusHealthBySchedule = "behind-schedule";
} }
setHealthBySchedule(statusHealthBySchedule); setHealthBySchedule(statusHealthBySchedule);
setIsReadySCurve(true); setIsReadySCurve(true);

Loading…
Cancel
Save