|
|
|
@ -30,7 +30,9 @@ import {
|
|
|
|
|
} from "./Components"; |
|
|
|
|
import { Fab, Action } from "react-tiny-fab"; |
|
|
|
|
import "react-tiny-fab/dist/styles.css"; |
|
|
|
|
import { useHistory, useLocation, useParams } from "react-router-dom"; |
|
|
|
|
import { useHistory, useParams } from "react-router-dom"; |
|
|
|
|
import { Icon } from '@iconify/react'; |
|
|
|
|
import arrowLeft from '@iconify/icons-ion/ios-arrow-back'; |
|
|
|
|
|
|
|
|
|
const { TextArea } = Input; |
|
|
|
|
|
|
|
|
@ -64,8 +66,7 @@ const center = {
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const DashboardProject = (props) => { |
|
|
|
|
|
|
|
|
|
let role_id = 0, user_id = 0, isLogin = false, token = '', company_id = 0, all_project = null, role_name='', hierarchy=[], user_name=''; |
|
|
|
|
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) { |
|
|
|
|
role_id = props.role_id; |
|
|
|
|
user_id = props.user_id; |
|
|
|
@ -78,7 +79,6 @@ const DashboardProject = (props) => {
|
|
|
|
|
hierarchy = props.hierarchy; |
|
|
|
|
user_name = props.user_name; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const HEADER = { |
|
|
|
|
headers: { |
|
|
|
|
"Content-Type": "application/json", |
|
|
|
@ -87,7 +87,7 @@ 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 = `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,6 +140,8 @@ 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(() => { |
|
|
|
@ -150,6 +152,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
getGantt(); |
|
|
|
|
getGanttParents(); |
|
|
|
|
return () => { |
|
|
|
|
console.log("unmount RenderMap"); |
|
|
|
|
}; |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
@ -158,9 +161,10 @@ const DashboardProject = (props) => {
|
|
|
|
|
getSCurve(); |
|
|
|
|
} |
|
|
|
|
window.addEventListener("message", handleIframeMessage); |
|
|
|
|
|
|
|
|
|
window.addEventListener('message', handleGetOverdue); |
|
|
|
|
return () => { |
|
|
|
|
window.removeEventListener("message", handleIframeMessage); |
|
|
|
|
window.removeEventListener("message", handleGetOverdue); |
|
|
|
|
}; |
|
|
|
|
}, [isHierarchy]); |
|
|
|
|
|
|
|
|
@ -183,8 +187,13 @@ const DashboardProject = (props) => {
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
let deviation = 0; |
|
|
|
|
if (plannedCost && totalCost) { |
|
|
|
|
deviation = plannedCost - totalCost; |
|
|
|
|
// 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; |
|
|
|
|
} |
|
|
|
|
setRemToComplete(deviation.toString()); |
|
|
|
|
}, [plannedCost, totalCost]); |
|
|
|
@ -199,6 +208,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
const response = await axios.get(url, HEADER); |
|
|
|
|
setManPower(response.data.totalRecord); |
|
|
|
|
} catch (error) { |
|
|
|
|
console.error("Failed to get manpower:", error); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -215,6 +225,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
} |
|
|
|
|
setIsReadyGantt(true); |
|
|
|
|
} catch (error) { |
|
|
|
|
console.error("Failed to get gantt data:", error); |
|
|
|
|
setIsReadyGantt(true); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
@ -227,6 +238,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
setDataGanttParents(response); |
|
|
|
|
setIsReadyGanttParents(true); |
|
|
|
|
} catch (error) { |
|
|
|
|
console.error("Failed to get gantt data:", error); |
|
|
|
|
setIsReadyGanttParents(true); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
@ -244,6 +256,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
setAssignedHrCount(assignedList.length); |
|
|
|
|
setAssignedHr(assignedList); |
|
|
|
|
} catch (error) { |
|
|
|
|
console.error("Failed to get assigned HR:", error); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -300,6 +313,7 @@ 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); |
|
|
|
@ -314,6 +328,8 @@ 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 : "-" |
|
|
|
@ -341,6 +357,7 @@ 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 |
|
|
|
@ -408,6 +425,7 @@ 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 && |
|
|
|
@ -431,21 +449,13 @@ 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[n]; |
|
|
|
|
setPlanningProgress(planningProgress); |
|
|
|
|
planningProgress = result.data.data[0].data?.percentagePlan[result.data.data[0].data?.percentagePlan.length - 1]; |
|
|
|
|
setPlanningProgress(Math.ceil(planningProgress)); |
|
|
|
|
} |
|
|
|
|
if ( |
|
|
|
|
result.data.data.length > 0 && |
|
|
|
@ -484,6 +494,7 @@ 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); |
|
|
|
@ -520,6 +531,7 @@ 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); |
|
|
|
@ -566,6 +578,7 @@ 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); |
|
|
|
@ -618,6 +631,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
.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); |
|
|
|
@ -638,6 +652,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const handleSendComment = async () => { |
|
|
|
|
// console.log('handleSendComment', comment);
|
|
|
|
|
setIsSendingComment(true); |
|
|
|
|
if (comment === "") { |
|
|
|
|
NotificationManager.error( |
|
|
|
@ -684,6 +699,10 @@ const DashboardProject = (props) => {
|
|
|
|
|
setIsSendingComment(false); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const handleBack = async () => { |
|
|
|
|
window.parent.location.reload(); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const initMap = () => { |
|
|
|
|
let mymap = L.map("map-area", { |
|
|
|
|
center: center, |
|
|
|
@ -731,6 +750,12 @@ const DashboardProject = (props) => {
|
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const handleGetOverdue = (event) => { |
|
|
|
|
if (event.data.type === 'dataOverdueUpdate') { |
|
|
|
|
setDataOverdue(event.data.dataOverdue); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const RenderGantt = useMemo( |
|
|
|
|
() => ( |
|
|
|
|
<iframe |
|
|
|
@ -804,53 +829,82 @@ const DashboardProject = (props) => {
|
|
|
|
|
}, [comment, comments, isSendingComment, isReadyComments]); |
|
|
|
|
|
|
|
|
|
const RenderBehindTasks = useMemo(() => { |
|
|
|
|
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 { |
|
|
|
|
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; |
|
|
|
|
} else { |
|
|
|
|
if (diffDays > 0) { |
|
|
|
|
message = `Overdue by ${diffDays + 1} days`; |
|
|
|
|
} else { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return ( |
|
|
|
|
<BehindTaskItem key={idx} name={item.name} message={message} /> |
|
|
|
|
); |
|
|
|
|
})} |
|
|
|
|
</div> |
|
|
|
|
); |
|
|
|
|
}, [overdueActivities, isReadyOverdueActivities]); |
|
|
|
|
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 ( |
|
|
|
|
<div style={{ marginLeft: -25, marginRight: -25 }}> |
|
|
|
@ -858,7 +912,23 @@ const DashboardProject = (props) => {
|
|
|
|
|
<Row> |
|
|
|
|
<Col span={18}> |
|
|
|
|
<Row> |
|
|
|
|
<Col span={8}> |
|
|
|
|
{ |
|
|
|
|
!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}> |
|
|
|
|
<div |
|
|
|
|
style={{ |
|
|
|
|
border: "solid", |
|
|
|
@ -1026,7 +1096,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
</Col> |
|
|
|
|
</Row> |
|
|
|
|
<Row> |
|
|
|
|
<Col span={SCURVE ? 8 : 10}> |
|
|
|
|
<Col span={SCURVE ? 8 : projectType != 9 ? 8 : 10}> |
|
|
|
|
<div |
|
|
|
|
style={{ |
|
|
|
|
border: "solid", |
|
|
|
@ -1151,7 +1221,7 @@ const DashboardProject = (props) => {
|
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</Col> |
|
|
|
|
{SCURVE ? ( |
|
|
|
|
{SCURVE || projectType != 9 ? ( |
|
|
|
|
<Col span={16}> |
|
|
|
|
<div |
|
|
|
|
style={{ |
|
|
|
@ -1290,15 +1360,17 @@ const DashboardProject = (props) => {
|
|
|
|
|
BCWP (cost vs perform) |
|
|
|
|
</Col> |
|
|
|
|
<Col span={5} style={{ fontSize: 11 }}> |
|
|
|
|
{isReadySCurve ? ( |
|
|
|
|
bcwp ? ( |
|
|
|
|
renderFormatRupiah(bcwp, "Rp.") |
|
|
|
|
) : ( |
|
|
|
|
"-" |
|
|
|
|
{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.") |
|
|
|
|
) |
|
|
|
|
) : ( |
|
|
|
|
<SingleTextLoader width={100} height={10} /> |
|
|
|
|
)} |
|
|
|
|
: ( |
|
|
|
|
"-" |
|
|
|
|
)} |
|
|
|
|
</Col> |
|
|
|
|
<Col |
|
|
|
|
span={7} |
|
|
|
|