diff --git a/Dockerfile b/Dockerfile
index f6b8ea0..6894b88 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,20 +1,22 @@
-FROM node:12
+# Stage 1
+FROM node:14 as build-stage
+
WORKDIR /react-docker
-COPY ./package*.json ./
-ENV PATH /app/node_modules/.bin:$PATH
+COPY package.json .
RUN npm install
-RUN npm i react@16.14.0
-RUN npm i react-scripts
-RUN npm audit fix
+COPY . .
+
+#ARG REACT_APP_API_BASE_URL
+#ENV REACT_APP_API_BASE_URL=$REACT_APP_API_BASE_URL
-EXPOSE 8445
+RUN npm run build
-# Comment as needed (Production / Dev)
-# [PROD] Use for Production
-# COPY . . # uncomment prod
+# Stage 2
+#FROM nginx:1.17.0-alpine
+FROM nginx:alpine
-# [DEV] Live Reload
-RUN mkdir -p /react-docker/src # comment for prod
-RUN mkdir -p /react-docker/public # comment for prod
+COPY --from=build-stage /react-docker/build /usr/share/nginx/html
+COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
+EXPOSE 80
-CMD PORT=8445 npm start #--host 0.0.0.0 --port 3000 --disableHostCheck true
+CMD nginx -g 'daemon off;'
diff --git a/docker-compose.yml b/docker-compose.yml
index b5ad061..b95feba 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,7 +1,7 @@
version: "3.3"
services:
frontend:
- container_name: ADW-Frontend
+ container_name: ct-frontend
tty: true
build:
context: .
@@ -11,6 +11,12 @@ services:
- ./src:/react-docker/src
- ./public:/react-docker/public
ports:
- - 8445:8445
+ - 8455:80
+ networks:
+ - ospro_ct
environment:
- CHOKIDAR_USEPOLLING=true
+
+networks:
+ ospro_ct:
+ driver: bridge
diff --git a/nginx/nginx.conf b/nginx/nginx.conf
new file mode 100644
index 0000000..40a878b
--- /dev/null
+++ b/nginx/nginx.conf
@@ -0,0 +1,17 @@
+server {
+
+ listen 80;
+
+ location / {
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+ try_files $uri $uri/ /index.html;
+ }
+
+ error_page 500 502 503 504 /50x.html;
+
+ location = /50x.html {
+ root /usr/share/nginx/html;
+ }
+
+}
diff --git a/src/assets/img/OSPRO.png b/src/assets/img/OSPRO.png
new file mode 100644
index 0000000..2a73619
Binary files /dev/null and b/src/assets/img/OSPRO.png differ
diff --git a/src/const/ApiConst.js b/src/const/ApiConst.js
index 4241749..99d1c34 100644
--- a/src/const/ApiConst.js
+++ b/src/const/ApiConst.js
@@ -107,7 +107,7 @@ export const API_DAILY_INFO = `${BASE_URL_GEOHR_API2}/employee.php?act=get_daily
export const API_DAILY_INFO_DETAIL = (start, length) =>
`${BASE_URL_GEOHR_API2}/employee.php?act=get_daily_detail&start=${start}&length=${length}&role_name=${roleName}`;
-export const APP_MODE = "ADW"; // KIT / ADW / NAWAKARA / IU
+export const APP_MODE = "ADW"; // ADW == Konstruksi
export const BASE_SIMPRO = "https://oslog.id/simpro-api/v1";
export const BASE_SIMPRO_V2 = "https://oslog.id/simpro-api/v2";
export const BASE_SIMPRO_LUMENNIAGA = "https://ospro-api.odm-iu.com/api";
@@ -116,7 +116,7 @@ export const TOKEN_ADW =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIxMjAyIiwiZXhwIjoxNjkxODMwNDkzfQ.DvBQIOZsdFndWsliPCZT65Y6G5Xx4vWBKz8Rhe7rvRA";
// export let BASE_OSPRO = "https://ospro-api.ospro.id";
-export let BASE_OSPRO = "https://adw-api.ospro.id";
+export let BASE_OSPRO = "https://konstruksi-api.ospro.id";
// export let BASE_OSPRO = "http://localhost:8444/adw-backend";
// export let BASE_OSPRO = "http://103.73.125.81:8444"; // ip public adw
export let BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`;
@@ -734,3 +734,28 @@ export const IMAGE_UPLOAD = `${BASE_SIMPRO_LUMEN}/image/upload`;
export const IMAGE_DELETE = (id, category) => {
return `${BASE_SIMPRO_LUMEN}/image/delete/${id}/${category}`;
}
+
+
+export const COMPANY_MANAGEMENT_ADD = `${BASE_SIMPRO_LUMEN}/company-management/add`;
+export const COMPANY_MANAGEMENT_SEARCH = `${BASE_SIMPRO_LUMEN}/company-management/search`;
+export const COMPANY_MANAGEMENT_EDIT = (id) => {
+ return `${BASE_SIMPRO_LUMEN}/company-management/update/${id}`;
+};
+export const COMPANY_MANAGEMENT_DELETE = (id) => {
+ return `${BASE_SIMPRO_LUMEN}/company-management/delete/${id}`;
+};
+export const COMPANY_MANAGEMENT_LIST = `${BASE_SIMPRO_LUMEN}/company-management/list`;
+
+
+export const MENU_COMPANY_ADD = `${BASE_SIMPRO_LUMEN}/menu-company/add-multiple`;
+export const MENU_COMPANY_MANAGEMENT = (id) => {
+ return `${BASE_SIMPRO_LUMEN}/menu-company/management/${id}`;
+};
+export const MENU_COMPANY_SEARCH = `${BASE_SIMPRO_LUMEN}/menu-company/search`;
+export const MENU_COMPANY_LIST = `${BASE_SIMPRO_LUMEN}/menu-company/list`;
+export const MENU_COMPANY_EDIT = (id) => {
+ return `${BASE_SIMPRO_LUMEN}/menu-company/update/${id}`;
+};
+export const MENU_COMPANY_DELETE = (id) => {
+ return `${BASE_SIMPRO_LUMEN}/menu-company/delete/${id}`;
+};
diff --git a/src/routes.js b/src/routes.js
index cf1b6e7..d20246d 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -50,6 +50,7 @@ const DashboardProject = React.lazy(() => import('./views/Dashboard/DashboardPro
const DashboardProjectCarousell = React.lazy(() => import('./views/Dashboard/DashboardProjectCarousell'));
const MapMonitoring = React.lazy(() => import('./views/MapMonitoring'));
const Settings = React.lazy(() => import('./views/SimproV2/Settings'));
+const CompanyManagement = React.lazy(() => import('./views/Master/MasterCompany'))
const routes = [
{ path: '/', exact: true, name: 'Home' },
{ path: '/dashboard', name: 'DashboardBOD', component: DashboardBOD },
@@ -109,6 +110,7 @@ const routes = [
{ path: '/map-monitoring', exact: true, name: 'Map Monitoring', component: MapMonitoring },
// { path: '/dashboard-project/:ID/:GANTTID', exact: true, name: 'Dashboard Project', component: DashboardProject },
{ path: '/settings', exact: true, name: 'Settings', component: Settings },
+ { path: '/company-management', exact: true, name: 'Company Management', component: CompanyManagement },
];
export default routes;
diff --git a/src/views/Dashboard/DashboardCustomer.js b/src/views/Dashboard/DashboardCustomer.js
index 7d482e8..37d0f0d 100644
--- a/src/views/Dashboard/DashboardCustomer.js
+++ b/src/views/Dashboard/DashboardCustomer.js
@@ -47,7 +47,8 @@ const DashboardCustomer = (props) => {
}
}
const { PROJECT_ID, GANTT_ID, SCURVE } = useParams();
- const URL_GANTT = `https://adw-gantt.ospro.id/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 URL_GANTT = `https://konstruksi-gantt.ospro.id/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('');
const [customerName, setCustomerName] = useState("");
diff --git a/src/views/Dashboard/DashboardProject.js b/src/views/Dashboard/DashboardProject.js
index 76ad3b6..6f19294 100644
--- a/src/views/Dashboard/DashboardProject.js
+++ b/src/views/Dashboard/DashboardProject.js
@@ -72,7 +72,7 @@ const DashboardProject = () => {
},
};
const { PROJECT_ID, GANTT_ID, SCURVE } = useParams();
- const URL_GANTT = `https://adw-gantt.ospro.id/view-mode/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`;
+ const URL_GANTT = `https://konstruksi-gantt.ospro.id/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("");
diff --git a/src/views/Dashboard/DashboardProject.js.save b/src/views/Dashboard/DashboardProject.js.save
new file mode 100644
index 0000000..f534d5f
--- /dev/null
+++ b/src/views/Dashboard/DashboardProject.js.save
@@ -0,0 +1,1698 @@
+import React, { useEffect, useMemo, useRef, useState } from "react";
+import axios from "axios";
+import { Row, Col, Button, Input } 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";
+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";
+import { Fab, Action } from "react-tiny-fab";
+import "react-tiny-fab/dist/styles.css";
+import { useHistory } 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 = () => {
+ const token = localStorage.getItem("token");
+ const HEADER = {
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${token}`,
+ },
+ };
+ const { PROJECT_ID, GANTT_ID, SCURVE } = useParams();
+ const URL_GANTT = `https://konstruksi-gantt.ospro.id17/04/2024/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("");
+ const [customerName, setCustomerName] = useState("");
+ const [plannedStart, setPlannedStart] = useState(null);
+ const [plannedFinish, setPlannedFinish] = useState(null);
+ const [plannedCost, setPlannedCost] = useState(null);
+ const [totalCost, setTotalCost] = 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(0);
+ const [actualProgress, setActualProgress] = useState(0);
+ const [currentBudget, setCurrentBudget] = useState(null);
+ const [addCostToComplete, setAddCostToComplete] = useState(null);
+ const [actualToDate, setActualToDate] = useState(null);
+ const [estAtCompletion, setEstAtCompletion] = useState(null);
+ const [bcwp, setBcwp] = useState(null);
+ const [costDeviation, setCostDeviation] = useState(null);
+ const [remToComplete, setRemToComplete] = useState(0);
+ const [totalInvoice, setTotalInvoice] = useState(null);
+ const [cashIn, setCashIn] = useState(null);
+ const [outstandingBalance, setOutstandingBalance] = useState(null);
+ const [comment, setComment] = useState("");
+ 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 [isReadyGantt, setIsReadyGantt] = 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([]);
+ const [manPower, setManPower] = useState(0);
+ const [assignedHr, setAssignedHr] = useState([]);
+ const [assignedHrCount, setAssignedHrCount] = useState(0);
+ const [actualHrCount, setActualHrCount] = useState(0);
+ const [dataGantt, setDataGantt] = useState({});
+ const [dataGanttParents, setDataGanttParents] = useState({});
+ const [isReadyGanttParents, setIsReadyGanttParents] = useState(false);
+ const [calculationStatus, setCalculationStatus] = useState(false);
+ const [isHierarchy, setIsHierarchy] = useState(false);
+ let history = useHistory();
+
+ useEffect(() => {
+ getProjectDetail();
+ getSCurve();
+ getOverdueActivities();
+ getReportDistribution();
+ getComments();
+ getGantt();
+ getGanttParents();
+ return () => {
+ console.log("unmount RenderMap");
+ };
+ }, []);
+
+ useEffect(() => {
+ if (activeTabIdx === 1) {
+ initMap();
+ }
+ }, [activeTabIdx]);
+
+ useEffect(() => {
+ async function fetchData() {
+ await Promise.all([
+ getManpower(),
+ getAssignedHR(),
+ ...(assignedHr.length > 0 ? [getActualHR()] : []),
+ ]);
+ }
+ fetchData();
+ }, []);
+
+ useEffect(() => {
+ let deviation = 0;
+ if (plannedCost && totalCost) {
+ deviation = plannedCost - totalCost;
+ }
+ setRemToComplete(deviation.toString());
+ }, [plannedCost, totalCost]);
+
+ const handleRedirect = () => {
+ history.push("/projects/" + GANTT_ID + "/" + PROJECT_ID + "/gantt");
+ };
+
+ const getManpower = async () => {
+ const url = `${BASE_OSPRO}/api/project/manpower/${PROJECT_ID}`;
+ try {
+ const response = await axios.get(url, HEADER);
+ setManPower(response.data.totalRecord);
+ } catch (error) {
+ console.error("Failed to get manpower:", error);
+ }
+ };
+
+ const getGantt = async () => {
+ setIsReadyGantt(false);
+ const url = `${BASE_OSPRO}/api/version-gantt/edit/${GANTT_ID}`;
+ try {
+ const response = await axios.get(url, HEADER);
+ setDataGantt(response);
+ if (response.data.data.hierarchy_ftth_id) {
+ setIsHierarchy(true);
+ }
+ setIsReadyGantt(true);
+ } catch (error) {
+ console.error("Failed to get gantt data:", error);
+ setIsReadyGantt(true);
+ }
+ };
+
+ const getGanttParents = async () => {
+ setIsReadyGanttParents(false);
+ const url = `${BASE_OSPRO}/api/hierarchy-ftths/tree-gantt/${GANTT_ID}`;
+ try {
+ const response = await axios.get(url, HEADER);
+ setDataGanttParents(response);
+ setIsReadyGanttParents(true);
+ } catch (error) {
+ console.error("Failed to get gantt data:", error);
+ setIsReadyGanttParents(true);
+ }
+ };
+
+ const getAssignedHR = async () => {
+ const url = `${BASE_OSPRO}/api/project/manpower/assigned/${GANTT_ID}`;
+ try {
+ const response = await axios.get(url, HEADER);
+ const today = moment();
+ const assignedList = response.data.data
+ .filter((item) =>
+ today.isBetween(moment(item.start_date), moment(item.end_date))
+ )
+ .map((item) => item.user_id);
+ setAssignedHrCount(assignedList.length);
+ setAssignedHr(assignedList);
+ } catch (error) {
+ console.error("Failed to get assigned HR:", error);
+ }
+ };
+
+ const getActualHR = async () => {
+ const dateStart = moment().startOf("day").toDate();
+ const dateEnd = moment().endOf("day").toDate();
+ try {
+ const payload = {
+ paging: { start: 0, length: -1 },
+ columns: [
+ {
+ name: "name",
+ logic_operator: "ilike",
+ value: "",
+ table_name: "m_users",
+ },
+ {
+ name: "clock_in",
+ logic_operator: "range",
+ value: dateStart,
+ value1: dateEnd,
+ },
+ ],
+ joins: [
+ {
+ name: "m_users",
+ column_join: "user_id",
+ column_results: ["name", "ktp_number"],
+ },
+ ],
+ orders: { columns: ["id"], ascending: false },
+ };
+ const url = `${BASE_OSPRO}/api/presence/search`;
+ const response = await axios.post(url, payload, HEADER);
+ const actualHrCount = response.data.data.filter((item) =>
+ assignedHr.includes(item.user_id)
+ ).length;
+ setActualHrCount(actualHrCount);
+ } catch (error) {
+ console.error("Failed to get actual HR:", error);
+ }
+ };
+
+ const getProjectDetail = async () => {
+ setIsReadyProjectDetail(false);
+ let URL = `${BASE_OSPRO}/api/project/detail/${PROJECT_ID}`;
+ if (GANTT_ID) {
+ URL = `${BASE_OSPRO}/api/project/detail/${PROJECT_ID}/${GANTT_ID}`;
+ }
+ if (SCURVE) {
+ URL = `${BASE_OSPRO}/api/project/detail/${PROJECT_ID}/${GANTT_ID}/${SCURVE}`;
+ }
+ 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(
+ SCURVE
+ ? result.data.data.mulai_proyek
+ ? result.data.data.mulai_proyek
+ : null
+ : result.data.data.header?.planned_start
+ ? result.data.data.header?.planned_start
+ : null
+ );
+ setPlannedFinish(
+ SCURVE
+ ? result.data.data.akhir_proyek
+ ? result.data.data.akhir_proyek
+ : null
+ : result.data.data.header?.planned_end
+ ? result.data.data.header?.planned_end
+ : 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
+ );
+ setPlannedCost(
+ result.data.data.rencana_biaya ? result.data.data.rencana_biaya : null
+ );
+ setIsReadyProjectDetail(true);
+ setCalculationStatus(
+ result.data.data.calculation_status
+ ? result.data.data.calculation_status
+ : false
+ );
+
+ if (
+ result.data.data.kode_sortname &&
+ result.data.data.kode_sortname !== ""
+ ) {
+ if (SCURVE) {
+ getIntegrationInvoice(result.data.data.kode_sortname, result.data.data.id);
+ } else {
+ getIntegrationInvoice(result.data.data.kode_sortname, result.data.data.id, GANTT_ID);
+ }
+ }
+ }
+ };
+
+ const getSCurve = async () => {
+ setIsReadySCurve(false);
+ let URL = `${BASE_OSPRO}/api/project/get-s-curve`;
+ if (SCURVE && SCURVE == "1" && isHierarchy) {
+ URL = `${BASE_OSPRO}/api/project/calculate-s-curve`;
+ }
+
+ const payload = {
+ project_id: PROJECT_ID.toString(),
+ gantt_id: GANTT_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);
+ 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?.budget_control
+ ) {
+ setCurrentBudget(
+ result.data.data[0].data.budget_control.current_budget?.toString()
+ );
+ setActualToDate(
+ result.data.data[0].data.budget_control.acwp?.toString()
+ );
+ setBcwp(result.data.data[0].data.budget_control.bcwp?.toString());
+ // setRemToComplete(result.data.data[0].data.budget_control.rem_to_complete?.toString())
+ setAddCostToComplete(
+ result.data.data[0].data.budget_control.add_cost_to_complete?.toString()
+ );
+ setEstAtCompletion(
+ result.data.data[0].data.budget_control.estimated_at_completion?.toString()
+ );
+ setCostDeviation(
+ result.data.data[0].data.budget_control.cost_deviation?.toString()
+ );
+ }
+ let now = new Date().toISOString().slice(0, 10);
+ let dates = result.data.data[0].data?.date;
+ let n = dates.findIndex(
+ (element) => new Date(now) < new Date(element[0])
+ );
+ if (n < 0) {
+ n = dates.length - 1;
+ }
+ if (
+ result.data.data.length > 0 &&
+ result.data.data[0].data?.percentagePlan &&
+ result.data.data[0].data?.percentagePlan.length > 0
+ ) {
+ if (SCURVE && SCURVE == "1") {
+ planningProgress =
+ result.data.data[0].data?.percentagePlan[
+ result.data.data[0].data?.percentagePlan.length - 1
+ ];
+ } else {
+ planningProgress = result.data.data[0].data?.percentagePlan[n];
+ }
+ setPlanningProgress(planningProgress);
+ }
+ if (
+ result.data.data.length > 0 &&
+ result.data.data[0].data?.percentageReal &&
+ result.data.data[0].data?.percentageReal.length > 0
+ ) {
+ 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(),
+ gantt: GANTT_ID.toString(),
+ scurve: SCURVE ? SCURVE.toString() : null,
+ 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 getIntegrationInvoice = async (kode_sortname, id, gantt_id = null) => {
+ setIsReadyIntegrationInvoice(false);
+ const URL = `${BASE_OSPRO}/api/project/get-integration-invoice`;
+ let payload = {
+ search: kode_sortname,
+ id: id
+ };
+ if (gantt_id) {
+ payload.gantt_id = gantt_id;
+ }
+ 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) {
+ 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 total_cost = result.data.data.data.total_cost;
+ let outstanding_balance = total_invoice - cash_in;
+ setTotalInvoice(total_invoice ? total_invoice.toString() : null);
+ setCashIn(cash_in ? cash_in.toString() : null);
+ total_cost = total_cost.toString().split(".")[0];
+ setTotalCost(total_cost ? total_cost.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`;
+ const payload = {
+ columns: [
+ {
+ name: "project_id",
+ logic_operator: "=",
+ value: PROJECT_ID.toString(),
+ operator: "AND",
+ },
+ {
+ name: "gantt_id",
+ logic_operator: "=",
+ value: GANTT_ID.toString(),
+ operator: "AND",
+ },
+ ],
+ joins: [
+ {
+ name: "m_users",
+ column_join: "sender_id",
+ column_results: ["name", "username"],
+ },
+ ],
+ orders: { columns: ["created_at"], ascending: false },
+ paging: { start: 0, length: -1 },
+ };
+
+ const result = await axios
+ .post(URL, payload, HEADER)
+ .then((res) => res)
+ .catch((err) => err.response);
+ console.log("getComments", result);
+ if (!result) {
+ NotificationManager.error(`Could not connect to internet.`, "Failed");
+ setIsReadyComments(true);
+ return;
+ }
+
+ if (result.status !== 200) {
+ NotificationManager.error(
+ `Get comments failed, ${result.data.message}`,
+ "Failed"
+ );
+ setIsReadyComments(true);
+ return;
+ } else if (result.status == 200 && result.data.data) {
+ setComments(result.data.data);
+ setIsReadyComments(true);
+ }
+ };
+
+ const handleSendComment = async () => {
+ // console.log('handleSendComment', comment);
+ setIsSendingComment(true);
+ if (comment === "") {
+ NotificationManager.error(
+ "Please leave a comment before you send it.",
+ "Failed"
+ );
+ setIsSendingComment(false);
+ return;
+ }
+ const URL = `${BASE_OSPRO}/api/project-comment/add`;
+ const payload = {
+ sender_id: localStorage.getItem("user_id"),
+ project_id: PROJECT_ID,
+ gantt_id: GANTT_ID,
+ comment: comment,
+ };
+ const result = await axios
+ .post(URL, payload, HEADER)
+ .then((res) => res)
+ .catch((err) => err.response);
+
+ if (!result) {
+ NotificationManager.error(`Could not connect to internet.`, "Failed");
+ setIsSendingComment(false);
+ return;
+ }
+
+ if (result.status !== 200) {
+ NotificationManager.error(
+ `Post comment failed, ${result.data.message}`,
+ "Failed"
+ );
+ setIsSendingComment(false);
+ return;
+ } else if (result.status === 200) {
+ NotificationManager.success(`Post comment success`, "Success");
+ resetInputComment();
+ getComments();
+ }
+ };
+
+ const resetInputComment = () => {
+ setComment("");
+ setIsSendingComment(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) {
+ if (reportDistribution.length > 0) {
+ reportDistribution.map((item, idx) => {
+ L.marker([item.lat, item.lon])
+ .addTo(mymap)
+ .bindPopup(PopupContent(item));
+ });
+ }
+ }
+ }, [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 },
+ event.origin
+ );
+ }
+ };
+
+ const RenderGantt = useMemo(
+ () => (
+
+ ),
+ [activeTabIdx]
+ );
+
+ const RenderComments = useMemo(() => {
+ return (
+ <>
+
+
+ {!isReadyComments &&
}
+ {isReadyComments && comments && comments.length < 1 && (
+
+ No comments found.
+
+ )}
+ {comments &&
+ comments.length > 0 &&
+ comments.map((item, idx) => (
+
+ ))}
+
+ >
+ );
+ }, [comment, comments, isSendingComment, isReadyComments]);
+
+ const RenderBehindTasks = useMemo(() => {
+ return (
+
+ {!isReadyOverdueActivities &&
}
+ {isReadyOverdueActivities &&
+ overdueActivities &&
+ overdueActivities.length < 1 && (
+
+ No overdue activity found.
+
+ )}
+ {isReadyOverdueActivities &&
+ overdueActivities &&
+ overdueActivities.length > 0 &&
+ overdueActivities.map((item, idx) => {
+ let end_date;
+ let planned_end;
+ let diffDays = 0;
+ let message = "";
+ if (item.end_date && item.end_date !== null) {
+ end_date = moment(item.end_date);
+ planned_end = moment(item.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 (
+
+ );
+ })}
+
+ );
+ }, [overdueActivities, isReadyOverdueActivities]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ Project
+
+
+ {isReadyProjectDetail &&
+ isReadyGanttParents &&
+ isReadyGantt ? (
+ (() => {
+ let parentNames = "";
+ for (
+ let i = dataGanttParents.data.data.length - 1;
+ i >= 0;
+ i--
+ ) {
+ parentNames += " - ";
+ parentNames += dataGanttParents.data.data[i].name;
+ }
+ return SCURVE && SCURVE == "1"
+ ? projectName
+ ? calculationStatus
+ ? projectName + " - S-Curve Ready"
+ : projectName + " - S-Curve Loading"
+ : null
+ : projectName +
+ parentNames +
+ " - " +
+ dataGantt.data.data.name_version;
+ })()
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Project Manager
+
+
+ {isReadyProjectDetail ? (
+ projectManagerName
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Customer
+
+
+ {isReadyProjectDetail ? (
+ customerName
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Planned Start
+
+
+ {isReadyProjectDetail ? (
+ plannedStart ? (
+ moment(plannedStart).format("D MMMM YYYY")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ Actual Start
+
+
+ {isReadyProjectDetail ? (
+ actualStart ? (
+ moment(actualStart).format("D MMMM YYYY")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ Planned Finish
+
+
+ {isReadyProjectDetail ? (
+ plannedFinish ? (
+ moment(plannedFinish).format("D MMMM YYYY")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ Estimated Finish
+
+
+ {isReadyProjectDetail ? (
+ estimatedFinish ? (
+ moment(estimatedFinish).format("D MMMM YYYY")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Current Budget
+
+
+ {isReadyProjectDetail ? (
+ plannedCost ? (
+ renderFormatRupiah(plannedCost, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+ Add Cost to Complete
+
+
+ {isReadyGantt ? (
+ dataGantt.data.data.cost_to_complete ? (
+ renderFormatRupiah(
+ dataGantt.data.data.cost_to_complete.toString(),
+ "Rp."
+ )
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ Actual to Date
+
+
+ {isReadyIntegrationInvoice ? (
+ totalCost ? (
+ renderFormatRupiah(totalCost, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+ Est. at Completion
+
+
+ {isReadyProjectDetail && isReadyGantt ? (
+ dataGantt.data.data.cost_to_complete ? (
+ renderFormatRupiah(
+ (parseInt(plannedCost) + dataGantt.data.data.cost_to_complete).toString(),
+ "Rp."
+ )
+ ) : (
+ renderFormatRupiah(
+ parseInt(plannedCost).toString(),
+ "Rp."
+ )
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ BCWP (cost vs perform)
+
+
+ {isReadySCurve ? (
+ bcwp ? (
+ renderFormatRupiah(bcwp, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+ Cost Deviation
+
+
+ {isReadyProjectDetail && isReadyGantt ? (
+ dataGantt.data.data.cost_to_complete ? (
+ renderFormatRupiah(
+ (dataGantt.data.data.cost_to_complete).toString(),
+ "Rp."
+ )
+ ) : (
+ "Rp. 0"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ Rem. to Complete
+
+
+ {isReadyProjectDetail && isReadyIntegrationInvoice ? (
+ remToComplete ? (
+ renderFormatRupiah(remToComplete, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+ Committed Cost
+
+
+ {isReadyGantt ? (
+ dataGantt.data.data.committed_cost ? (
+ renderFormatRupiah(
+ dataGantt.data.data.committed_cost.toString(),
+ "Rp."
+ )
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ setActiveTabIdx(0)}
+ >
+ S Curve
+
+
+
+ setActiveTabIdx(1)}
+ >
+ Maps
+
+
+
+
+
+
+ {/* { activeTabIdx === 0 &&
{RenderGantt}
}
+ { activeTabIdx === 1 &&
} */}
+
+ {RenderGantt}
+
+ {activeTabIdx === 1 && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ Progress
+
+ {isReadySCurve ? (
+
100 ? 100 : planningProgress}
+ />
+ ) : (
+
+ )}
+
+ {isReadySCurve ? (
+ 100
+ ? parseFloat(
+ (actualProgress / planningProgress) * 100
+ ).toFixed(2)
+ : actualProgress
+ }
+ />
+ ) : (
+
+ )}
+
+
+
+
+
+
+ {isReadyOverdueActivities && (
+
+ )}
+ {isReadySCurve && (
+
+ )}
+
+
+
+
+
+
+ Manpower : {manPower}
+
+
+
+
+
+
+ Assigned : {assignedHrCount}
+
+
+
+
+ Actual : {actualHrCount}
+
+
+
+
+
+
+
+
+ {/*
Health By Schedule
+
*/}
+
+ Total Invoice
+
+ {isReadyIntegrationInvoice ? (
+ totalInvoice ? (
+ renderFormatRupiah(totalInvoice, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+ Cash In
+
+ {isReadyIntegrationInvoice ? (
+ cashIn ? (
+ renderFormatRupiah(cashIn, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+ Outstanding Balance
+
+ {isReadyIntegrationInvoice ? (
+ outstandingBalance ? (
+ renderFormatRupiah(outstandingBalance, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+ {/*
+
setActiveTabCommentIdx(0)}>Behind Task
+
setActiveTabCommentIdx(1)}>Comment From Customer
+
*/}
+
+
+ setActiveTabCommentIdx(0)}
+ >
+ Behind Task
+
+
+
+ setActiveTabCommentIdx(1)}
+ >
+ Comment From Customer
+
+
+
+
+ {activeTabCommentIdx === 0 && RenderBehindTasks}
+ {activeTabCommentIdx === 1 && RenderComments}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DashboardProject;
diff --git a/src/views/Dashboard/DashboardProject.js.save.1 b/src/views/Dashboard/DashboardProject.js.save.1
new file mode 100644
index 0000000..6db17c7
--- /dev/null
+++ b/src/views/Dashboard/DashboardProject.js.save.1
@@ -0,0 +1,1698 @@
+import React, { useEffect, useMemo, useRef, useState } from "react";
+import axios from "axios";
+import { Row, Col, Button, Input } 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";
+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";
+import { Fab, Action } from "react-tiny-fab";
+import "react-tiny-fab/dist/styles.css";
+import { useHistory } 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 = () => {
+ const token = localStorage.getItem("token");
+ const HEADER = {
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${token}`,
+ },
+ };
+ const { PROJECT_ID, GANTT_ID, SCURVE } = useParams();
+ const URL_GANTT = `17/04/2024https://konstruksi-gantt.ospro.id/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("");
+ const [customerName, setCustomerName] = useState("");
+ const [plannedStart, setPlannedStart] = useState(null);
+ const [plannedFinish, setPlannedFinish] = useState(null);
+ const [plannedCost, setPlannedCost] = useState(null);
+ const [totalCost, setTotalCost] = 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(0);
+ const [actualProgress, setActualProgress] = useState(0);
+ const [currentBudget, setCurrentBudget] = useState(null);
+ const [addCostToComplete, setAddCostToComplete] = useState(null);
+ const [actualToDate, setActualToDate] = useState(null);
+ const [estAtCompletion, setEstAtCompletion] = useState(null);
+ const [bcwp, setBcwp] = useState(null);
+ const [costDeviation, setCostDeviation] = useState(null);
+ const [remToComplete, setRemToComplete] = useState(0);
+ const [totalInvoice, setTotalInvoice] = useState(null);
+ const [cashIn, setCashIn] = useState(null);
+ const [outstandingBalance, setOutstandingBalance] = useState(null);
+ const [comment, setComment] = useState("");
+ 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 [isReadyGantt, setIsReadyGantt] = 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([]);
+ const [manPower, setManPower] = useState(0);
+ const [assignedHr, setAssignedHr] = useState([]);
+ const [assignedHrCount, setAssignedHrCount] = useState(0);
+ const [actualHrCount, setActualHrCount] = useState(0);
+ const [dataGantt, setDataGantt] = useState({});
+ const [dataGanttParents, setDataGanttParents] = useState({});
+ const [isReadyGanttParents, setIsReadyGanttParents] = useState(false);
+ const [calculationStatus, setCalculationStatus] = useState(false);
+ const [isHierarchy, setIsHierarchy] = useState(false);
+ let history = useHistory();
+
+ useEffect(() => {
+ getProjectDetail();
+ getSCurve();
+ getOverdueActivities();
+ getReportDistribution();
+ getComments();
+ getGantt();
+ getGanttParents();
+ return () => {
+ console.log("unmount RenderMap");
+ };
+ }, []);
+
+ useEffect(() => {
+ if (activeTabIdx === 1) {
+ initMap();
+ }
+ }, [activeTabIdx]);
+
+ useEffect(() => {
+ async function fetchData() {
+ await Promise.all([
+ getManpower(),
+ getAssignedHR(),
+ ...(assignedHr.length > 0 ? [getActualHR()] : []),
+ ]);
+ }
+ fetchData();
+ }, []);
+
+ useEffect(() => {
+ let deviation = 0;
+ if (plannedCost && totalCost) {
+ deviation = plannedCost - totalCost;
+ }
+ setRemToComplete(deviation.toString());
+ }, [plannedCost, totalCost]);
+
+ const handleRedirect = () => {
+ history.push("/projects/" + GANTT_ID + "/" + PROJECT_ID + "/gantt");
+ };
+
+ const getManpower = async () => {
+ const url = `${BASE_OSPRO}/api/project/manpower/${PROJECT_ID}`;
+ try {
+ const response = await axios.get(url, HEADER);
+ setManPower(response.data.totalRecord);
+ } catch (error) {
+ console.error("Failed to get manpower:", error);
+ }
+ };
+
+ const getGantt = async () => {
+ setIsReadyGantt(false);
+ const url = `${BASE_OSPRO}/api/version-gantt/edit/${GANTT_ID}`;
+ try {
+ const response = await axios.get(url, HEADER);
+ setDataGantt(response);
+ if (response.data.data.hierarchy_ftth_id) {
+ setIsHierarchy(true);
+ }
+ setIsReadyGantt(true);
+ } catch (error) {
+ console.error("Failed to get gantt data:", error);
+ setIsReadyGantt(true);
+ }
+ };
+
+ const getGanttParents = async () => {
+ setIsReadyGanttParents(false);
+ const url = `${BASE_OSPRO}/api/hierarchy-ftths/tree-gantt/${GANTT_ID}`;
+ try {
+ const response = await axios.get(url, HEADER);
+ setDataGanttParents(response);
+ setIsReadyGanttParents(true);
+ } catch (error) {
+ console.error("Failed to get gantt data:", error);
+ setIsReadyGanttParents(true);
+ }
+ };
+
+ const getAssignedHR = async () => {
+ const url = `${BASE_OSPRO}/api/project/manpower/assigned/${GANTT_ID}`;
+ try {
+ const response = await axios.get(url, HEADER);
+ const today = moment();
+ const assignedList = response.data.data
+ .filter((item) =>
+ today.isBetween(moment(item.start_date), moment(item.end_date))
+ )
+ .map((item) => item.user_id);
+ setAssignedHrCount(assignedList.length);
+ setAssignedHr(assignedList);
+ } catch (error) {
+ console.error("Failed to get assigned HR:", error);
+ }
+ };
+
+ const getActualHR = async () => {
+ const dateStart = moment().startOf("day").toDate();
+ const dateEnd = moment().endOf("day").toDate();
+ try {
+ const payload = {
+ paging: { start: 0, length: -1 },
+ columns: [
+ {
+ name: "name",
+ logic_operator: "ilike",
+ value: "",
+ table_name: "m_users",
+ },
+ {
+ name: "clock_in",
+ logic_operator: "range",
+ value: dateStart,
+ value1: dateEnd,
+ },
+ ],
+ joins: [
+ {
+ name: "m_users",
+ column_join: "user_id",
+ column_results: ["name", "ktp_number"],
+ },
+ ],
+ orders: { columns: ["id"], ascending: false },
+ };
+ const url = `${BASE_OSPRO}/api/presence/search`;
+ const response = await axios.post(url, payload, HEADER);
+ const actualHrCount = response.data.data.filter((item) =>
+ assignedHr.includes(item.user_id)
+ ).length;
+ setActualHrCount(actualHrCount);
+ } catch (error) {
+ console.error("Failed to get actual HR:", error);
+ }
+ };
+
+ const getProjectDetail = async () => {
+ setIsReadyProjectDetail(false);
+ let URL = `${BASE_OSPRO}/api/project/detail/${PROJECT_ID}`;
+ if (GANTT_ID) {
+ URL = `${BASE_OSPRO}/api/project/detail/${PROJECT_ID}/${GANTT_ID}`;
+ }
+ if (SCURVE) {
+ URL = `${BASE_OSPRO}/api/project/detail/${PROJECT_ID}/${GANTT_ID}/${SCURVE}`;
+ }
+ 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(
+ SCURVE
+ ? result.data.data.mulai_proyek
+ ? result.data.data.mulai_proyek
+ : null
+ : result.data.data.header?.planned_start
+ ? result.data.data.header?.planned_start
+ : null
+ );
+ setPlannedFinish(
+ SCURVE
+ ? result.data.data.akhir_proyek
+ ? result.data.data.akhir_proyek
+ : null
+ : result.data.data.header?.planned_end
+ ? result.data.data.header?.planned_end
+ : 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
+ );
+ setPlannedCost(
+ result.data.data.rencana_biaya ? result.data.data.rencana_biaya : null
+ );
+ setIsReadyProjectDetail(true);
+ setCalculationStatus(
+ result.data.data.calculation_status
+ ? result.data.data.calculation_status
+ : false
+ );
+
+ if (
+ result.data.data.kode_sortname &&
+ result.data.data.kode_sortname !== ""
+ ) {
+ if (SCURVE) {
+ getIntegrationInvoice(result.data.data.kode_sortname, result.data.data.id);
+ } else {
+ getIntegrationInvoice(result.data.data.kode_sortname, result.data.data.id, GANTT_ID);
+ }
+ }
+ }
+ };
+
+ const getSCurve = async () => {
+ setIsReadySCurve(false);
+ let URL = `${BASE_OSPRO}/api/project/get-s-curve`;
+ if (SCURVE && SCURVE == "1" && isHierarchy) {
+ URL = `${BASE_OSPRO}/api/project/calculate-s-curve`;
+ }
+
+ const payload = {
+ project_id: PROJECT_ID.toString(),
+ gantt_id: GANTT_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);
+ 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?.budget_control
+ ) {
+ setCurrentBudget(
+ result.data.data[0].data.budget_control.current_budget?.toString()
+ );
+ setActualToDate(
+ result.data.data[0].data.budget_control.acwp?.toString()
+ );
+ setBcwp(result.data.data[0].data.budget_control.bcwp?.toString());
+ // setRemToComplete(result.data.data[0].data.budget_control.rem_to_complete?.toString())
+ setAddCostToComplete(
+ result.data.data[0].data.budget_control.add_cost_to_complete?.toString()
+ );
+ setEstAtCompletion(
+ result.data.data[0].data.budget_control.estimated_at_completion?.toString()
+ );
+ setCostDeviation(
+ result.data.data[0].data.budget_control.cost_deviation?.toString()
+ );
+ }
+ let now = new Date().toISOString().slice(0, 10);
+ let dates = result.data.data[0].data?.date;
+ let n = dates.findIndex(
+ (element) => new Date(now) < new Date(element[0])
+ );
+ if (n < 0) {
+ n = dates.length - 1;
+ }
+ if (
+ result.data.data.length > 0 &&
+ result.data.data[0].data?.percentagePlan &&
+ result.data.data[0].data?.percentagePlan.length > 0
+ ) {
+ if (SCURVE && SCURVE == "1") {
+ planningProgress =
+ result.data.data[0].data?.percentagePlan[
+ result.data.data[0].data?.percentagePlan.length - 1
+ ];
+ } else {
+ planningProgress = result.data.data[0].data?.percentagePlan[n];
+ }
+ setPlanningProgress(planningProgress);
+ }
+ if (
+ result.data.data.length > 0 &&
+ result.data.data[0].data?.percentageReal &&
+ result.data.data[0].data?.percentageReal.length > 0
+ ) {
+ 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(),
+ gantt: GANTT_ID.toString(),
+ scurve: SCURVE ? SCURVE.toString() : null,
+ 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 getIntegrationInvoice = async (kode_sortname, id, gantt_id = null) => {
+ setIsReadyIntegrationInvoice(false);
+ const URL = `${BASE_OSPRO}/api/project/get-integration-invoice`;
+ let payload = {
+ search: kode_sortname,
+ id: id
+ };
+ if (gantt_id) {
+ payload.gantt_id = gantt_id;
+ }
+ 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) {
+ 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 total_cost = result.data.data.data.total_cost;
+ let outstanding_balance = total_invoice - cash_in;
+ setTotalInvoice(total_invoice ? total_invoice.toString() : null);
+ setCashIn(cash_in ? cash_in.toString() : null);
+ total_cost = total_cost.toString().split(".")[0];
+ setTotalCost(total_cost ? total_cost.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`;
+ const payload = {
+ columns: [
+ {
+ name: "project_id",
+ logic_operator: "=",
+ value: PROJECT_ID.toString(),
+ operator: "AND",
+ },
+ {
+ name: "gantt_id",
+ logic_operator: "=",
+ value: GANTT_ID.toString(),
+ operator: "AND",
+ },
+ ],
+ joins: [
+ {
+ name: "m_users",
+ column_join: "sender_id",
+ column_results: ["name", "username"],
+ },
+ ],
+ orders: { columns: ["created_at"], ascending: false },
+ paging: { start: 0, length: -1 },
+ };
+
+ const result = await axios
+ .post(URL, payload, HEADER)
+ .then((res) => res)
+ .catch((err) => err.response);
+ console.log("getComments", result);
+ if (!result) {
+ NotificationManager.error(`Could not connect to internet.`, "Failed");
+ setIsReadyComments(true);
+ return;
+ }
+
+ if (result.status !== 200) {
+ NotificationManager.error(
+ `Get comments failed, ${result.data.message}`,
+ "Failed"
+ );
+ setIsReadyComments(true);
+ return;
+ } else if (result.status == 200 && result.data.data) {
+ setComments(result.data.data);
+ setIsReadyComments(true);
+ }
+ };
+
+ const handleSendComment = async () => {
+ // console.log('handleSendComment', comment);
+ setIsSendingComment(true);
+ if (comment === "") {
+ NotificationManager.error(
+ "Please leave a comment before you send it.",
+ "Failed"
+ );
+ setIsSendingComment(false);
+ return;
+ }
+ const URL = `${BASE_OSPRO}/api/project-comment/add`;
+ const payload = {
+ sender_id: localStorage.getItem("user_id"),
+ project_id: PROJECT_ID,
+ gantt_id: GANTT_ID,
+ comment: comment,
+ };
+ const result = await axios
+ .post(URL, payload, HEADER)
+ .then((res) => res)
+ .catch((err) => err.response);
+
+ if (!result) {
+ NotificationManager.error(`Could not connect to internet.`, "Failed");
+ setIsSendingComment(false);
+ return;
+ }
+
+ if (result.status !== 200) {
+ NotificationManager.error(
+ `Post comment failed, ${result.data.message}`,
+ "Failed"
+ );
+ setIsSendingComment(false);
+ return;
+ } else if (result.status === 200) {
+ NotificationManager.success(`Post comment success`, "Success");
+ resetInputComment();
+ getComments();
+ }
+ };
+
+ const resetInputComment = () => {
+ setComment("");
+ setIsSendingComment(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) {
+ if (reportDistribution.length > 0) {
+ reportDistribution.map((item, idx) => {
+ L.marker([item.lat, item.lon])
+ .addTo(mymap)
+ .bindPopup(PopupContent(item));
+ });
+ }
+ }
+ }, [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 },
+ event.origin
+ );
+ }
+ };
+
+ const RenderGantt = useMemo(
+ () => (
+
+ ),
+ [activeTabIdx]
+ );
+
+ const RenderComments = useMemo(() => {
+ return (
+ <>
+
+
+ {!isReadyComments &&
}
+ {isReadyComments && comments && comments.length < 1 && (
+
+ No comments found.
+
+ )}
+ {comments &&
+ comments.length > 0 &&
+ comments.map((item, idx) => (
+
+ ))}
+
+ >
+ );
+ }, [comment, comments, isSendingComment, isReadyComments]);
+
+ const RenderBehindTasks = useMemo(() => {
+ return (
+
+ {!isReadyOverdueActivities &&
}
+ {isReadyOverdueActivities &&
+ overdueActivities &&
+ overdueActivities.length < 1 && (
+
+ No overdue activity found.
+
+ )}
+ {isReadyOverdueActivities &&
+ overdueActivities &&
+ overdueActivities.length > 0 &&
+ overdueActivities.map((item, idx) => {
+ let end_date;
+ let planned_end;
+ let diffDays = 0;
+ let message = "";
+ if (item.end_date && item.end_date !== null) {
+ end_date = moment(item.end_date);
+ planned_end = moment(item.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 (
+
+ );
+ })}
+
+ );
+ }, [overdueActivities, isReadyOverdueActivities]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ Project
+
+
+ {isReadyProjectDetail &&
+ isReadyGanttParents &&
+ isReadyGantt ? (
+ (() => {
+ let parentNames = "";
+ for (
+ let i = dataGanttParents.data.data.length - 1;
+ i >= 0;
+ i--
+ ) {
+ parentNames += " - ";
+ parentNames += dataGanttParents.data.data[i].name;
+ }
+ return SCURVE && SCURVE == "1"
+ ? projectName
+ ? calculationStatus
+ ? projectName + " - S-Curve Ready"
+ : projectName + " - S-Curve Loading"
+ : null
+ : projectName +
+ parentNames +
+ " - " +
+ dataGantt.data.data.name_version;
+ })()
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Project Manager
+
+
+ {isReadyProjectDetail ? (
+ projectManagerName
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Customer
+
+
+ {isReadyProjectDetail ? (
+ customerName
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Planned Start
+
+
+ {isReadyProjectDetail ? (
+ plannedStart ? (
+ moment(plannedStart).format("D MMMM YYYY")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ Actual Start
+
+
+ {isReadyProjectDetail ? (
+ actualStart ? (
+ moment(actualStart).format("D MMMM YYYY")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ Planned Finish
+
+
+ {isReadyProjectDetail ? (
+ plannedFinish ? (
+ moment(plannedFinish).format("D MMMM YYYY")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ Estimated Finish
+
+
+ {isReadyProjectDetail ? (
+ estimatedFinish ? (
+ moment(estimatedFinish).format("D MMMM YYYY")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Current Budget
+
+
+ {isReadyProjectDetail ? (
+ plannedCost ? (
+ renderFormatRupiah(plannedCost, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+ Add Cost to Complete
+
+
+ {isReadyGantt ? (
+ dataGantt.data.data.cost_to_complete ? (
+ renderFormatRupiah(
+ dataGantt.data.data.cost_to_complete.toString(),
+ "Rp."
+ )
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ Actual to Date
+
+
+ {isReadyIntegrationInvoice ? (
+ totalCost ? (
+ renderFormatRupiah(totalCost, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+ Est. at Completion
+
+
+ {isReadyProjectDetail && isReadyGantt ? (
+ dataGantt.data.data.cost_to_complete ? (
+ renderFormatRupiah(
+ (parseInt(plannedCost) + dataGantt.data.data.cost_to_complete).toString(),
+ "Rp."
+ )
+ ) : (
+ renderFormatRupiah(
+ parseInt(plannedCost).toString(),
+ "Rp."
+ )
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ BCWP (cost vs perform)
+
+
+ {isReadySCurve ? (
+ bcwp ? (
+ renderFormatRupiah(bcwp, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+ Cost Deviation
+
+
+ {isReadyProjectDetail && isReadyGantt ? (
+ dataGantt.data.data.cost_to_complete ? (
+ renderFormatRupiah(
+ (dataGantt.data.data.cost_to_complete).toString(),
+ "Rp."
+ )
+ ) : (
+ "Rp. 0"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+ Rem. to Complete
+
+
+ {isReadyProjectDetail && isReadyIntegrationInvoice ? (
+ remToComplete ? (
+ renderFormatRupiah(remToComplete, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+ Committed Cost
+
+
+ {isReadyGantt ? (
+ dataGantt.data.data.committed_cost ? (
+ renderFormatRupiah(
+ dataGantt.data.data.committed_cost.toString(),
+ "Rp."
+ )
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ setActiveTabIdx(0)}
+ >
+ S Curve
+
+
+
+ setActiveTabIdx(1)}
+ >
+ Maps
+
+
+
+
+
+
+ {/* { activeTabIdx === 0 &&
{RenderGantt}
}
+ { activeTabIdx === 1 &&
} */}
+
+ {RenderGantt}
+
+ {activeTabIdx === 1 && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ Progress
+
+ {isReadySCurve ? (
+
100 ? 100 : planningProgress}
+ />
+ ) : (
+
+ )}
+
+ {isReadySCurve ? (
+ 100
+ ? parseFloat(
+ (actualProgress / planningProgress) * 100
+ ).toFixed(2)
+ : actualProgress
+ }
+ />
+ ) : (
+
+ )}
+
+
+
+
+
+
+ {isReadyOverdueActivities && (
+
+ )}
+ {isReadySCurve && (
+
+ )}
+
+
+
+
+
+
+ Manpower : {manPower}
+
+
+
+
+
+
+ Assigned : {assignedHrCount}
+
+
+
+
+ Actual : {actualHrCount}
+
+
+
+
+
+
+
+
+ {/*
Health By Schedule
+
*/}
+
+ Total Invoice
+
+ {isReadyIntegrationInvoice ? (
+ totalInvoice ? (
+ renderFormatRupiah(totalInvoice, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+ Cash In
+
+ {isReadyIntegrationInvoice ? (
+ cashIn ? (
+ renderFormatRupiah(cashIn, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+ Outstanding Balance
+
+ {isReadyIntegrationInvoice ? (
+ outstandingBalance ? (
+ renderFormatRupiah(outstandingBalance, "Rp.")
+ ) : (
+ "-"
+ )
+ ) : (
+
+ )}
+
+
+
+
+
+
+
+
+ {/*
+
setActiveTabCommentIdx(0)}>Behind Task
+
setActiveTabCommentIdx(1)}>Comment From Customer
+
*/}
+
+
+ setActiveTabCommentIdx(0)}
+ >
+ Behind Task
+
+
+
+ setActiveTabCommentIdx(1)}
+ >
+ Comment From Customer
+
+
+
+
+ {activeTabCommentIdx === 0 && RenderBehindTasks}
+ {activeTabCommentIdx === 1 && RenderComments}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DashboardProject;
diff --git a/src/views/DashboardProject/ganttDashboard.js b/src/views/DashboardProject/ganttDashboard.js
index 5fe6faa..49eb582 100644
--- a/src/views/DashboardProject/ganttDashboard.js
+++ b/src/views/DashboardProject/ganttDashboard.js
@@ -11,7 +11,7 @@ const token = localStorage.getItem("token")
const Gantt = (props) => {
const { ID } = useParams();
const { GANTTID } = useParams();
-const url = `https://ospro-gantt.ospro.id/src/index.html?base_url=https://ospro-api.ospro.id/api&gantt_id=${GANTTID}&proyek_id=${ID}&token=${token}&ro=0`
+const url = `https://konstruksi-gantt.ospro.id/view-mode/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`
const RenderGantt = () => (