|
|
@ -80,6 +80,10 @@ const DashboardProject = () => { |
|
|
|
const [healthBySchedule, setHealthBySchedule] = useState('-') |
|
|
|
const [healthBySchedule, setHealthBySchedule] = useState('-') |
|
|
|
const [healthByBudget, setHealthByBudget] = useState('-') |
|
|
|
const [healthByBudget, setHealthByBudget] = useState('-') |
|
|
|
const [reportDistribution, setReportDistribution] = useState([]); |
|
|
|
const [reportDistribution, setReportDistribution] = useState([]); |
|
|
|
|
|
|
|
const [manPower, setManPower] = useState(0); |
|
|
|
|
|
|
|
const [assignedHr, setAssignedHr] = useState([]); |
|
|
|
|
|
|
|
const [assignedHrCount, setAssignedHrCount] = useState(0); |
|
|
|
|
|
|
|
const [actualHrCount, setActualHrCount] = useState(0); |
|
|
|
let history = useHistory(); |
|
|
|
let history = useHistory(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -100,6 +104,17 @@ const DashboardProject = () => { |
|
|
|
} |
|
|
|
} |
|
|
|
}, [activeTabIdx]); |
|
|
|
}, [activeTabIdx]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
|
|
async function fetchData() { |
|
|
|
|
|
|
|
await Promise.all([ |
|
|
|
|
|
|
|
getManpower(), |
|
|
|
|
|
|
|
getAssignedHR(), |
|
|
|
|
|
|
|
...(assignedHr.length > 0 ? [getActualHR()] : []) |
|
|
|
|
|
|
|
]) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
fetchData() |
|
|
|
|
|
|
|
}, [manPower, assignedHr]) |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
useEffect(() => { |
|
|
|
let deviation = 0; |
|
|
|
let deviation = 0; |
|
|
|
if (plannedCost && totalCost) { |
|
|
|
if (plannedCost && totalCost) { |
|
|
@ -113,6 +128,57 @@ const DashboardProject = () => { |
|
|
|
history.push("/projects/" + GANTT_ID + "/" + PROJECT_ID + "/gantt"); |
|
|
|
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 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 () => { |
|
|
|
const getProjectDetail = async () => { |
|
|
|
setIsReadyProjectDetail(false); |
|
|
|
setIsReadyProjectDetail(false); |
|
|
|
const URL = `${BASE_OSPRO}/api/project/detail/${PROJECT_ID}`; |
|
|
|
const URL = `${BASE_OSPRO}/api/project/detail/${PROJECT_ID}`; |
|
|
@ -631,6 +697,7 @@ const DashboardProject = () => { |
|
|
|
: <SingleTextLoader width={100} height={10} /> |
|
|
|
: <SingleTextLoader width={100} height={10} /> |
|
|
|
} |
|
|
|
} |
|
|
|
</Col> |
|
|
|
</Col> |
|
|
|
|
|
|
|
<Col span={7} style={{ fontSize: 11, fontWeight: 'bold' }}><i className="fa fa-calendar" style={{ marginRight: 8 }}></i>Committed Cost</Col> |
|
|
|
</Row> |
|
|
|
</Row> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
@ -680,18 +747,68 @@ const DashboardProject = () => { |
|
|
|
</Row> |
|
|
|
</Row> |
|
|
|
<Row> |
|
|
|
<Row> |
|
|
|
<Col span={12}> |
|
|
|
<Col span={12}> |
|
|
|
<div style={{ backgroundColor: '#FFFFFF', padding: 10, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', marginBottom: 5, marginRight: 2 }}> |
|
|
|
<div style={{ |
|
|
|
<div style={{ color: '#000000', textAlign: 'center', marginBottom: 10, fontWeight: 'bold', fontSize: 12 }}>Health By Budget</div> |
|
|
|
background: '#FFF', |
|
|
|
{isReadyOverdueActivities ? <HealthByBudget status={healthByBudget} /> : <SingleTextLoader width={"100%"} height={30} />} |
|
|
|
padding: 10, |
|
|
|
|
|
|
|
border: '1px solid #DDD', |
|
|
|
|
|
|
|
marginRight: 2 |
|
|
|
|
|
|
|
}}> |
|
|
|
|
|
|
|
{isReadyOverdueActivities && <HealthByBudget status={healthByBudget} />} |
|
|
|
|
|
|
|
{isReadySCurve && <HealthBySchedule status={healthBySchedule} />} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</Col> |
|
|
|
</Col> |
|
|
|
<Col span={12}> |
|
|
|
<Col span={12}> |
|
|
|
<div style={{ backgroundColor: '#FFFFFF', padding: 10, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', marginBottom: 5, marginLeft: 2 }}> |
|
|
|
<Row style={{ height: '50%' }}> |
|
|
|
<div style={{ color: '#000000', textAlign: 'center', marginBottom: 10, fontWeight: 'bold', fontSize: 12 }}>Health By Schedule</div> |
|
|
|
<Col span={24}> |
|
|
|
{isReadySCurve ? <HealthBySchedule status={healthBySchedule} /> : <SingleTextLoader width={"100%"} height={30} />} |
|
|
|
<div style={{ |
|
|
|
|
|
|
|
display: 'flex', |
|
|
|
|
|
|
|
alignItems: 'center', |
|
|
|
|
|
|
|
justifyContent: 'center', |
|
|
|
|
|
|
|
height: '100%', |
|
|
|
|
|
|
|
background: '#FFF', |
|
|
|
|
|
|
|
padding: 10, |
|
|
|
|
|
|
|
border: '1px solid #DDD', |
|
|
|
|
|
|
|
marginBottom: 5, |
|
|
|
|
|
|
|
marginRight: 2 |
|
|
|
|
|
|
|
}}> |
|
|
|
|
|
|
|
Manpower : {manPower} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</Col> |
|
|
|
</Col> |
|
|
|
</Row> |
|
|
|
</Row> |
|
|
|
|
|
|
|
<Row style={{ height: '50%' }}> |
|
|
|
|
|
|
|
<Col span={12}> |
|
|
|
|
|
|
|
<div style={{ |
|
|
|
|
|
|
|
display: 'flex', |
|
|
|
|
|
|
|
alignItems: 'center', |
|
|
|
|
|
|
|
justifyContent: 'center', |
|
|
|
|
|
|
|
height: '100%', |
|
|
|
|
|
|
|
background: '#FFF', |
|
|
|
|
|
|
|
padding: 10, |
|
|
|
|
|
|
|
border: '1px solid #DDD', |
|
|
|
|
|
|
|
marginBottom: 5, |
|
|
|
|
|
|
|
marginRight: 2 |
|
|
|
|
|
|
|
}}> |
|
|
|
|
|
|
|
Assigned : {assignedHrCount} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</Col> |
|
|
|
|
|
|
|
<Col span={12}> |
|
|
|
|
|
|
|
<div style={{ |
|
|
|
|
|
|
|
display: 'flex', |
|
|
|
|
|
|
|
alignItems: 'center', |
|
|
|
|
|
|
|
justifyContent: 'center', |
|
|
|
|
|
|
|
height: '100%', |
|
|
|
|
|
|
|
background: '#FFF', |
|
|
|
|
|
|
|
padding: 10, |
|
|
|
|
|
|
|
border: '1px solid #DDD', |
|
|
|
|
|
|
|
marginBottom: 5, |
|
|
|
|
|
|
|
marginRight: 2 |
|
|
|
|
|
|
|
}}> |
|
|
|
|
|
|
|
Actual : {actualHrCount} |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</Col> |
|
|
|
|
|
|
|
</Row> |
|
|
|
|
|
|
|
</Col> |
|
|
|
|
|
|
|
</Row> |
|
|
|
<Row> |
|
|
|
<Row> |
|
|
|
<Col span={24}> |
|
|
|
<Col span={24}> |
|
|
|
<div style={{ backgroundColor: '#FFFFFF', padding: 10, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', marginBottom: 5, marginRight: 2 }}> |
|
|
|
<div style={{ backgroundColor: '#FFFFFF', padding: 10, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', marginBottom: 5, marginRight: 2 }}> |
|
|
|