From 3e4bd305edfc6a1e5009f243b3463585e6c4f6f8 Mon Sep 17 00:00:00 2001 From: wahyuun Date: Tue, 21 Nov 2023 10:07:26 +0700 Subject: [PATCH 1/3] add button link --- src/views/Dashboard/DashboardBOD.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/views/Dashboard/DashboardBOD.js b/src/views/Dashboard/DashboardBOD.js index 74e64d0..61742de 100644 --- a/src/views/Dashboard/DashboardBOD.js +++ b/src/views/Dashboard/DashboardBOD.js @@ -12,6 +12,7 @@ import ContentLoader from 'react-content-loader'; import toRupiah from '@develoka/angka-rupiah-js'; import Icon from '@iconify/react'; import { HealthByBudget, HealthBySchedule } from './Components'; +import { Link } from 'react-router-dom'; const DashboardBOD = (props) => { let role_id = '', user_id='',isLogin='',token=''; @@ -20,13 +21,11 @@ const DashboardBOD = (props) => { user_id = props.location.state.user_id; token = props.location.state.token; isLogin = props.location.state.isLogin; - console.log('props.location.state success'); } else { role_id = localStorage.getItem("role_id"); user_id = localStorage.getItem("user_id"); token = localStorage.getItem("token"); isLogin = localStorage.getItem("isLogin"); - console.error('undefined in props.location.state'); } const HEADER = { headers: { @@ -427,11 +426,20 @@ const DashboardBOD = (props) => {
Total Project Expenditure from on-going project.
{PROJECT_EXPENDITURE ? -
-
- Detailed View + <> +
+
+ Detailed View +
-
+
+ +
+ View All Gantt +
+ +
+ :
Detailed View
From c3417b1352504b672036fb347b167f52e1f427b2 Mon Sep 17 00:00:00 2001 From: wahyuun Date: Tue, 21 Nov 2023 10:09:55 +0700 Subject: [PATCH 2/3] add file --- .../Dashboard/DashboardProjectCarousell.js | 977 ++++++++++++++++++ 1 file changed, 977 insertions(+) create mode 100644 src/views/Dashboard/DashboardProjectCarousell.js diff --git a/src/views/Dashboard/DashboardProjectCarousell.js b/src/views/Dashboard/DashboardProjectCarousell.js new file mode 100644 index 0000000..64e9f94 --- /dev/null +++ b/src/views/Dashboard/DashboardProjectCarousell.js @@ -0,0 +1,977 @@ +import React, { useEffect, useMemo, useRef, useState } from "react"; +import axios from "axios"; +import { Row, Col, Button, Input,Spin } from "antd"; +import { + CardDashboard, + CardExpenditure, + CardScheduleHealthPerDivision, +} from "../../components/CardDashboard/CardDashboard"; +import L from "leaflet"; +import "../../assets/css/customscroll.css"; +import moment from "moment"; +import { renderFormatRupiah } from "../../const/CustomFunc"; +import { BASE_OSPRO, BASE_OSPRO_FE, VERSION_GANTT_SEARCH } from "../../const/ApiConst"; +import { SendOutlined } from "@ant-design/icons"; +import { + NotificationContainer, + NotificationManager, +} from "react-notifications"; +import { + Carousel, + CarouselItem, + CarouselControl, + CarouselIndicators, + CarouselCaption, +} from 'reactstrap'; +import ContentLoader from "react-content-loader"; +import { + BehindTaskItem, + Comment, + HealthByBudget, + HealthBySchedule, + ListLoader, + PopupContent, + ProgressActualBar, + ProgressPlanningBar, + SingleTextLoader, +} from "./Components"; +import { Fab, Action } from "react-tiny-fab"; +import "react-tiny-fab/dist/styles.css"; +import { useHistory, useLocation, useParams } from "react-router-dom"; + +const { TextArea } = Input; + +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.2, + lng: 106.816666, +}; + +const DashboardProject = (args) => { + const token = localStorage.getItem("token"); + const HEADER = { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + }; + const { PROJECT_ID } = useParams(); + const mapRef = useRef(); + const [mymap, setMymap] = useState(null); + const [activeTabIdx, setActiveTabIdx] = useState(0); + const [activeTabCommentIdx, setActiveTabCommentIdx] = useState(0); + const [planningProgress, setPlanningProgress] = useState(0); + const [actualProgress, setActualProgress] = useState(0); + const [isReadyComments, setIsReadyComments] = useState(false); + const [isSendingComment, setIsSendingComment] = useState(false); + const [isReadyProjectDetail, setIsReadyProjectDetail] = useState(false); + const [isReadySCurve, setIsReadySCurve] = useState(false); + const [isReadyGantt, setIsReadyGantt] = useState(false); + const [reportDistribution, setReportDistribution] = useState([]); + const [isReadyOverdueActivities, setIsReadyOverdueActivities] = + useState(false); + const [healthBySchedule, setHealthBySchedule] = useState("-"); + const [allDataMaster, sourceData] = useState([]); + const [isHierarchy, setIsHierarchy] = useState(false); + let history = useHistory(); + // Carousell + const [activeIndex, setActiveIndex] = useState(0); + const [animating, setAnimating] = useState(false); + const [loading, setLoading] = useState(true); + + + useEffect(() => { + setLoading(true); + getAllData(); + },[]); + + useEffect(() => { + if (activeTabIdx === 1) { + initMap(); + } + }, [activeTabIdx]); + + const getAllData = async () => { + setIsReadyProjectDetail(false); + setIsReadySCurve(false) + setIsReadySCurve(false); + const URL = `${BASE_OSPRO}/api/project-carausell`; + const result = await axios + .get(URL, HEADER) + .then((res) => res) + .catch((err) => err.response); + if (!result) { + NotificationManager.error(`Could not connect to internet.`, "Failed"); + setIsReadyProjectDetail(true); + setIsReadySCurve(true); + setLoading(false); + return; + } + + if (result.status !== 200) { + NotificationManager.error( + `Get project detail failed, ${result.data.message}`, + "Failed" + ); + setIsReadyProjectDetail(true); + setIsReadySCurve(true); + setLoading(false); + return; + } else if (result.status == 200 && result.data.data) { + const dataResault = result.data.data; + console.log("Resault Data",dataResault); + sourceData(dataResault); + setIsReadyGantt(true); + setIsReadyProjectDetail(true); + // // SCurve + // let statusHealthBySchedule = "on-schedule"; + // let selisihProgress = 0; + // dataResault.map((item, idx) => { + // item.SCurve.map((itemSCurve, idx) => { + // let planningProgress = 0; + // let actualProgress = 0; + // let now = new Date().toISOString().slice(0, 10); + // let dates = itemSCurve.data?.date; + // let n = dates.findIndex( + // (element) => new Date(now) < new Date(element) + // ); + // if ( + // itemSCurve.length > 0 && + // itemSCurve.data?.percentagePlan && + // itemSCurve.data?.percentagePlan.length > 0 + // ) { + // planningProgress = itemSCurve.data?.percentagePlan[n]; + // if (n < 0) { + // planningProgress = 100; + // } + // setPlanningProgress(planningProgress); + // } + // if ( + // itemSCurve.length > 0 && + // itemSCurve.data?.percentageReal && + // itemSCurve.data?.percentageReal.length > 0 + // ) { + // actualProgress = + // itemSCurve.data?.percentageReal[ + // itemSCurve.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); + // setHierarchy + setIsHierarchy(true); + // Get Overdue + setIsReadyOverdueActivities(true); + // Distribution + setReportDistribution(dataResault); + // loading + setLoading(false); + + } + }; + + 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: + '© OpenStreetMap contributors', + }).addTo(mymap); + }; + + useEffect(() => { + if (mymap) { + reportDistribution.map((item, idx) => { + if (item.report_distribution.length > 0) { + item.report_distribution.map((itemDistribution, idx) => { + L.marker([itemDistribution.lat, itemDistribution.lon]) + .addTo(mymap) + .bindPopup(PopupContent(itemDistribution)); + }) + } + }); + } + }, [mymap, reportDistribution]); + + useEffect(() => { + // Add event listener for receiving messages from the iframe + window.addEventListener("message", handleIframeMessage); + + // Clean up the event listener on component unmount + return () => { + window.removeEventListener("message", handleIframeMessage); + }; + }, []); + + const handleIframeMessage = (event) => { + if (event.data && event.data.action === "getUrl") { + const childUrl = window.location.href; + + // Send the URL back to the iframe + event.source.postMessage( + { action: "sendUrl", url: childUrl, isHierarchy: isHierarchy }, + event.origin + ); + } + }; + + const next = () => { + if (animating) return; + const nextIndex = activeIndex === allDataMaster.length - 1 ? 0 : activeIndex + 1; + setActiveIndex(nextIndex); + }; + + const previous = () => { + if (animating) return; + const nextIndex = activeIndex === 0 ? allDataMaster.length - 1 : activeIndex - 1; + setActiveIndex(nextIndex); + }; + + const goToIndex = (newIndex) => { + if (animating) return; + setActiveIndex(newIndex); + }; + + const slides = allDataMaster.map((item, index) => { + let URL_GANTT = ""; + let version_gantt = ""; + item.project.gantt.map((itemGantt, index) => { + URL_GANTT = `http://localhost:8444/adw-gantt/view-mode/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${itemGantt.gantt_id}&proyek_id=${itemGantt.proyek_id}&token=${token}&ro=1`; + version_gantt = itemGantt.name_version + }); + const today = moment(); + const assignedList = item.assigned + .filter((itemAssigned) => + today.isBetween(moment(itemAssigned.start_date), moment(itemAssigned.end_date)) + ) + .map((itemMap) => itemMap.user_id); + // SCurve + let statusHealthBySchedule = "on-schedule"; + let selisihProgress = 0; + let SetplanningProgress = 0; + let SetactualProgress = 0; + let now = new Date().toISOString().slice(0, 10); + let dates = item.SCurve[0].data?.date; + let n = dates.findIndex( + (element) => new Date(now) < new Date(element) + ); + if ( + item.SCurve[0].length > 0 && + item.SCurve[0].data?.percentagePlan && + item.SCurve[0].data?.percentagePlan.length > 0 + ) { + SetplanningProgress = item.SCurve[0].data?.percentagePlan[n]; + if (n < 0) { + SetplanningProgress = 100; + } + } + if ( + item.SCurve[0].length > 0 && + item.SCurve[0].data?.percentageReal && + item.SCurve[0].data?.percentageReal.length > 0 + ) { + SetactualProgress = + item.SCurve[0].data?.percentageReal[ + item.SCurve[0].data?.percentageReal.length - 1 + ]; + } + + selisihProgress = SetplanningProgress - SetactualProgress; + if (selisihProgress > 0 && selisihProgress <= 5) { + statusHealthBySchedule = "warning"; + } else if (selisihProgress > 5) { + statusHealthBySchedule = "danger"; + } + return ( + setAnimating(true)} + onExited={() => setAnimating(false)} + key={parseInt(item.key)}> + + + + +
+
+
+
+ Project +
+
+ {isReadyProjectDetail && + isReadyGantt ? ( + (() => { + let parentNames = ""; + if (item.hierarchy.length > 0) + { + for ( let i = item.hierarchy.length - 1; i >= 0; i--) { + parentNames += " - "; + parentNames += item.hierarchy[i].name; + } + } + return item.project.nama + + parentNames + + " - " + + version_gantt + })() + ) : ( + + )} +
+
+
+ +
+
+
+ + +
+
+
+
+ Project Manager +
+
+ {isReadyProjectDetail ? ( + item.project_manager + ) : ( + + )} +
+
+
+ +
+
+
+ + +
+
+
+
+ Customer +
+
+ {isReadyProjectDetail ? ( + item.project.company + ) : ( + + )} +
+
+
+ +
+
+
+ +
+ + +
+
+
+
+ Schedule +
+
+
+ + + + Planned Start + + + {isReadyProjectDetail ? ( + item.activity.planned_start ? ( + moment(item.activity.planned_start).format("D MMMM YYYY") + ) : ( + "-" + ) + ) : ( + + )} + + + + + + Actual Start + + + {isReadyProjectDetail ? ( + item.activity.start_date ? ( + moment(item.activity.start_date).format("D MMMM YYYY") + ) : ( + "-" + ) + ) : ( + + )} + + + + + + Planned Finish + + + {isReadyProjectDetail ? ( + item.activity.planned_end ? ( + moment(item.activity.planned_end).format("D MMMM YYYY") + ) : ( + "-" + ) + ) : ( + + )} + + + + + + Estimated Finish + + + {isReadyProjectDetail ? ( + item.activity.end_date ? ( + moment(item.activity.end_date).format("D MMMM YYYY") + ) : ( + "-" + ) + ) : ( + + )} + + +
+
+
+ +
+
+ + +
setActiveTabIdx(0)} + > + S Curve +
+ + +
setActiveTabIdx(1)} + > + Maps +
+ +
+ + +
+
+ +
+ {activeTabIdx === 1 && ( +
+ )} +
+ +
+
+ + +
+ + +
+
+ Progress +
+ {isReadySCurve ? ( + 100 ? 100 : SetplanningProgress} + /> + ) : ( + + )} +
+ {isReadySCurve ? ( + 100 || SetactualProgress > 100 + ? parseFloat( + (SetactualProgress / SetplanningProgress) * 100 + ).toFixed(0) + : SetactualProgress + } + /> + ) : ( + + )} +
+ +
+ + +
+ {isReadyOverdueActivities && ( + + )} + {isReadySCurve && ( + + )} +
+ + + + +
+ Manpower : {item.manpower} +
+ +
+ + +
+ Assigned: {item.assigned && assignedList.length} +
+ + +
+ Actual : {item.actual} +
+ +
+ +
+ + +
+ + +
setActiveTabCommentIdx(0)} + > + Behind Task +
+ + +
setActiveTabCommentIdx(1)} + > + Comment From Customer +
+ +
+
+ {activeTabCommentIdx === 0 && ( +
+ {isReadyOverdueActivities && item.overdueActivities != [] && item.overdueActivities.length > 0 ? ( + item.overdueActivities.map((overdueItem, idx) => { + let end_date; + let planned_end; + let diffDays = 0; + let message = ""; + + if (overdueItem.end_date && overdueItem.end_date !== null) { + end_date = moment(overdueItem.end_date); + planned_end = moment(overdueItem.planned_end); + diffDays = end_date.diff(planned_end, "days"); + + if (isNaN(diffDays)) { + return null; + } else { + if (diffDays > 0) { + message = `Overdue by ${diffDays + 1} days`; + } else { + return null; + } + } + } + + return ; + }) + ) : ( +
+ No overdue activity found. +
+ )} +
+ )} + {activeTabCommentIdx === 1 &&( +
+ {item.project_comment && item.project_comment != [] ? ( + item.project_comment.map((commentItem, idx) => { + + return + }) + ) : ( +
+ No comments found. +
+ )} +
+ )} +
+
+ +
+
+ +
+ +
+ ); + }); + + return ( +
+ + + + + + + {slides} + + + + + + + +
+ ); +}; + +export default DashboardProject; From ec82a512504e7fa592ee411b72d450c09c523e3c Mon Sep 17 00:00:00 2001 From: wahyuun Date: Tue, 21 Nov 2023 10:11:20 +0700 Subject: [PATCH 3/3] add route --- src/routes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes.js b/src/routes.js index 4df1073..cf1b6e7 100644 --- a/src/routes.js +++ b/src/routes.js @@ -47,15 +47,15 @@ const UserShift = React.lazy(() => import('./views/SimproV2/UserShift')); const DashboardBOD = React.lazy(() => import('./views/Dashboard/DashboardBOD')); const DashboardCustomer = React.lazy(() => import('./views/Dashboard/DashboardCustomer')); const DashboardProject = React.lazy(() => import('./views/Dashboard/DashboardProject')); +const DashboardProjectCarousell = React.lazy(() => import('./views/Dashboard/DashboardProjectCarousell')); const MapMonitoring = React.lazy(() => import('./views/MapMonitoring')); const Settings = React.lazy(() => import('./views/SimproV2/Settings')); const routes = [ { path: '/', exact: true, name: 'Home' }, - // { path: '/dashboard', name: 'Dashboard', component: Dashboard}, { path: '/dashboard', name: 'DashboardBOD', component: DashboardBOD }, - // { path: '/dashboard-customer/:PROJECT_ID/:GANTT_ID', name: 'DashboardCustomer', component: DashboardCustomer }, { path: '/dashboard-customer/:PROJECT_ID/:GANTT_ID/:SCURVE', name: 'DashboardCustomer', component: DashboardCustomer }, { path: '/dashboard-project/:PROJECT_ID/:GANTT_ID', exact: true, name: 'Dashboard Project', component: DashboardProject }, + { path: '/dashboard-project-carousell', exact: true, name: 'Dashboard Project Carousell', component: DashboardProjectCarousell }, { path: '/dashboard-project/:PROJECT_ID/:GANTT_ID/:SCURVE', exact: true, name: 'Dashboard Project', component: DashboardProject }, { path: '/projects', exact: true, name: 'Projects', component: CreatedProyek }, { path: '/projects/:id/import/activity', exact: true, name: 'Gantt Import Activity', component: GanttImportActivity },