Browse Source

update DashboardProject and DashboardCustomer

pull/2/head
ardhi 2 years ago
parent
commit
e672e6d672
  1. 166
      src/views/Dashboard/Components/index.js
  2. 306
      src/views/Dashboard/DashboardCustomer.js
  3. 213
      src/views/Dashboard/DashboardProject.js

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

@ -0,0 +1,166 @@
import React from 'react'
import moment from "moment"
import ContentLoader from "react-content-loader"
export const SingleTextLoader = ({width, height}) => (
<ContentLoader
speed={1}
width={width ? width : 200}
height={height ? height : 20}
viewBox={`0 0 ${width ? width : 200} ${height ? height : 20}`}
backgroundColor="#E6E6E6"
foregroundColor="#ffffff"
>
<rect x="0" y="0" rx="8" ry="8" width={`${width ? width : 200}`} height={`${height ? height : 20}`} />
</ContentLoader>
)
export const ListLoader = () => (
// <div style={{color: 'grey', flex: 1, textAlign: 'center', marginTop: 50, marginBottom: 50}}>Loading overdue activities...</div>
<div>
<SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div>
<SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div>
<SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div>
<SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div>
<SingleTextLoader width={"100%"} height={50} />
</div>
)
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={{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>{comment}</div>
</div>
)
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={{fontWeight: 'bold'}}>{name}</div>
<div style={{color: '#E80053'}}>{message}</div>
{/* <div style={{color: '#E80053', textAlign: 'right', fontWeight: 500, fontSize: 12}}>{division}</div> */}
</div>
)
export const ProgressPlanningBar = ({progress}) => {
if (progress) {
if (progress > 30) {
return (
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}>
<div style={{backgroundColor: '#7209B7', 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>
)
}
else {
return (
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}>
<div style={{backgroundColor: '#7209B7', 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>
</div>
)
}
}
else {
return null;
}
}
export const ProgressActualBar = ({progress}) => {
if (progress) {
if (progress > 30) {
return (
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}>
<div style={{backgroundColor: '#0059C9', 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>
)
}
else {
return (
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}>
<div style={{backgroundColor: '#7209B7', 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>
</div>
)
}
}
else {
return null;
}
}
export const HealthByBudget = ({status}) => {
let bgColor = '#52AC0B'; // on-budget
if (status && status !== '-') {
if (status === 'warning') bgColor = '#ED7014';
else if (status === 'danger') bgColor = '#D0312D';
}
return (
<div style={{backgroundColor: bgColor, color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10, minHeight: 25}}>
<div style={{textAlign: 'center', fontWeight: 500, fontSize: 12}}>{status.toUpperCase()}</div>
</div>
)
}
export const HealthBySchedule = ({status}) => {
// let bgColor = '#E80053'; // pink
let bgColor = '#52AC0B'; // green
if (status && status !== '-') {
if (status === 'warning') bgColor = '#ED7014';
else if (status === 'danger') bgColor = '#D0312D';
}
return (
<div style={{backgroundColor: bgColor, color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10, minHeight: 25}}>
<div style={{textAlign: 'center', fontWeight: 500, fontSize: 12}}>{status.toUpperCase()}</div>
</div>
)
}
export const PopupContent = (item) => {
return `<table>
<tr>
<td>Activity</td>
<td>:</td>
<td><b>${item.activity_name}</b></td>
</tr>
<tr>
<td>Reported at</td>
<td>:</td>
<td><b>${item.report_date ? moment(item.report_date).format('D MMMM YYYY HH:mm:ss') : '-'}</b></td>
</tr>
<tr>
<td>Reported by</td>
<td>:</td>
<td><b>${item.name}</b></td>
</tr>
<tr>
<td colspan="3"><hr style="margin-top: 5px; margin-bottom: 5px;"></td>
</tr>
<tr>
<td>Material</td>
<td>:</td>
<td><b>${item.material_name}</b></td>
</tr>
<tr>
<td>QTY Plan</td>
<td>:</td>
<td><b>${item.qty_planning} (${item.material_unit})</b></td>
</tr>
<tr>
<td>QTY Real</td>
<td>:</td>
<td><b>${item.qty_real} (${item.material_unit})</b></td>
</tr>
<tr>
<td colspan="3"><hr style="margin-top: 5px; margin-bottom: 5px;"></td>
</tr>
<tr>
<td>Notes</td>
<td>:</td>
<td><b>${item.report_notes}</b></td>
</tr>
`;
}

306
src/views/Dashboard/DashboardCustomer.js

@ -9,6 +9,7 @@ import moment from 'moment';
import { BASE_OSPRO } from '../../const/ApiConst';
import { SendOutlined } from '@ant-design/icons';
import { NotificationContainer, NotificationManager } from 'react-notifications';
import { BehindTaskItem, Comment, HealthBySchedule, ListLoader, PopupContent, ProgressActualBar, ProgressPlanningBar, SingleTextLoader } from './Components';
const { TextArea } = Input;
const styles = {
@ -34,31 +35,42 @@ const DashboardCustomer = () => {
}
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 = `https://adw-gantt.ospro.id/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`;
// const URL_GANTT = `https://adw-gantt.ospro.id/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`;
// const URL_GANTT = '';
const URL_GANTT = `http://192.168.1.104:8446/view-mode/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 [projectName, setProjectName] = useState("");
const [projectManagerName, setProjectManagerName] = useState('');
const [customerName, setCustomerName] = useState("");
const [plannedStart, setPlannedStart] = useState(null)
const [plannedFinish, setPlannedFinish] = useState(null)
const [actualStart, setActualStart] = useState(null)
const [actualFinish, setActualFinish] = useState(null)
const [estimatedFinish, setEstimatedFinish] = useState(null)
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);
const [planningProgress, setPlanningProgress] = useState(0);
const [actualProgress, setActualProgress] = useState(0);
const [comment, setComment] = useState('');
const [comments, setComments] = useState([]);
const [isReadyComments, setIsReadyComments] = useState(false);
const [isSendingComment, setIsSendingComment] = useState(false);
const [behindTasks, setBehindTasks] = useState([]);
const [isReadyProjectDetail, setIsReadyProjectDetail] = useState(false);
const [isReadySCurve, setIsReadySCurve] = useState(false);
const [isReadyOverdueActivities, setIsReadyOverdueActivities] = useState(false);
const [isReadyReportDistribution, setIsReadyReportDistribution] = useState(false);
const [overdueActivities, setOverdueActivities] = useState([]);
const [healthBySchedule, setHealthBySchedule] = useState('-');
const [healthByBudget, setHealthByBudget] = useState('-');
const [reportDistribution, setReportDistribution] = useState([]);
useEffect(() => {
// console.log('URL_GANTT', URL_GANTT);
getProjectDetail();
getSCurve()
getOverdueActivities();
getReportDistribution()
getComments();
getBehindTasks();
return () => {
console.log('unmount RenderMap');
}
@ -70,6 +82,138 @@ const DashboardCustomer = () => {
}
}, [activeTabIdx]);
const getProjectDetail = async () => {
setIsReadyProjectDetail(false);
const URL = `${BASE_OSPRO}/api/project/detail/${PROJECT_ID}`;
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
console.log('getProjectDetail', result);
if (!result) {
NotificationManager.error(`Could not connect to internet.`, "Failed");
setIsReadyProjectDetail(true);
return;
}
if (result.status !== 200) {
NotificationManager.error(`Get project detail failed, ${result.data.message}`, "Failed");
setIsReadyProjectDetail(true);
return;
}
else if (result.status == 200 && result.data.data) {
console.log(result.data.data);
// setComments(result.data.data);
setProjectName(result.data.data.nama ? result.data.data.nama : '-');
setProjectManagerName(result.data.data.projectManager ? result.data.data.projectManager : '-');
setCustomerName(result.data.data.company ? result.data.data.company : '-')
setPlannedStart(result.data.data?.mulai_proyek ? result.data.data.mulai_proyek : null)
setPlannedFinish(result.data.data?.akhir_proyek ? result.data.data.akhir_proyek : null)
setActualStart(result.data.data.header?.start_date ? result.data.data.header.start_date : null)
setEstimatedFinish(result.data.data.header?.end_date ? result.data.data.header.end_date : null)
setIsReadyProjectDetail(true);
}
}
const getSCurve = async () => {
setIsReadySCurve(false);
const URL = `${BASE_OSPRO}/api/project/get-s-curve`;
const payload = {
"project_id": PROJECT_ID.toString(),
"period": "week",
"end_date": moment(new Date()).format('YYYY-MM-DD')
}
const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response)
console.log('getSCurve', result);
if (!result) {
NotificationManager.error(`Could not connect to internet.`, "Failed");
setIsReadySCurve(true);
return;
}
if (result.status !== 200) {
NotificationManager.error(`Get S Curve failed, ${result.data.message}`, "Failed");
setIsReadySCurve(true);
return;
}
else if (result.status == 200 && result.data.data) {
let selisihProgress = 0;
let planningProgress = 0;
let actualProgress = 0;
let statusHealthBySchedule = 'on-schedule';
if (result.data.data.length > 0 && result.data.data[0].data?.percentagePlan && result.data.data[0].data?.percentagePlan.length > 0) {
let planningProgress = result.data.data[0].data?.percentagePlan[result.data.data[0].data?.percentagePlan.length-1];
setPlanningProgress(planningProgress);
}
if (result.data.data.length > 0 && result.data.data[0].data?.percentageReal && result.data.data[0].data?.percentageReal.length > 0) {
let actualProgress = result.data.data[0].data?.percentageReal[result.data.data[0].data?.percentageReal.length-1];
setActualProgress(actualProgress);
}
selisihProgress = planningProgress - actualProgress;
if (selisihProgress > 0 && selisihProgress <= 5) {
statusHealthBySchedule = 'warning'
}
else if (selisihProgress > 5) {
statusHealthBySchedule = 'danger'
}
setHealthBySchedule(statusHealthBySchedule)
setIsReadySCurve(true);
}
}
const getOverdueActivities = async () => {
setIsReadyOverdueActivities(false);
const URL = `${BASE_OSPRO}/api/project/get-overdue-activities`;
const payload = {
"id": PROJECT_ID.toString(),
"till_date": moment(new Date()).format('YYYY-MM-DD')
}
const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response)
console.log('getOverdueActivities', result);
if (!result) {
NotificationManager.error(`Could not connect to internet.`, "Failed");
setIsReadyOverdueActivities(true);
return;
}
if (result.status !== 200) {
NotificationManager.error(`Get Overdue Activities failed, ${result.data.message}`, "Failed");
setIsReadyOverdueActivities(true);
return;
}
else if (result.status == 200 && result.data.data) {
if (result.data.data.overdueActivities) {
setOverdueActivities(result.data.data.overdueActivities)
}
setHealthByBudget(result.data.data.budget_health)
setIsReadyOverdueActivities(true);
}
}
const getReportDistribution = async () => {
setIsReadyReportDistribution(false);
const URL = `${BASE_OSPRO}/api/project/get-report-distribution`;
const payload = {
"project_id": PROJECT_ID,
"start_date": moment().startOf('month').subtract(1, 'years').format('YYYY-MM-DD'),
"end_date": moment(new Date()).subtract(1, 'years').format('YYYY-MM-DD')
}
const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response)
console.log('getReportDistribution', result);
if (!result) {
NotificationManager.error(`Could not connect to internet.`, "Failed");
setIsReadyReportDistribution(true);
return;
}
if (result.status !== 200) {
NotificationManager.error(`Get report distribution failed, ${result.data.message}`, "Failed");
setIsReadyReportDistribution(true);
return;
}
else if (result.status == 200 && result.data.data) {
setReportDistribution(result.data.data);
setIsReadyReportDistribution(true);
}
}
const getComments = async () => {
setIsReadyComments(false);
const URL = `${BASE_OSPRO}/api/project-comment/search`;
@ -108,19 +252,18 @@ const DashboardCustomer = () => {
}
if (result.status !== 200) {
NotificationManager.error(`Post comment failed, ${result.data.message}`, "Failed");
NotificationManager.error(`Get comments failed, ${result.data.message}`, "Failed");
setIsReadyComments(true);
return;
}
else if (result.status == 200 && result.data.data) {
console.log(result.data.data);
setComments(result.data.data);
setIsReadyComments(true);
}
}
const handleSendComment = async () => {
console.log('handleSendComment', comment);
// console.log('handleSendComment', comment);
setIsSendingComment(true);
if (comment === '') {
NotificationManager.error("Please leave a comment before you send it.", "Failed");
@ -159,34 +302,25 @@ const DashboardCustomer = () => {
setIsSendingComment(false);
}
const getBehindTasks = () => {
setBehindTasks([
{
id: 1,
name: "Tom",
message: "Overdue by 5 days",
division: "IT"
},
{
id: 1,
name: "Jerry",
message: "Overdue by 3 days",
division: "Finance"
}
])
}
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: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}).addTo(mymap);
}
useEffect(() => {
if (mymap) {
if (reportDistribution.length > 0) {
reportDistribution.map((item, idx) => {
L.marker([item.lat, item.lon]).addTo(mymap).bindPopup(PopupContent(item))
});
}
}
}, [mymap, reportDistribution])
const RenderGantt = useMemo(() => (
<iframe
id="frame-gantt"
@ -201,22 +335,6 @@ const DashboardCustomer = () => {
></iframe>
), [activeTabIdx])
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={{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>{comment}</div>
</div>
)
const BehindTaskItem = ({name, message, division}) => (
<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>{message}</div>
<div style={{color: '#E80053', textAlign: 'right', fontWeight: 500, fontSize: 12}}>{division}</div>
</div>
)
const RenderComments = useMemo(() => {
return (
<>
@ -227,7 +345,7 @@ const DashboardCustomer = () => {
</div>
</div>
<div>
{!isReadyComments && <div style={{color: 'grey', flex: 1, textAlign: 'center', marginTop: 50, marginBottom: 50}}>Loading comments...</div>}
{!isReadyComments && <ListLoader />}
{isReadyComments && comments && comments.length < 1 && <div style={{flex: 1, textAlign: 'center', color: '#E80053', marginTop: 50, marginBottom: 50}}>No comments found.</div>}
{comments && comments.length > 0 && comments.map((item, idx) =>
<Comment key={idx} name={item.join_first_name} comment={item.comment} created_at={item.created_at} />
@ -240,13 +358,24 @@ const DashboardCustomer = () => {
const RenderBehindTasks = useMemo(() => {
return (
<div>
{behindTasks && behindTasks.length < 1 && <div style={{flex: 1, textAlign: 'center', color: '#E80053', marginTop: 50, marginBottom: 50}}>No behind task found.</div>}
{behindTasks && behindTasks.length > 0 && behindTasks.map((item, idx) =>
<BehindTaskItem key={idx} name={item.name} message={item.message} division={item.division} />
)}
{!isReadyOverdueActivities && <ListLoader />}
{isReadyOverdueActivities && overdueActivities && overdueActivities.length < 1 && <div style={{flex: 1, textAlign: 'center', color: '#E80053', marginTop: 50, marginBottom: 50}}>No behind task found.</div>}
{isReadyOverdueActivities && overdueActivities && overdueActivities.length > 0 && overdueActivities.map((item, idx) => {
let end_date = null;
let today = null;
let diffDays = 0;
let message = '';
if (item.end_date && item.end_date !== null) {
end_date = moment(item.end_date);
today = moment(new Date());
diffDays = today.diff(end_date, 'days');
message = `Overdue by ${diffDays} days`;
}
return <BehindTaskItem key={idx} name={item.name} message={message} />
})}
</div>
)
}, [behindTasks])
}, [overdueActivities, isReadyOverdueActivities])
return (
<div style={{ marginLeft: -25, marginRight: -25 }}>
@ -270,7 +399,7 @@ const DashboardCustomer = () => {
<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 style={{fontSize: 14}}>{isReadyProjectDetail ? projectName : <SingleTextLoader />}</div>
</div>
<div>
<i className="fa fa-check-square" style={{fontSize: 28}}></i>
@ -284,7 +413,7 @@ const DashboardCustomer = () => {
<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 style={{fontSize: 14}}>{isReadyProjectDetail ? customerName : <SingleTextLoader />}</div>
</div>
<div>
<i className="fa fa-home" style={{fontSize: 28}}></i>
@ -302,18 +431,36 @@ const DashboardCustomer = () => {
</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>
<Col span={6} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Planned Start</Col>
<Col span={6}>
{isReadyProjectDetail ?
plannedStart ? moment(plannedStart).format('D MMMM YYYY') : '-'
: <SingleTextLoader width={100} height={10} />
}
</Col>
<Col span={6} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Planned Finish</Col>
<Col span={6}>
{isReadyProjectDetail ?
plannedFinish ? moment(plannedFinish).format('D MMMM YYYY') : '-'
: <SingleTextLoader width={100} height={10} />
}
</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>
<Col span={6} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Actual Start</Col>
<Col span={6}>
{isReadyProjectDetail ?
actualStart ? moment(actualStart).format('D MMMM YYYY') : '-'
: <SingleTextLoader width={100} height={10} />
}
</Col>
<Col span={6} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Estimated Finish</Col>
<Col span={6}>
{isReadyProjectDetail ?
estimatedFinish ? moment(estimatedFinish).format('D MMMM YYYY') : '-'
: <SingleTextLoader width={100} height={10} />
}
</Col>
</Row>
</div>
</div>
@ -347,12 +494,17 @@ const DashboardCustomer = () => {
<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>
{isReadySCurve ?
<ProgressPlanningBar progress={planningProgress} />
:
<SingleTextLoader width={"100%"} height={30} />
}
<div style={{marginTop: 10, marginBottom: 10}}></div>
{isReadySCurve ?
<ProgressActualBar progress={actualProgress} />
:
<SingleTextLoader width={"100%"} height={30} />
}
</div>
</Col>
</Row>
@ -360,9 +512,7 @@ const DashboardCustomer = () => {
<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>
{isReadySCurve ? <HealthBySchedule status={healthBySchedule} /> : <SingleTextLoader width={"100%"} height={30} /> }
</div>
</Col>
</Row>

213
src/views/Dashboard/DashboardProject.js

@ -11,6 +11,7 @@ import { BASE_OSPRO } from '../../const/ApiConst';
import { SendOutlined } from '@ant-design/icons';
import { NotificationContainer, NotificationManager } from 'react-notifications';
import ContentLoader from 'react-content-loader';
import { BehindTaskItem, Comment, HealthByBudget, HealthBySchedule, ListLoader, PopupContent, ProgressActualBar, ProgressPlanningBar, SingleTextLoader } from './Components';
const { TextArea } = Input;
const styles = {
@ -37,7 +38,8 @@ const DashboardProject = () => {
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 = `https://adw-gantt.ospro.id/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`;
const URL_GANTT = '';
// const URL_GANTT = '';
const URL_GANTT = `http://192.168.1.104:8446/view-mode/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("");
const [projectManagerName, setProjectManagerName] = useState("");
@ -50,8 +52,8 @@ const DashboardProject = () => {
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 [planningProgress, setPlanningProgress] = useState(0);
const [actualProgress, setActualProgress] = useState(0);
const [currentBudget, setCurrentBudget] = useState(null)
const [addCostToComplete, setAddCostToComplete] = useState(null)
const [actualToDate, setActualToDate] = useState(null)
@ -66,21 +68,23 @@ const DashboardProject = () => {
const [comments, setComments] = useState([]);
const [isReadyComments, setIsReadyComments] = useState(false);
const [isSendingComment, setIsSendingComment] = useState(false);
const [isReadyProjectDetail, setIsReadyProjectDetail] = useState(false);
const [isReadySCurve, setIsReadySCurve] = useState(false);
const [isReadyOverdueActivities, setIsReadyOverdueActivities] = useState(false);
const [isReadyIntegrationInvoice, setIsReadyIntegrationInvoice] = useState(false);
const [isReadyReportDistribution, setIsReadyReportDistribution] = useState(false);
const [overdueActivities, setOverdueActivities] = useState([]);
const [healthBySchedule, setHealthBySchedule] = useState('-')
const [healthByBudget, setHealthByBudget] = useState('-')
const [reportDistribution, setReportDistribution] = useState([]);
useEffect(() => {
// console.log('URL_GANTT', URL_GANTT);
getProjectDetail();
getSCurve();
getOverdueActivities();
getComments();
getProjectDetail()
getSCurve()
getOverdueActivities()
getReportDistribution()
getComments()
return () => {
console.log('unmount RenderMap');
}
@ -119,6 +123,10 @@ const DashboardProject = () => {
setActualStart(result.data.data.header?.start_date ? result.data.data.header.start_date : null)
setEstimatedFinish(result.data.data.header?.end_date ? result.data.data.header.end_date : null)
setIsReadyProjectDetail(true);
if (result.data.data.kode_sortname && result.data.data.kode_sortname !== '') {
getIntegrationInvoice(result.data.data.kode_sortname)
}
}
}
@ -144,7 +152,10 @@ const DashboardProject = () => {
return;
}
else if (result.status == 200 && result.data.data) {
console.log(result.data.data);
let selisihProgress = 0;
let planningProgress = 0;
let actualProgress = 0;
let statusHealthBySchedule = 'on-schedule';
if (result.data.data.length > 0 && result.data.data[0].data?.budget_control) {
setCurrentBudget(result.data.data[0].data.budget_control.current_budget?.toString())
setActualToDate(result.data.data[0].data.budget_control.acwp?.toString())
@ -154,6 +165,24 @@ const DashboardProject = () => {
setEstAtCompletion(result.data.data[0].data.budget_control.estimated_at_completion?.toString())
setCostDeviation(result.data.data[0].data.budget_control.cost_deviation?.toString())
}
if (result.data.data.length > 0 && result.data.data[0].data?.percentagePlan && result.data.data[0].data?.percentagePlan.length > 0) {
let planningProgress = result.data.data[0].data?.percentagePlan[result.data.data[0].data?.percentagePlan.length-1];
setPlanningProgress(planningProgress);
}
if (result.data.data.length > 0 && result.data.data[0].data?.percentageReal && result.data.data[0].data?.percentageReal.length > 0) {
let actualProgress = result.data.data[0].data?.percentageReal[result.data.data[0].data?.percentageReal.length-1];
setActualProgress(actualProgress);
}
selisihProgress = planningProgress - actualProgress;
if (selisihProgress > 0 && selisihProgress <= 5) {
statusHealthBySchedule = 'warning'
}
else if (selisihProgress > 5) {
statusHealthBySchedule = 'danger'
}
setHealthBySchedule(statusHealthBySchedule)
setIsReadySCurve(true);
}
}
@ -179,7 +208,6 @@ const DashboardProject = () => {
return;
}
else if (result.status == 200 && result.data.data) {
console.log(result.data.data);
if (result.data.data.overdueActivities) {
setOverdueActivities(result.data.data.overdueActivities)
}
@ -188,6 +216,66 @@ const DashboardProject = () => {
}
}
const getIntegrationInvoice = async (kode_sortname) => {
setIsReadyIntegrationInvoice(false);
const URL = `${BASE_OSPRO}/api/project/get-integration-invoice`;
const payload = {
"search": kode_sortname
}
const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response)
console.log('getIntegrationInvoice', result);
if (!result) {
NotificationManager.error(`Could not connect to internet.`, "Failed");
setIsReadyIntegrationInvoice(true);
return;
}
if (result.status !== 200) {
NotificationManager.error(`Get integration invoice failed, ${result.data.message}`, "Failed");
setIsReadyIntegrationInvoice(true);
return;
}
else if (result.status == 200 && result.data.data) {
// console.log()
if (result.data.data.data) {
let total_invoice = result.data.data.data.total_invoice_amount;
let cash_in = result.data.data.data.total_invoice_paid_amount;
let outstanding_balance = total_invoice - cash_in;
setTotalInvoice(total_invoice ? total_invoice.toString() : null);
setCashIn(cash_in ? total_invoice.toString() : null)
setOutstandingBalance(outstanding_balance ? outstanding_balance.toString() : null);
}
setIsReadyIntegrationInvoice(true);
}
}
const getReportDistribution = async () => {
setIsReadyReportDistribution(false);
const URL = `${BASE_OSPRO}/api/project/get-report-distribution`;
const payload = {
"project_id": PROJECT_ID,
"start_date": moment().startOf('month').subtract(1, 'years').format('YYYY-MM-DD'),
"end_date": moment(new Date()).subtract(1, 'years').format('YYYY-MM-DD')
}
const result = await axios.post(URL, payload, HEADER).then(res => res).catch(err => err.response)
console.log('getReportDistribution', result);
if (!result) {
NotificationManager.error(`Could not connect to internet.`, "Failed");
setIsReadyReportDistribution(true);
return;
}
if (result.status !== 200) {
NotificationManager.error(`Get report distribution failed, ${result.data.message}`, "Failed");
setIsReadyReportDistribution(true);
return;
}
else if (result.status == 200 && result.data.data) {
setReportDistribution(result.data.data);
setIsReadyReportDistribution(true);
}
}
const getComments = async () => {
setIsReadyComments(false);
const URL = `${BASE_OSPRO}/api/project-comment/search`;
@ -231,7 +319,6 @@ const DashboardProject = () => {
return;
}
else if (result.status == 200 && result.data.data) {
console.log(result.data.data);
setComments(result.data.data);
setIsReadyComments(true);
}
@ -284,10 +371,19 @@ const DashboardProject = () => {
});
setMymap(mymap);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}).addTo(mymap);
}
useEffect(() => {
if (mymap) {
if (reportDistribution.length > 0) {
reportDistribution.map((item, idx) => {
L.marker([item.lat, item.lon]).addTo(mymap).bindPopup(PopupContent(item));
});
}
}
}, [mymap, reportDistribution])
const RenderGantt = useMemo(() => (
<iframe
id="frame-gantt"
@ -302,49 +398,6 @@ const DashboardProject = () => {
></iframe>
), [activeTabIdx])
const SingleTextLoader = ({width, height}) => (
<ContentLoader
speed={1}
width={width ? width : 200}
height={height ? height : 20}
viewBox={`0 0 ${width ? width : 200} ${height ? height : 20}`}
backgroundColor="#E6E6E6"
foregroundColor="#ffffff"
>
<rect x="0" y="0" rx="5" ry="5" width={`${width ? width : 200}`} height={`${height ? height : 20}`} />
</ContentLoader>
)
const ListLoader = () => (
// <div style={{color: 'grey', flex: 1, textAlign: 'center', marginTop: 50, marginBottom: 50}}>Loading overdue activities...</div>
<div>
<SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div>
<SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div>
<SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div>
<SingleTextLoader width={"100%"} height={50} />
<div style={{marginBottom: 5}}></div>
<SingleTextLoader width={"100%"} height={50} />
</div>
)
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={{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>{comment}</div>
</div>
)
const BehindTaskItem = ({name, 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 style={{color: '#E80053'}}>{message}</div>
{/* <div style={{color: '#E80053', textAlign: 'right', fontWeight: 500, fontSize: 12}}>{division}</div> */}
</div>
)
const RenderComments = useMemo(() => {
return (
@ -578,12 +631,17 @@ const DashboardProject = () => {
<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>
{isReadySCurve ?
<ProgressPlanningBar progress={planningProgress} />
:
<SingleTextLoader width={"100%"} height={30} />
}
<div style={{marginTop: 10, marginBottom: 10}}></div>
{isReadySCurve ?
<ProgressActualBar progress={actualProgress} />
:
<SingleTextLoader width={"100%"} height={30} />
}
</div>
</Col>
</Row>
@ -591,17 +649,13 @@ const DashboardProject = () => {
<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 Budget</div>
<div style={{backgroundColor: '#52AC0B', color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10, minHeight: 25}}>
<div style={{textAlign: 'center', fontWeight: 500, fontSize: 12}}>{healthByBudget}</div>
</div>
{isReadyOverdueActivities ? <HealthByBudget status={healthByBudget} /> : <SingleTextLoader width={"100%"} height={30} /> }
</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, minHeight: 25}}>
<div style={{textAlign: 'center', fontWeight: 500, fontSize: 12}}>{healthBySchedule}</div>
</div>
{isReadySCurve ? <HealthBySchedule status={healthBySchedule} /> : <SingleTextLoader width={"100%"} height={30} /> }
</div>
</Col>
</Row>
@ -614,18 +668,33 @@ const DashboardProject = () => {
</div> */}
<Row>
<Col span={12}>Total Invoice</Col>
<Col span={12} style={{textAlign: 'right'}}>{totalInvoice ? renderFormatRupiah(totalInvoice, 'Rp.') : '-' }</Col>
<Col span={12} style={{textAlign: 'right'}}>
{isReadyIntegrationInvoice ?
totalInvoice ? renderFormatRupiah(totalInvoice, 'Rp.') : '-'
: <SingleTextLoader width={100} height={10} />
}
</Col>
</Row>
<Row>
<Col span={12}>Cash In</Col>
<Col span={12} style={{textAlign: 'right'}}>{cashIn ? renderFormatRupiah(cashIn, 'Rp.') : '-' }</Col>
<Col span={12} style={{textAlign: 'right'}}>
{isReadyIntegrationInvoice ?
cashIn ? renderFormatRupiah(cashIn, 'Rp.') : '-'
: <SingleTextLoader width={100} height={10} />
}
</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>
<Col span={12} style={{textAlign: 'right', color: '#E80053', fontWeight: 500}}>
{isReadyIntegrationInvoice ?
outstandingBalance ? renderFormatRupiah(outstandingBalance, 'Rp.') : '-'
: <SingleTextLoader width={100} height={10} />
}
</Col>
</Row>
</div>
</Col>

Loading…
Cancel
Save