|
|
@ -9,6 +9,7 @@ import moment from 'moment'; |
|
|
|
import { BASE_OSPRO } from '../../const/ApiConst'; |
|
|
|
import { BASE_OSPRO } from '../../const/ApiConst'; |
|
|
|
import { SendOutlined } from '@ant-design/icons'; |
|
|
|
import { SendOutlined } from '@ant-design/icons'; |
|
|
|
import { NotificationContainer, NotificationManager } from 'react-notifications'; |
|
|
|
import { NotificationContainer, NotificationManager } from 'react-notifications'; |
|
|
|
|
|
|
|
import { BehindTaskItem, Comment, HealthBySchedule, ListLoader, PopupContent, ProgressActualBar, ProgressPlanningBar, SingleTextLoader } from './Components'; |
|
|
|
const { TextArea } = Input; |
|
|
|
const { TextArea } = Input; |
|
|
|
|
|
|
|
|
|
|
|
const styles = { |
|
|
|
const styles = { |
|
|
@ -34,31 +35,42 @@ const DashboardCustomer = () => { |
|
|
|
} |
|
|
|
} |
|
|
|
const { PROJECT_ID, GANTT_ID } = useParams(); |
|
|
|
const { PROJECT_ID, GANTT_ID } = useParams(); |
|
|
|
// const URL_GANTT = `http://103.73.125.81:8446/index.html?base_url=http://103.73.125.81:8444/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`;
|
|
|
|
// const URL_GANTT = `http://103.73.125.81:8446/index.html?base_url=http://103.73.125.81:8444/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`;
|
|
|
|
const URL_GANTT = `https://adw-gantt.ospro.id/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`; |
|
|
|
// const URL_GANTT = `https://adw-gantt.ospro.id/index.html?base_url=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`;
|
|
|
|
// const URL_GANTT = '';
|
|
|
|
// const URL_GANTT = '';
|
|
|
|
|
|
|
|
const URL_GANTT = `http://192.168.1.104:8446/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 mapRef = useRef() |
|
|
|
const [projectName, setProjectName] = useState("Project Tower ABC"); |
|
|
|
const [projectName, setProjectName] = useState(""); |
|
|
|
const [customerName, setCustomerName] = useState("Jaya Gedung Group"); |
|
|
|
const [projectManagerName, setProjectManagerName] = useState(''); |
|
|
|
const [plannedStart, setPlannedStart] = useState("2019-03-04") |
|
|
|
const [customerName, setCustomerName] = useState(""); |
|
|
|
const [plannedFinish, setPlannedFinish] = useState("2020-05-28") |
|
|
|
const [plannedStart, setPlannedStart] = useState(null) |
|
|
|
const [actualStart, setActualStart] = useState("2019-03-04") |
|
|
|
const [plannedFinish, setPlannedFinish] = useState(null) |
|
|
|
const [actualFinish, setActualFinish] = useState("2020-05-28") |
|
|
|
const [actualStart, setActualStart] = useState(null) |
|
|
|
const [estimatedFinish, setEstimatedFinish] = useState("2020-05-28") |
|
|
|
const [actualFinish, setActualFinish] = useState(null) |
|
|
|
|
|
|
|
const [estimatedFinish, setEstimatedFinish] = useState(null) |
|
|
|
const [mymap, setMymap] = useState(null); |
|
|
|
const [mymap, setMymap] = useState(null); |
|
|
|
const [activeTabIdx, setActiveTabIdx] = useState(0); |
|
|
|
const [activeTabIdx, setActiveTabIdx] = useState(0); |
|
|
|
const [activeTabCommentIdx, setActiveTabCommentIdx] = useState(0); |
|
|
|
const [activeTabCommentIdx, setActiveTabCommentIdx] = useState(0); |
|
|
|
const [planningProgress, setPlanningProgress] = useState(79); |
|
|
|
const [planningProgress, setPlanningProgress] = useState(0); |
|
|
|
const [actualProgress, setActualProgress] = useState(50); |
|
|
|
const [actualProgress, setActualProgress] = useState(0); |
|
|
|
const [comment, setComment] = useState(''); |
|
|
|
const [comment, setComment] = useState(''); |
|
|
|
const [comments, setComments] = useState([]); |
|
|
|
const [comments, setComments] = useState([]); |
|
|
|
const [isReadyComments, setIsReadyComments] = useState(false); |
|
|
|
const [isReadyComments, setIsReadyComments] = useState(false); |
|
|
|
const [isSendingComment, setIsSendingComment] = useState(false); |
|
|
|
const [isSendingComment, setIsSendingComment] = useState(false); |
|
|
|
const [behindTasks, setBehindTasks] = useState([]); |
|
|
|
const [isReadyProjectDetail, setIsReadyProjectDetail] = useState(false); |
|
|
|
|
|
|
|
const [isReadySCurve, setIsReadySCurve] = useState(false); |
|
|
|
|
|
|
|
const [isReadyOverdueActivities, setIsReadyOverdueActivities] = useState(false); |
|
|
|
|
|
|
|
const [isReadyReportDistribution, setIsReadyReportDistribution] = useState(false); |
|
|
|
|
|
|
|
const [overdueActivities, setOverdueActivities] = useState([]); |
|
|
|
|
|
|
|
const [healthBySchedule, setHealthBySchedule] = useState('-'); |
|
|
|
|
|
|
|
const [healthByBudget, setHealthByBudget] = useState('-'); |
|
|
|
|
|
|
|
const [reportDistribution, setReportDistribution] = useState([]); |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
useEffect(() => { |
|
|
|
// console.log('URL_GANTT', URL_GANTT);
|
|
|
|
getProjectDetail(); |
|
|
|
|
|
|
|
getSCurve() |
|
|
|
|
|
|
|
getOverdueActivities(); |
|
|
|
|
|
|
|
getReportDistribution() |
|
|
|
getComments(); |
|
|
|
getComments(); |
|
|
|
getBehindTasks(); |
|
|
|
|
|
|
|
return () => { |
|
|
|
return () => { |
|
|
|
console.log('unmount RenderMap'); |
|
|
|
console.log('unmount RenderMap'); |
|
|
|
} |
|
|
|
} |
|
|
@ -70,6 +82,138 @@ const DashboardCustomer = () => { |
|
|
|
} |
|
|
|
} |
|
|
|
}, [activeTabIdx]); |
|
|
|
}, [activeTabIdx]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const getProjectDetail = async () => { |
|
|
|
|
|
|
|
setIsReadyProjectDetail(false); |
|
|
|
|
|
|
|
const URL = `${BASE_OSPRO}/api/project/detail/${PROJECT_ID}`; |
|
|
|
|
|
|
|
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(result.data.data?.mulai_proyek ? result.data.data.mulai_proyek : null) |
|
|
|
|
|
|
|
setPlannedFinish(result.data.data?.akhir_proyek ? result.data.data.akhir_proyek : 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) |
|
|
|
|
|
|
|
setIsReadyProjectDetail(true); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const getSCurve = async () => { |
|
|
|
|
|
|
|
setIsReadySCurve(false); |
|
|
|
|
|
|
|
const URL = `${BASE_OSPRO}/api/project/get-s-curve`; |
|
|
|
|
|
|
|
const payload = { |
|
|
|
|
|
|
|
"project_id": PROJECT_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) |
|
|
|
|
|
|
|
console.log('getSCurve', result); |
|
|
|
|
|
|
|
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?.percentagePlan && result.data.data[0].data?.percentagePlan.length > 0) { |
|
|
|
|
|
|
|
let planningProgress = result.data.data[0].data?.percentagePlan[result.data.data[0].data?.percentagePlan.length-1]; |
|
|
|
|
|
|
|
setPlanningProgress(planningProgress); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (result.data.data.length > 0 && result.data.data[0].data?.percentageReal && result.data.data[0].data?.percentageReal.length > 0) { |
|
|
|
|
|
|
|
let 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(), |
|
|
|
|
|
|
|
"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 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 () => { |
|
|
|
const getComments = async () => { |
|
|
|
setIsReadyComments(false); |
|
|
|
setIsReadyComments(false); |
|
|
|
const URL = `${BASE_OSPRO}/api/project-comment/search`; |
|
|
|
const URL = `${BASE_OSPRO}/api/project-comment/search`; |
|
|
@ -108,19 +252,18 @@ const DashboardCustomer = () => { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (result.status !== 200) { |
|
|
|
if (result.status !== 200) { |
|
|
|
NotificationManager.error(`Post comment failed, ${result.data.message}`, "Failed"); |
|
|
|
NotificationManager.error(`Get comments failed, ${result.data.message}`, "Failed"); |
|
|
|
setIsReadyComments(true); |
|
|
|
setIsReadyComments(true); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (result.status == 200 && result.data.data) { |
|
|
|
else if (result.status == 200 && result.data.data) { |
|
|
|
console.log(result.data.data); |
|
|
|
|
|
|
|
setComments(result.data.data); |
|
|
|
setComments(result.data.data); |
|
|
|
setIsReadyComments(true); |
|
|
|
setIsReadyComments(true); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const handleSendComment = async () => { |
|
|
|
const handleSendComment = async () => { |
|
|
|
console.log('handleSendComment', comment); |
|
|
|
// console.log('handleSendComment', comment);
|
|
|
|
setIsSendingComment(true); |
|
|
|
setIsSendingComment(true); |
|
|
|
if (comment === '') { |
|
|
|
if (comment === '') { |
|
|
|
NotificationManager.error("Please leave a comment before you send it.", "Failed"); |
|
|
|
NotificationManager.error("Please leave a comment before you send it.", "Failed"); |
|
|
@ -159,34 +302,25 @@ const DashboardCustomer = () => { |
|
|
|
setIsSendingComment(false); |
|
|
|
setIsSendingComment(false); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const getBehindTasks = () => { |
|
|
|
|
|
|
|
setBehindTasks([ |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
id: 1, |
|
|
|
|
|
|
|
name: "Tom", |
|
|
|
|
|
|
|
message: "Overdue by 5 days", |
|
|
|
|
|
|
|
division: "IT" |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
id: 1, |
|
|
|
|
|
|
|
name: "Jerry", |
|
|
|
|
|
|
|
message: "Overdue by 3 days", |
|
|
|
|
|
|
|
division: "Finance" |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
]) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const initMap = () => { |
|
|
|
const initMap = () => { |
|
|
|
let mymap = L.map('map-area', { |
|
|
|
let mymap = L.map('map-area', { |
|
|
|
center: center, |
|
|
|
center: center, |
|
|
|
zoom: 13 |
|
|
|
zoom: 13 |
|
|
|
}) |
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
setMymap(mymap); |
|
|
|
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); |
|
|
|
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) { |
|
|
|
|
|
|
|
reportDistribution.map((item, idx) => { |
|
|
|
|
|
|
|
L.marker([item.lat, item.lon]).addTo(mymap).bindPopup(PopupContent(item)) |
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}, [mymap, reportDistribution]) |
|
|
|
|
|
|
|
|
|
|
|
const RenderGantt = useMemo(() => ( |
|
|
|
const RenderGantt = useMemo(() => ( |
|
|
|
<iframe |
|
|
|
<iframe |
|
|
|
id="frame-gantt" |
|
|
|
id="frame-gantt" |
|
|
@ -201,22 +335,6 @@ const DashboardCustomer = () => { |
|
|
|
></iframe> |
|
|
|
></iframe> |
|
|
|
), [activeTabIdx]) |
|
|
|
), [activeTabIdx]) |
|
|
|
|
|
|
|
|
|
|
|
const Comment = ({name, comment, created_at}) => ( |
|
|
|
|
|
|
|
<div style={{backgroundColor: '#EEEEEE', border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 5, marginBottom: 5, marginRight: 2, marginLeft: 2}}> |
|
|
|
|
|
|
|
<div style={{fontWeight: 'bold'}}>{name}</div> |
|
|
|
|
|
|
|
<div style={{color: '#E80053', fontWeight: 100, fontSize: 10}}>{created_at ? moment(created_at).format('D MMMM YYYY HH:mm:ss') : '-'}</div> |
|
|
|
|
|
|
|
<div>{comment}</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const BehindTaskItem = ({name, message, division}) => ( |
|
|
|
|
|
|
|
<div style={{backgroundColor: '#EEEEEE', border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', padding: 5, marginBottom: 5, marginRight: 2, marginLeft: 2}}> |
|
|
|
|
|
|
|
<div style={{fontWeight: 'bold'}}>{name}</div> |
|
|
|
|
|
|
|
<div>{message}</div> |
|
|
|
|
|
|
|
<div style={{color: '#E80053', textAlign: 'right', fontWeight: 500, fontSize: 12}}>{division}</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const RenderComments = useMemo(() => { |
|
|
|
const RenderComments = useMemo(() => { |
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<> |
|
|
|
<> |
|
|
@ -227,7 +345,7 @@ const DashboardCustomer = () => { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div> |
|
|
|
<div> |
|
|
|
{!isReadyComments && <div style={{color: 'grey', flex: 1, textAlign: 'center', marginTop: 50, marginBottom: 50}}>Loading comments...</div>} |
|
|
|
{!isReadyComments && <ListLoader />} |
|
|
|
{isReadyComments && comments && comments.length < 1 && <div style={{flex: 1, textAlign: 'center', color: '#E80053', marginTop: 50, marginBottom: 50}}>No comments found.</div>} |
|
|
|
{isReadyComments && comments && comments.length < 1 && <div style={{flex: 1, textAlign: 'center', color: '#E80053', marginTop: 50, marginBottom: 50}}>No comments found.</div>} |
|
|
|
{comments && comments.length > 0 && comments.map((item, idx) =>
|
|
|
|
{comments && comments.length > 0 && comments.map((item, idx) =>
|
|
|
|
<Comment key={idx} name={item.join_first_name} comment={item.comment} created_at={item.created_at} /> |
|
|
|
<Comment key={idx} name={item.join_first_name} comment={item.comment} created_at={item.created_at} /> |
|
|
@ -240,13 +358,24 @@ const DashboardCustomer = () => { |
|
|
|
const RenderBehindTasks = useMemo(() => { |
|
|
|
const RenderBehindTasks = useMemo(() => { |
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<div> |
|
|
|
<div> |
|
|
|
{behindTasks && behindTasks.length < 1 && <div style={{flex: 1, textAlign: 'center', color: '#E80053', marginTop: 50, marginBottom: 50}}>No behind task found.</div>} |
|
|
|
{!isReadyOverdueActivities && <ListLoader />} |
|
|
|
{behindTasks && behindTasks.length > 0 && behindTasks.map((item, idx) =>
|
|
|
|
{isReadyOverdueActivities && overdueActivities && overdueActivities.length < 1 && <div style={{flex: 1, textAlign: 'center', color: '#E80053', marginTop: 50, marginBottom: 50}}>No behind task found.</div>} |
|
|
|
<BehindTaskItem key={idx} name={item.name} message={item.message} division={item.division} /> |
|
|
|
{isReadyOverdueActivities && overdueActivities && overdueActivities.length > 0 && overdueActivities.map((item, idx) => { |
|
|
|
)} |
|
|
|
let end_date = null; |
|
|
|
|
|
|
|
let today = null; |
|
|
|
|
|
|
|
let diffDays = 0; |
|
|
|
|
|
|
|
let message = ''; |
|
|
|
|
|
|
|
if (item.end_date && item.end_date !== null) { |
|
|
|
|
|
|
|
end_date = moment(item.end_date); |
|
|
|
|
|
|
|
today = moment(new Date()); |
|
|
|
|
|
|
|
diffDays = today.diff(end_date, 'days'); |
|
|
|
|
|
|
|
message = `Overdue by ${diffDays} days`; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return <BehindTaskItem key={idx} name={item.name} message={message} /> |
|
|
|
|
|
|
|
})} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
) |
|
|
|
) |
|
|
|
}, [behindTasks]) |
|
|
|
}, [overdueActivities, isReadyOverdueActivities]) |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
return ( |
|
|
|
<div style={{ marginLeft: -25, marginRight: -25 }}> |
|
|
|
<div style={{ marginLeft: -25, marginRight: -25 }}> |
|
|
@ -270,7 +399,7 @@ const DashboardCustomer = () => { |
|
|
|
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: 10 }}> |
|
|
|
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: 10 }}> |
|
|
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
|
|
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
|
|
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Project</div> |
|
|
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Project</div> |
|
|
|
<div style={{fontSize: 14}}>{projectName}</div> |
|
|
|
<div style={{fontSize: 14}}>{isReadyProjectDetail ? projectName : <SingleTextLoader />}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div> |
|
|
|
<div> |
|
|
|
<i className="fa fa-check-square" style={{fontSize: 28}}></i> |
|
|
|
<i className="fa fa-check-square" style={{fontSize: 28}}></i> |
|
|
@ -284,7 +413,7 @@ const DashboardCustomer = () => { |
|
|
|
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: 10 }}> |
|
|
|
<div style={{ display: 'flex', flexDirection: 'row', marginBottom: 10 }}> |
|
|
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
|
|
|
<div style={{ flex: 20, display: 'flex', flexDirection: 'column' }}> |
|
|
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Customer</div> |
|
|
|
<div style={{fontSize: 16, fontWeight: 'bold', marginBottom: 10}}>Customer</div> |
|
|
|
<div style={{fontSize: 14}}>{customerName}</div> |
|
|
|
<div style={{fontSize: 14}}>{isReadyProjectDetail ? customerName : <SingleTextLoader />}</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div> |
|
|
|
<div> |
|
|
|
<i className="fa fa-home" style={{fontSize: 28}}></i> |
|
|
|
<i className="fa fa-home" style={{fontSize: 28}}></i> |
|
|
@ -302,18 +431,36 @@ const DashboardCustomer = () => { |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div> |
|
|
|
<div> |
|
|
|
<Row> |
|
|
|
<Row> |
|
|
|
<Col span={4} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Planned Start</Col> |
|
|
|
<Col span={6} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Planned Start</Col> |
|
|
|
<Col span={4}>{plannedStart ? moment(plannedStart).format('D MMMM YYYY') : '-'}</Col> |
|
|
|
<Col span={6}> |
|
|
|
<Col span={4} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Planned Finish</Col> |
|
|
|
{isReadyProjectDetail ? |
|
|
|
<Col span={4}>{plannedFinish ? moment(plannedFinish).format('D MMMM YYYY') : '-'}</Col> |
|
|
|
plannedStart ? moment(plannedStart).format('D MMMM YYYY') : '-' |
|
|
|
<Col span={4} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Actual Finish</Col> |
|
|
|
: <SingleTextLoader width={100} height={10} /> |
|
|
|
<Col span={4}>{actualFinish ? moment(actualFinish).format('D MMMM YYYY') : '-'}</Col> |
|
|
|
} |
|
|
|
|
|
|
|
</Col> |
|
|
|
|
|
|
|
<Col span={6} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Planned Finish</Col> |
|
|
|
|
|
|
|
<Col span={6}> |
|
|
|
|
|
|
|
{isReadyProjectDetail ? |
|
|
|
|
|
|
|
plannedFinish ? moment(plannedFinish).format('D MMMM YYYY') : '-' |
|
|
|
|
|
|
|
: <SingleTextLoader width={100} height={10} /> |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
</Col> |
|
|
|
</Row> |
|
|
|
</Row> |
|
|
|
<Row> |
|
|
|
<Row> |
|
|
|
<Col span={4} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Actual Start</Col> |
|
|
|
<Col span={6} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Actual Start</Col> |
|
|
|
<Col span={4}>{actualStart ? moment(actualStart).format('D MMMM YYYY') : '-'}</Col> |
|
|
|
<Col span={6}> |
|
|
|
<Col span={4} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Estimated Finish</Col> |
|
|
|
{isReadyProjectDetail ? |
|
|
|
<Col span={4}>{estimatedFinish ? moment(estimatedFinish).format('D MMMM YYYY') : '-'}</Col> |
|
|
|
actualStart ? moment(actualStart).format('D MMMM YYYY') : '-' |
|
|
|
|
|
|
|
: <SingleTextLoader width={100} height={10} /> |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
</Col> |
|
|
|
|
|
|
|
<Col span={6} style={{fontWeight: 'bold'}}><i className="fa fa-calendar" style={{marginRight:8}}></i>Estimated Finish</Col> |
|
|
|
|
|
|
|
<Col span={6}> |
|
|
|
|
|
|
|
{isReadyProjectDetail ? |
|
|
|
|
|
|
|
estimatedFinish ? moment(estimatedFinish).format('D MMMM YYYY') : '-' |
|
|
|
|
|
|
|
: <SingleTextLoader width={100} height={10} /> |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
</Col> |
|
|
|
</Row> |
|
|
|
</Row> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
@ -347,12 +494,17 @@ const DashboardCustomer = () => { |
|
|
|
<Col span={24}> |
|
|
|
<Col span={24}> |
|
|
|
<div style={{backgroundColor: '#222222', padding: 10, marginBottom: 5}}> |
|
|
|
<div style={{backgroundColor: '#222222', padding: 10, marginBottom: 5}}> |
|
|
|
<div style={{color: '#FFFFFF', textAlign: 'center', marginBottom: 10, fontWeight: 'bold'}}>Progress</div> |
|
|
|
<div style={{color: '#FFFFFF', textAlign: 'center', marginBottom: 10, fontWeight: 'bold'}}>Progress</div> |
|
|
|
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> |
|
|
|
{isReadySCurve ? |
|
|
|
<div style={{backgroundColor: '#7209B7', width: `${planningProgress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: planningProgress < 100 ? 0 : 10, borderBottomRightRadius: planningProgress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500}}>Planning : {planningProgress}%</div> |
|
|
|
<ProgressPlanningBar progress={planningProgress} /> |
|
|
|
</div> |
|
|
|
: |
|
|
|
<div style={{backgroundColor: '#DDDDDD', color: '#FFFFFF', borderRadius: 10, marginBottom: 10}}> |
|
|
|
<SingleTextLoader width={"100%"} height={30} />
|
|
|
|
<div style={{backgroundColor: '#0059C9', width: `${actualProgress}%`, padding: 5, borderTopLeftRadius: 10, borderBottomLeftRadius: 10, borderTopRightRadius: actualProgress < 100 ? 0 : 10, borderBottomRightRadius: actualProgress < 100 ? 0 : 10, textAlign: 'center', fontWeight: 500}}>{actualProgress && actualProgress < 50 ? `${actualProgress}%` : `Actual : ${actualProgress}%` }</div> |
|
|
|
} |
|
|
|
</div> |
|
|
|
<div style={{marginTop: 10, marginBottom: 10}}></div> |
|
|
|
|
|
|
|
{isReadySCurve ? |
|
|
|
|
|
|
|
<ProgressActualBar progress={actualProgress} /> |
|
|
|
|
|
|
|
: |
|
|
|
|
|
|
|
<SingleTextLoader width={"100%"} height={30} />
|
|
|
|
|
|
|
|
} |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</Col> |
|
|
|
</Col> |
|
|
|
</Row> |
|
|
|
</Row> |
|
|
@ -360,9 +512,7 @@ const DashboardCustomer = () => { |
|
|
|
<Col span={24}> |
|
|
|
<Col span={24}> |
|
|
|
<div style={{backgroundColor: '#FFFFFF', padding: 10, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', marginBottom: 5}}> |
|
|
|
<div style={{backgroundColor: '#FFFFFF', padding: 10, border: 'solid', borderWidth: 1, borderColor: '#DDDDDD', marginBottom: 5}}> |
|
|
|
<div style={{color: '#000000', textAlign: 'center', marginBottom: 10, fontWeight: 'bold'}}>Health By Schedule</div> |
|
|
|
<div style={{color: '#000000', textAlign: 'center', marginBottom: 10, fontWeight: 'bold'}}>Health By Schedule</div> |
|
|
|
<div style={{backgroundColor: '#E80053', color: '#FFFFFF', padding: 5, borderRadius: 10, marginBottom: 10}}> |
|
|
|
{isReadySCurve ? <HealthBySchedule status={healthBySchedule} /> : <SingleTextLoader width={"100%"} height={30} /> } |
|
|
|
<div style={{textAlign: 'center', fontWeight: 500}}>Behind Schedule</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</Col> |
|
|
|
</Col> |
|
|
|
</Row> |
|
|
|
</Row> |
|
|
|