|
|
|
@ -30,12 +30,8 @@ import {
|
|
|
|
|
} from "./Components"; |
|
|
|
|
import { Fab, Action } from "react-tiny-fab"; |
|
|
|
|
import "react-tiny-fab/dist/styles.css"; |
|
|
|
|
import { useHistory, useParams } from "react-router-dom"; |
|
|
|
|
import { Icon } from '@iconify/react'; |
|
|
|
|
import arrowLeft from '@iconify/icons-ion/ios-arrow-back'; |
|
|
|
|
|
|
|
|
|
import { useHistory, useLocation, useParams } from "react-router-dom"; |
|
|
|
|
const { TextArea } = Input; |
|
|
|
|
|
|
|
|
|
const styles = { |
|
|
|
|
cardContainer: { |
|
|
|
|
backgroundColor: "#F8F8F8", |
|
|
|
@ -59,12 +55,10 @@ const styles = {
|
|
|
|
|
cardTitle: { color: "#444444", fontSize: 16, fontWeight: "bold" }, |
|
|
|
|
cardSubtitle: { color: "#888888", fontSize: 12 }, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const center = { |
|
|
|
|
lat: -6.2, |
|
|
|
|
lng: 106.816666, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const DashboardProject = (props) => { |
|
|
|
|
let role_id = 0, user_id = 0, isLogin = false, token = '', company_id = 0, all_project = null, role_name = '', hierarchy = [], user_name = ''; |
|
|
|
|
if (props && props.role_id && props.user_id) { |
|
|
|
@ -86,8 +80,8 @@ const DashboardProject = (props) => {
|
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
const { PROJECT_ID, GANTT_ID, SCURVE } = useParams(); |
|
|
|
|
const URL_GANTT = `https://project-gantt.ospro.id/view-mode/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1&role_name=${role_name}&company_id=${company_id}`; |
|
|
|
|
// const URL_GANTT = `http://localhost:8444/generic-ospro-gantt/view-mode/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1&role_name=${role_name}&company_id=${company_id}`;
|
|
|
|
|
// const URL_GANTT = `https://project-gantt.ospro.id/view-mode/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1&role_name=${role_name}&company_id=${company_id}`;
|
|
|
|
|
const URL_GANTT = `http://localhost:8444/generic-ospro-gantt/view-mode/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1&role_name=${role_name}&company_id=${company_id}`; |
|
|
|
|
const mapRef = useRef(); |
|
|
|
|
const [projectName, setProjectName] = useState(""); |
|
|
|
|
const [projectManagerName, setProjectManagerName] = useState(""); |
|
|
|
@ -140,10 +134,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
const [isReadyGanttParents, setIsReadyGanttParents] = useState(false); |
|
|
|
|
const [calculationStatus, setCalculationStatus] = useState(false); |
|
|
|
|
const [isHierarchy, setIsHierarchy] = useState(null); |
|
|
|
|
const [dataOverdue, setDataOverdue] = useState(null); |
|
|
|
|
const [projectType, setProjectType] = useState(null); |
|
|
|
|
let history = useHistory(); |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
getProjectDetail(); |
|
|
|
|
getOverdueActivities(); |
|
|
|
@ -152,28 +143,22 @@ const DashboardProject = (props) => {
|
|
|
|
|
getGantt(); |
|
|
|
|
getGanttParents(); |
|
|
|
|
return () => { |
|
|
|
|
console.log("unmount RenderMap"); |
|
|
|
|
}; |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
if (isHierarchy != null) { |
|
|
|
|
getSCurve(); |
|
|
|
|
} |
|
|
|
|
window.addEventListener("message", handleIframeMessage); |
|
|
|
|
window.addEventListener('message', handleGetOverdue); |
|
|
|
|
return () => { |
|
|
|
|
window.removeEventListener("message", handleIframeMessage); |
|
|
|
|
window.removeEventListener("message", handleGetOverdue); |
|
|
|
|
}; |
|
|
|
|
}, [isHierarchy]); |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
if (activeTabIdx === 1) { |
|
|
|
|
initMap(); |
|
|
|
|
} |
|
|
|
|
}, [activeTabIdx]); |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
async function fetchData() { |
|
|
|
|
await Promise.all([ |
|
|
|
@ -184,34 +169,24 @@ const DashboardProject = (props) => {
|
|
|
|
|
} |
|
|
|
|
fetchData(); |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
let deviation = 0; |
|
|
|
|
// Convert plannedCost and totalCost to numbers
|
|
|
|
|
const plannedCostNumber = parseFloat(plannedCost); |
|
|
|
|
const totalCostNumber = parseFloat(totalCost); |
|
|
|
|
|
|
|
|
|
// Check if conversion is successful
|
|
|
|
|
if (!isNaN(plannedCostNumber) && !isNaN(totalCostNumber)) { |
|
|
|
|
deviation = plannedCostNumber - totalCostNumber; |
|
|
|
|
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}`; |
|
|
|
@ -225,11 +200,9 @@ const DashboardProject = (props) => {
|
|
|
|
|
} |
|
|
|
|
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}`; |
|
|
|
@ -238,11 +211,9 @@ const DashboardProject = (props) => {
|
|
|
|
|
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 { |
|
|
|
@ -256,10 +227,8 @@ const DashboardProject = (props) => {
|
|
|
|
|
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(); |
|
|
|
@ -299,7 +268,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
console.error("Failed to get actual HR:", error); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const getProjectDetail = async () => { |
|
|
|
|
setIsReadyProjectDetail(false); |
|
|
|
|
let URL = `${BASE_OSPRO}/api/project/detail/${PROJECT_ID}`; |
|
|
|
@ -313,13 +281,11 @@ const DashboardProject = (props) => {
|
|
|
|
|
.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}`, |
|
|
|
@ -328,8 +294,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
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 : "-" |
|
|
|
@ -357,7 +321,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
? result.data.data.header?.planned_end |
|
|
|
|
: null |
|
|
|
|
); |
|
|
|
|
setProjectType(result.data.data.type_proyek_id); |
|
|
|
|
setActualStart( |
|
|
|
|
result.data.data.header?.start_date |
|
|
|
|
? result.data.data.header.start_date |
|
|
|
@ -377,7 +340,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
? result.data.data.calculation_status |
|
|
|
|
: false |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if ( |
|
|
|
|
result.data.data.kode_sortname && |
|
|
|
|
result.data.data.kode_sortname !== "" |
|
|
|
@ -390,14 +352,12 @@ const DashboardProject = (props) => {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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(), |
|
|
|
@ -413,7 +373,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
setIsReadySCurve(true); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (result.status !== 200) { |
|
|
|
|
NotificationManager.error( |
|
|
|
|
`Get S Curve failed, ${result.data.message}`, |
|
|
|
@ -425,7 +384,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
let selisihProgress = 0; |
|
|
|
|
let planningProgress = 0; |
|
|
|
|
let actualProgress = 0; |
|
|
|
|
console.log("test flow ", result.data.data); |
|
|
|
|
let statusHealthBySchedule = "on-schedule"; |
|
|
|
|
if ( |
|
|
|
|
result.data.data.length > 0 && |
|
|
|
@ -449,13 +407,21 @@ const DashboardProject = (props) => {
|
|
|
|
|
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 |
|
|
|
|
) { |
|
|
|
|
planningProgress = result.data.data[0].data?.percentagePlan[result.data.data[0].data?.percentagePlan.length - 1]; |
|
|
|
|
setPlanningProgress(Math.ceil(planningProgress)); |
|
|
|
|
planningProgress = result.data.data[0].data?.percentagePlan[n]; |
|
|
|
|
setPlanningProgress(planningProgress); |
|
|
|
|
} |
|
|
|
|
if ( |
|
|
|
|
result.data.data.length > 0 && |
|
|
|
@ -468,19 +434,16 @@ const DashboardProject = (props) => {
|
|
|
|
|
]; |
|
|
|
|
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`; |
|
|
|
@ -494,13 +457,11 @@ const DashboardProject = (props) => {
|
|
|
|
|
.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}`, |
|
|
|
@ -516,7 +477,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
setIsReadyOverdueActivities(true); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const getIntegrationInvoice = async (kode_sortname, id, gantt_id = null) => { |
|
|
|
|
setIsReadyIntegrationInvoice(false); |
|
|
|
|
const URL = `${BASE_OSPRO}/api/project/get-integration-invoice`; |
|
|
|
@ -531,13 +491,11 @@ const DashboardProject = (props) => {
|
|
|
|
|
.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}`, |
|
|
|
@ -562,7 +520,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
setIsReadyIntegrationInvoice(true); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const getReportDistribution = async () => { |
|
|
|
|
setIsReadyReportDistribution(false); |
|
|
|
|
const URL = `${BASE_OSPRO}/api/project/get-report-distribution`; |
|
|
|
@ -578,13 +535,11 @@ const DashboardProject = (props) => {
|
|
|
|
|
.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}`, |
|
|
|
@ -597,7 +552,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
setIsReadyReportDistribution(true); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const getComments = async () => { |
|
|
|
|
setIsReadyComments(false); |
|
|
|
|
const URL = `${BASE_OSPRO}/api/project-comment/search`; |
|
|
|
@ -626,18 +580,15 @@ const DashboardProject = (props) => {
|
|
|
|
|
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}`, |
|
|
|
@ -650,9 +601,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
setIsReadyComments(true); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const handleSendComment = async () => { |
|
|
|
|
// console.log('handleSendComment', comment);
|
|
|
|
|
setIsSendingComment(true); |
|
|
|
|
if (comment === "") { |
|
|
|
|
NotificationManager.error( |
|
|
|
@ -673,13 +622,11 @@ const DashboardProject = (props) => {
|
|
|
|
|
.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}`, |
|
|
|
@ -693,29 +640,21 @@ const DashboardProject = (props) => {
|
|
|
|
|
getComments(); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const resetInputComment = () => { |
|
|
|
|
setComment(""); |
|
|
|
|
setIsSendingComment(false); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const handleBack = async () => { |
|
|
|
|
window.parent.location.reload(); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const initMap = () => { |
|
|
|
|
let mymap = L.map("map-area", { |
|
|
|
|
center: center, |
|
|
|
|
zoom: 13, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
setMymap(mymap); |
|
|
|
|
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { |
|
|
|
|
attribution: |
|
|
|
|
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', |
|
|
|
|
}).addTo(mymap); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
if (mymap) { |
|
|
|
|
if (reportDistribution.length > 0) { |
|
|
|
@ -727,21 +666,17 @@ const DashboardProject = (props) => {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}, [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 }, |
|
|
|
@ -749,13 +684,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const handleGetOverdue = (event) => { |
|
|
|
|
if (event.data.type === 'dataOverdueUpdate') { |
|
|
|
|
setDataOverdue(event.data.dataOverdue); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const RenderGantt = useMemo( |
|
|
|
|
() => ( |
|
|
|
|
<iframe |
|
|
|
@ -772,7 +700,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
), |
|
|
|
|
[activeTabIdx] |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const RenderComments = useMemo(() => { |
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
@ -827,108 +754,61 @@ const DashboardProject = (props) => {
|
|
|
|
|
</> |
|
|
|
|
); |
|
|
|
|
}, [comment, comments, isSendingComment, isReadyComments]); |
|
|
|
|
|
|
|
|
|
const RenderBehindTasks = useMemo(() => { |
|
|
|
|
return SCURVE ? |
|
|
|
|
( |
|
|
|
|
<div> |
|
|
|
|
{!isReadyOverdueActivities && <ListLoader />} |
|
|
|
|
{isReadyOverdueActivities && |
|
|
|
|
overdueActivities && |
|
|
|
|
overdueActivities.length < 1 && ( |
|
|
|
|
<div |
|
|
|
|
style={{ |
|
|
|
|
flex: 1, |
|
|
|
|
textAlign: "center", |
|
|
|
|
color: "#E80053", |
|
|
|
|
marginTop: 50, |
|
|
|
|
marginBottom: 50, |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
No overdue activity found. |
|
|
|
|
</div> |
|
|
|
|
)} |
|
|
|
|
{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; |
|
|
|
|
return ( |
|
|
|
|
<div> |
|
|
|
|
{!isReadyOverdueActivities && <ListLoader />} |
|
|
|
|
{isReadyOverdueActivities && |
|
|
|
|
overdueActivities && |
|
|
|
|
overdueActivities.length < 1 && ( |
|
|
|
|
<div |
|
|
|
|
style={{ |
|
|
|
|
flex: 1, |
|
|
|
|
textAlign: "center", |
|
|
|
|
color: "#E80053", |
|
|
|
|
marginTop: 50, |
|
|
|
|
marginBottom: 50, |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
No overdue activity found. |
|
|
|
|
</div> |
|
|
|
|
)} |
|
|
|
|
{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 { |
|
|
|
|
if (diffDays > 0) { |
|
|
|
|
message = `Overdue by ${diffDays + 1} days`; |
|
|
|
|
} else { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return ( |
|
|
|
|
<BehindTaskItem key={idx} name={item.name + ' - ' + item.name_version} message={message} scurve={SCURVE} proyek_id={PROJECT_ID} gantt_id={item.version_gantt_id} /> |
|
|
|
|
); |
|
|
|
|
})} |
|
|
|
|
</div> |
|
|
|
|
) |
|
|
|
|
: |
|
|
|
|
( |
|
|
|
|
<div> |
|
|
|
|
{!dataOverdue && <ListLoader />} |
|
|
|
|
{dataOverdue && |
|
|
|
|
dataOverdue.length < 1 && ( |
|
|
|
|
<div |
|
|
|
|
style={{ |
|
|
|
|
flex: 1, |
|
|
|
|
textAlign: "center", |
|
|
|
|
color: "#E80053", |
|
|
|
|
marginTop: 50, |
|
|
|
|
marginBottom: 50, |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
No overdue activity found. |
|
|
|
|
</div> |
|
|
|
|
)} |
|
|
|
|
{dataOverdue && |
|
|
|
|
dataOverdue.length > 0 && |
|
|
|
|
dataOverdue.map((item, idx) => { |
|
|
|
|
let message = `Overdue by ${item.overdue} days`; |
|
|
|
|
return ( |
|
|
|
|
<BehindTaskItem key={idx} name={item.text} message={message} /> |
|
|
|
|
); |
|
|
|
|
})} |
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
}, [dataOverdue, isReadyOverdueActivities, overdueActivities]); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
return ( |
|
|
|
|
<BehindTaskItem key={idx} name={item.name} message={message} /> |
|
|
|
|
); |
|
|
|
|
})} |
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
}, [overdueActivities, isReadyOverdueActivities]); |
|
|
|
|
return ( |
|
|
|
|
<div style={{ marginLeft: -25, marginRight: -25 }}> |
|
|
|
|
<NotificationContainer /> |
|
|
|
|
<Row> |
|
|
|
|
<Col span={18}> |
|
|
|
|
<Row> |
|
|
|
|
{ |
|
|
|
|
!SCURVE ? |
|
|
|
|
<Col span={2}> |
|
|
|
|
<Button |
|
|
|
|
style={{ |
|
|
|
|
height: "100%", |
|
|
|
|
width: "100%", |
|
|
|
|
fontSize: "18px" |
|
|
|
|
}} |
|
|
|
|
onClick={handleBack} |
|
|
|
|
type="primary" |
|
|
|
|
> |
|
|
|
|
<Icon icon={arrowLeft} style={{ fontSize: "20px" }} /> Back |
|
|
|
|
</Button> |
|
|
|
|
</Col> : null |
|
|
|
|
} |
|
|
|
|
<Col span={!SCURVE ? 6 : 8}> |
|
|
|
|
<Col span={8}> |
|
|
|
|
<div |
|
|
|
|
style={{ |
|
|
|
|
border: "solid", |
|
|
|
@ -1096,7 +976,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
</Col> |
|
|
|
|
</Row> |
|
|
|
|
<Row> |
|
|
|
|
<Col span={SCURVE ? 8 : projectType != 9 ? 8 : 10}> |
|
|
|
|
<Col span={SCURVE ? 8 : 10}> |
|
|
|
|
<div |
|
|
|
|
style={{ |
|
|
|
|
border: "solid", |
|
|
|
@ -1221,7 +1101,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</Col> |
|
|
|
|
{SCURVE || projectType != 9 ? ( |
|
|
|
|
{SCURVE ? ( |
|
|
|
|
<Col span={16}> |
|
|
|
|
<div |
|
|
|
|
style={{ |
|
|
|
@ -1360,17 +1240,15 @@ const DashboardProject = (props) => {
|
|
|
|
|
BCWP (cost vs perform) |
|
|
|
|
</Col> |
|
|
|
|
<Col span={5} style={{ fontSize: 11 }}> |
|
|
|
|
{isReadySCurve ? |
|
|
|
|
( |
|
|
|
|
planningProgress > 100 || actualProgress > 100 |
|
|
|
|
? |
|
|
|
|
renderFormatRupiah(((parseInt(plannedCost) * parseFloat((actualProgress / planningProgress) * 100).toFixed(0)) / 100).toString(), "Rp.") |
|
|
|
|
: |
|
|
|
|
renderFormatRupiah(((parseInt(plannedCost) * parseInt(actualProgress)) / 100).toString(), "Rp.") |
|
|
|
|
) |
|
|
|
|
: ( |
|
|
|
|
{isReadySCurve ? ( |
|
|
|
|
bcwp ? ( |
|
|
|
|
renderFormatRupiah(bcwp, "Rp.") |
|
|
|
|
) : ( |
|
|
|
|
"-" |
|
|
|
|
)} |
|
|
|
|
) |
|
|
|
|
) : ( |
|
|
|
|
<SingleTextLoader width={100} height={10} /> |
|
|
|
|
)} |
|
|
|
|
</Col> |
|
|
|
|
<Col |
|
|
|
|
span={7} |
|
|
|
@ -1778,5 +1656,4 @@ const DashboardProject = (props) => {
|
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
export default DashboardProject; |