From c7cab677d30ae88cd136cbe8d829a27199702158 Mon Sep 17 00:00:00 2001 From: ardhi Date: Wed, 5 Oct 2022 12:31:04 +0700 Subject: [PATCH] finish dashboard BOD using API, add DashboardCustomer (dummy), add DashboardProject (dummy) --- src/components/BottomModal/BottomModal.css | 30 + src/components/BottomModal/BottomModal.js | 173 +++ src/components/BottomModal/package.json | 7 + src/components/CardDashboard/CardDashboard.js | 361 +++++ src/components/CardDashboard/package.json | 7 + src/const/ApiConst.js | 53 +- src/const/CustomFunc.js | 4 + src/routes.js | 13 +- src/views/Dashboard/DashboardBOD.js | 1291 +++++++++++------ src/views/Dashboard/DashboardCustomer.js | 244 ++++ src/views/Dashboard/DashboardProject.js | 323 +++++ 11 files changed, 2006 insertions(+), 500 deletions(-) create mode 100644 src/components/BottomModal/BottomModal.css create mode 100644 src/components/BottomModal/BottomModal.js create mode 100644 src/components/BottomModal/package.json create mode 100644 src/components/CardDashboard/CardDashboard.js create mode 100644 src/components/CardDashboard/package.json create mode 100644 src/views/Dashboard/DashboardCustomer.js create mode 100644 src/views/Dashboard/DashboardProject.js diff --git a/src/components/BottomModal/BottomModal.css b/src/components/BottomModal/BottomModal.css new file mode 100644 index 0000000..821a42f --- /dev/null +++ b/src/components/BottomModal/BottomModal.css @@ -0,0 +1,30 @@ +.bottommodal-window-button-container { + float: right; + right: 10px; + text-align: right; +} + +.bottommodal-close, +.bottommodal-maximize, +.bottommodal-minimize { + cursor: pointer; + padding: 4px; +} + +.bottommodal-header { + margin-bottom: -10px; +} + +.bottommodal-title { + font-size: 16px; + font-weight: 750; + text-align: left; + margin-left: 20px; + color: #FFFFFF; +} + +.bottommodal-close:hover, +.bottommodal-maximize:hover, +.bottommodal-minimize:hover { + color: #20a8d8; +} \ No newline at end of file diff --git a/src/components/BottomModal/BottomModal.js b/src/components/BottomModal/BottomModal.js new file mode 100644 index 0000000..96dd39c --- /dev/null +++ b/src/components/BottomModal/BottomModal.js @@ -0,0 +1,173 @@ +import React, { useMemo, useState } from 'react' +import { Resizable } from 're-resizable' +import './BottomModal.css' +import { Card, CardBody, CardHeader, Col, Row, Table } from 'reactstrap' +import Icon from '@iconify/react' +import closeCircleOutline from '@iconify/icons-ion/close-circle-outline'; +import removeCircleOutline from '@iconify/icons-ion/remove-circle-outline'; +import windowMaximaze from '@iconify/icons-mdi/window-maximize'; + +const BottomModal = ({title, tableHeader, tableData, closeModal}) => { + const [tableHeight, setTableHeight] = useState(150) + const [resizableWidth, setResizableWidth] = useState("100%") + const [resizableHeight, setResizableHeight] = useState(300) + const [maximizeTitle, setMaximizeTitle] = useState("maximize") + + const setBottomTableWindow = (type) => { + if (type === 'min') { + // minimize + setResizableHeight(55); + setTableHeight(150) + } + else if (type === 'max') { + if (resizableHeight === '100vh') { + // restore + setResizableHeight(300) + setTableHeight(150) + setMaximizeTitle("maximize"); + } + else { + // maximize + // setResizableHeight(600) + // setTableHeight(950) + setResizableHeight('100vh'); + setTableHeight('90vh'); + setMaximizeTitle("restore"); + } + } + } + + const renderTableRow = (item) => { + let row = []; + // looping through its object keys (item is an object) + Object.keys(item).map(key => { + // if (key !== 'id') { + row.push({item[key]}) + // } + }) + return row; + } + + const RenderTable = useMemo(() => { + if (tableData && tableData.length > 0) { + return ( + + + + + {tableHeader && tableHeader.length > 0 && + tableHeader.map((item, idx) => ( + + )) + } + + + + {tableData && tableData.length > 0 && + tableData.map((item, index) => { + return ( + {renderTableRow(item, tableHeader)} + ) + }) + } + +
#{item.title}
+ ) + } + + return ( + // + // + // + // + //
No Data Available
+
+ No Data Available +
+ ) + }, [tableData]) + + return ( + { + setResizableWidth("100%"); + setResizableHeight(resizableHeight + d.height); + if (e.screenY < 100) { // max + setResizableHeight('100vh'); + setTableHeight(450) + } + else if (e.screenY < 150) { + setTableHeight(400) + } + else if (e.screenY < 200) { + setTableHeight(350) + } + else if (e.screenY < 250) { + setTableHeight(300) + } + else if (e.screenY < 300) { + setTableHeight(250) + } + else if (e.screenY < 400) { + setTableHeight(200) + } + else if (e.screenY < 600) { + setTableHeight(150) // default + } + else if (e.screenY >= 600) { + setBottomTableWindow('min'); + } + }} + > +
+ + +

{title}

+ + + setBottomTableWindow("min")} + > + + + setBottomTableWindow("max")} + > + + + + + + +
+ + + {RenderTable} + + +
+
+ ) +} + +export default BottomModal; \ No newline at end of file diff --git a/src/components/BottomModal/package.json b/src/components/BottomModal/package.json new file mode 100644 index 0000000..7faf5b7 --- /dev/null +++ b/src/components/BottomModal/package.json @@ -0,0 +1,7 @@ +{ + "name": "BottomModal", + "version": "0.0.0", + "private": true, + "main": "./BottomModal.js" + } + \ No newline at end of file diff --git a/src/components/CardDashboard/CardDashboard.js b/src/components/CardDashboard/CardDashboard.js new file mode 100644 index 0000000..c2c9c14 --- /dev/null +++ b/src/components/CardDashboard/CardDashboard.js @@ -0,0 +1,361 @@ +import React, { useEffect, useState } from 'react'; +import { + Chart as ChartJS, + CategoryScale, + LineController, + LineElement, + LinearScale, + BarController, + BarElement, + PieController, + Title, + Tooltip, + Legend, + ArcElement +} from 'chart.js'; +// import Chart from 'chart.js/auto'; +import { Chart, Bar, Line, Pie } from 'react-chartjs-2'; +import ChartDataLabels from 'chartjs-plugin-datalabels'; +import ContentLoader from 'react-content-loader'; + +ChartJS.register( + CategoryScale, + LineController, + LineElement, + LinearScale, + BarController, + BarElement, + PieController, + Title, + Tooltip, + Legend, + ArcElement, + ChartDataLabels +); + +const styles = { + cardContainer: { backgroundColor: '#F8F8F8', margin: 2, paddingLeft: 20, paddingRight: 20, paddingTop: 10, height: '30vh' }, + cardHeaderContainer: { display: 'flex', flexDirection: 'row', marginBottom: 10 }, + cardChartContainer: { position: 'relative', height: '20vh', margin: 'auto', paddingBottom: 10, justifyContent: 'center' }, + cardTitle: { color: '#444444', fontSize: 14, fontWeight: 'bold' }, + cardSubtitle: { color: '#888888', fontSize: 12 } +} + +const verticalBarChartOption = { + elements: { + bar: { + borderWidth: 2, + }, + }, + indexAxis: 'x', + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: true, + position: 'right', + labels: { + boxWidth: 10 + } + }, + datalabels: { + color: '#FFFFFF' + } + }, + // scales: { + // yAxes: [{ + // afterFit: function(scaleInstance) { + // scaleInstance.width = 100; // sets the width to 100px + // } + // }] + // } +} + +const horizontalBarChartOption = { + indexAxis: 'y', + elements: { + bar: { + borderWidth: 2, + }, + }, + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false, + }, + datalabels: { + color: '#FFFFFF' + } + } +} + +const pieChartOption = { + elements: { + pie: { + borderWidth: 2, + }, + }, + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: true, + position: 'right', + labels: { + boxWidth: 10 + } + }, + datalabels: { + color: '#FFFFFF' + } + } +} + +const lineChartOption = { + elements: { + line: { + borderWidth: 2, + }, + }, + responsive: false, + plugins: { + datalabels: { + color: '#FFFFFF' + } + } +} + +const optionsExpenditure = { + indexAxis: 'y', + elements: { + bar: { + borderWidth: 2, + }, + }, + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false, + position: 'right', + labels: { + boxWidth: 10 + } + }, + datalabels: { + color: '#FFFFFF' + } + }, +}; + +const optionsScheduleHealthPerDivision = { + elements: { + bar: { + borderWidth: 2, + }, + }, + indexAxis: 'x', + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: true, + position: 'top', + labels: { + boxWidth: 10, + font: { + size: 10 + } + } + }, + datalabels: { + color: '#FFFFFF' + } + } +} + +export const ContentLoaderChart = ({type}) => { + if (type === 'vertical-bar') { + return ( + + {/* + + + + + + + + + + + + + + + */} + {/* + + + + + + + + */} + + + + + + + + + + + ) + } + else if (type === 'pie') { + return ( + + + + + + + ) + } + else { + return ( + + + + + + + ) + } +} + +// export const FullCard = + +export const CardDashboard = ({ title, subtitle, chartType, chartData, chartOption, isReady }) => { + let chart = null; + if (chartType === 'vertical-bar') { + chart = + } + else if (chartType === 'horizontal-bar') { + chart = + } + else if (chartType === 'pie') { + chart = + } + else if (chartType === 'line') { + chart = + } + + return ( +
+
+
+
{title}
+
{subtitle}
+
+
+ +
+ { isReady ? + chartData ? chart : + : + + } +
+ +
+ ) +} + +// export const CardExpenditure = ({ title, subtitle, chartData, chartOption }) => { +// return ( +//
+//
+//
+//
{title}
+//
{subtitle}
+//
+//
+//
Detailed View
+//
+//
+ +//
+// +//
+ +//
+// ) +// } + +export const CardScheduleHealthPerDivision = ({ isReady, title, subtitle, mode, changeMode, chartData, chartOption, chartType }) => { + return ( +
+
+
+
{title}
+
{subtitle}
+
+
+
changeMode('budget')}>Budget
+
changeMode('schedule')}>Schedule
+
+
+ +
+ {isReady ? chartData ? : : } +
+ +
+ ) +} + +export const NoDataChart = ({message}) => { + return ( +
+ {message ? message : "No Data Available"} +
+ ) +} \ No newline at end of file diff --git a/src/components/CardDashboard/package.json b/src/components/CardDashboard/package.json new file mode 100644 index 0000000..ce79c21 --- /dev/null +++ b/src/components/CardDashboard/package.json @@ -0,0 +1,7 @@ +{ + "name": "CardDashboard", + "version": "0.0.0", + "private": true, + "main": "./CardDashboard.js" + } + \ No newline at end of file diff --git a/src/const/ApiConst.js b/src/const/ApiConst.js index 7d348d3..569f5e3 100644 --- a/src/const/ApiConst.js +++ b/src/const/ApiConst.js @@ -111,41 +111,44 @@ export const BASE_SIMPRO_LUMENNIAGA = "https://ospro-api.odm-iu.com/api"; export const BASE_INTEGRATION_V1 = "http://ospro-api.adyawinsa.com:9083"; export const TOKEN_ADW = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIxMjAyIiwiZXhwIjoxNjkxODMwNDkzfQ.DvBQIOZsdFndWsliPCZT65Y6G5Xx4vWBKz8Rhe7rvRA"; -export let BASE_OSPRO = "https://ospro-api.ospro.id"; +// export let BASE_OSPRO = "https://ospro-api.ospro.id"; +export let BASE_OSPRO = "https://adw-api.ospro.id"; +// export let BASE_OSPRO = "http://192.168.1.123:8444"; // local +// export let BASE_OSPRO = "http://103.73.125.81:8444"; // ip public adw export let BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; export let BASE_SIMPRO_LUMEN_IMAGE = `${BASE_OSPRO}/assets/image`; export let BASE_SIMPRO_LUMEN_FILE = `${BASE_OSPRO}/assets/file/project`; -switch (APP_MODE) { - case 'KIT': - BASE_OSPRO = "https://kit-api.oslogdev.com" +// switch (APP_MODE) { +// case 'KIT': +// BASE_OSPRO = "https://kit-api.oslogdev.com" - //for KIT server - // BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; - // BASE_SIMPRO_LUMEN_IMAGE = `${BASE_SIMPRO_LUMEN}/assets/image`; - // BASE_SIMPRO_LUMEN_FILE = `${BASE_SIMPRO_LUMEN}/assets/file/project`; +// //for KIT server +// // BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; +// // BASE_SIMPRO_LUMEN_IMAGE = `${BASE_SIMPRO_LUMEN}/assets/image`; +// // BASE_SIMPRO_LUMEN_FILE = `${BASE_SIMPRO_LUMEN}/assets/file/project`; - break; - case 'ADW': - BASE_OSPRO = "https://adw-api.ospro.id" +// break; +// case 'ADW': +// // BASE_OSPRO = "https://adw-api.ospro.id" +// BASE_OSPRO = "http://103.73.125.81:8444"; - BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; - BASE_SIMPRO_LUMEN_IMAGE = `${BASE_OSPRO}/assets/image`; - BASE_SIMPRO_LUMEN_FILE = `${BASE_OSPRO}/assets/file/project`; +// BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; +// BASE_SIMPRO_LUMEN_IMAGE = `${BASE_OSPRO}/assets/image`; +// BASE_SIMPRO_LUMEN_FILE = `${BASE_OSPRO}/assets/file/project`; - break; - case 'IU': - BASE_OSPRO = "https://api-iu.ospro.id" +// break; +// case 'IU': +// BASE_OSPRO = "https://api-iu.ospro.id" - BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; - BASE_SIMPRO_LUMEN_IMAGE = `${BASE_OSPRO}/assets/image`; - BASE_SIMPRO_LUMEN_FILE = `${BASE_OSPRO}/assets/file/project`; +// BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; +// BASE_SIMPRO_LUMEN_IMAGE = `${BASE_OSPRO}/assets/image`; +// BASE_SIMPRO_LUMEN_FILE = `${BASE_OSPRO}/assets/file/project`; - break; - default: - BASE_OSPRO = "https://ospro-api.ospro.id" - -} +// break; +// default: +// BASE_OSPRO = "https://ospro-api.ospro.id" +// } export const USERROLE_ADD = `${BASE_SIMPRO}/user-role/add` export const USERROLE_SEARCH = `${BASE_SIMPRO}/user-role/search` diff --git a/src/const/CustomFunc.js b/src/const/CustomFunc.js index ad8a75a..6ce81f4 100644 --- a/src/const/CustomFunc.js +++ b/src/const/CustomFunc.js @@ -345,3 +345,7 @@ export const formatRibuanDecimal = (n) => { let parts=n.toString().split("."); return "Rp. " + parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ".") + (parts[1] ? "," + parts[1] : ""); } + +export const capitalizeFirstLetter = (string) => { + return string.charAt(0).toUpperCase() + string.slice(1); +} diff --git a/src/routes.js b/src/routes.js index bdca249..355572c 100644 --- a/src/routes.js +++ b/src/routes.js @@ -42,14 +42,17 @@ const Shift = React.lazy(() => import('./views/SimproV2/Shift')); const TestGantt = React.lazy(() => import('./views/testgantt')); const UserAdmin = React.lazy(() => import('./views/Master/UserAdmin')); const UserShift = React.lazy(() => import('./views/SimproV2/UserShift')); -const DashboardProject = React.lazy(() => import('./views/DashboardProject')); +// const DashboardProject = React.lazy(() => import('./views/DashboardProject')); const DashboardBOD = React.lazy(() => import('./views/Dashboard/DashboardBOD')); - +const DashboardCustomer = React.lazy(() => import('./views/Dashboard/DashboardCustomer')); +const DashboardProject = React.lazy(() => import('./views/Dashboard/DashboardProject')); const routes = [ { path: '/', exact: true, name: 'Home' }, - { path: '/dashboard', name: 'Dashboard', component: Dashboard}, - { path: '/dashboard-bod', name: 'DashboardBOD', component: DashboardBOD}, + // { path: '/dashboard', name: 'Dashboard', component: Dashboard}, + { path: '/dashboard', name: 'DashboardBOD', component: DashboardBOD}, + { path: '/dashboard-customer/:PROJECT_ID/:GANTT_ID', name: 'DashboardCustomer', component: DashboardCustomer}, + { path: '/dashboard-project/:PROJECT_ID/:GANTT_ID', exact: true, name: 'Dashboard Project', component: DashboardProject }, { path: '/projects', exact: true, name: 'Projects', component: CreatedProyek }, { path: '/projects/:id/:project/gantt', exact: true, name: 'Gantt', component: Gantt }, { path: '/human-resource', exact: true, name: 'Human Resource', component: ResourceWorker }, @@ -97,7 +100,7 @@ const routes = [ { path: '/user-admin', exact: true, name: 'User Admin', component: UserAdmin }, { path: '/user-shift', exact: true, name: 'Shift', component: UserShift }, { path: '/working-hour', exact: true, name: 'Working Hour', component: Shift }, - { path: '/dashboard-project/:ID/:GANTTID', exact: true, name: 'Dashboard Project', component: DashboardProject }, + // { path: '/dashboard-project/:ID/:GANTTID', exact: true, name: 'Dashboard Project', component: DashboardProject }, ]; export default routes; diff --git a/src/views/Dashboard/DashboardBOD.js b/src/views/Dashboard/DashboardBOD.js index 3705ab1..93b8f55 100644 --- a/src/views/Dashboard/DashboardBOD.js +++ b/src/views/Dashboard/DashboardBOD.js @@ -1,483 +1,834 @@ -import React, { useEffect, useState } from 'react'; +import React, { Suspense, useEffect, useMemo, useState } from 'react'; import axios from 'axios' -import { Row, Col, Button } from 'antd'; -import { - Chart as ChartJS, - CategoryScale, - LineController, - LineElement, - LinearScale, - BarController, - BarElement, - PieController, - Title, - Tooltip, - Legend, - ArcElement -} from 'chart.js'; -// import Chart from 'chart.js/auto'; -import {Chart, Bar, Line, Pie } from 'react-chartjs-2'; -import ChartDataLabels from 'chartjs-plugin-datalabels'; - -ChartJS.register( - CategoryScale, - LineController, - LineElement, - LinearScale, - BarController, - BarElement, - PieController, - Title, - Tooltip, - Legend, - ArcElement, - ChartDataLabels -); - -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} -} +import { Row, Col } from 'antd'; +import { CardDashboard, CardScheduleHealthPerDivision, ContentLoaderChart, NoDataChart } from '../../components/CardDashboard/CardDashboard'; +import { Bar } from 'react-chartjs-2'; +import BottomModal from '../../components/BottomModal/BottomModal'; +import moment from 'moment'; +import { NotificationContainer, NotificationManager } from 'react-notifications'; +import { BASE_OSPRO } from '../../const/ApiConst'; +import { capitalizeFirstLetter, sortBy } from '../../const/CustomFunc'; +import ContentLoader from 'react-content-loader'; +import toRupiah from '@develoka/angka-rupiah-js'; +import Icon from '@iconify/react'; -const verticalBarChartOption = { - elements: { - bar: { - borderWidth: 2, - }, - }, - indexAxis: 'x', - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: true, - position: 'right', - labels: { - boxWidth: 10 - } - }, - datalabels: { - color: '#FFFFFF' - } - } -} +const DashboardBOD = () => { + const token = localStorage.getItem("token") + const HEADER = { + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + } + } + const [openDetailedView, setOpenDetailedView] = useState(false) + const [filterYear, setFilterYear] = useState(moment().format('YYYY')); + const [healthPerDivisionMode, setHealthPerDivisionMode] = useState('schedule'); // schedule / budget + const [READY_PROJECT_EXPENDITURE, SET_READY_PROJECT_EXPENDITURE] = useState(false); + const [READY_PROJECT_BY_PHASE, SET_READY_PROJECT_BY_PHASE] = useState(false); + const [READY_PROJECT_BY_FINANCIAL_HEALTH, SET_READY_PROJECT_BY_FINANCIAL_HEALTH] = useState(false); + const [READY_PROJECT_BY_SCHEDULE_HEALTH, SET_READY_PROJECT_BY_SCHEDULE_HEALTH] = useState(false); + const [READY_PROJECT_PER_DIVISION, SET_READY_PROJECT_PER_DIVISION] = useState(false); + const [READY_PROJECT_VALUE_PER_DIVISION, SET_READY_PROJECT_VALUE_PER_DIVISION] = useState(false); + const [READY_PROJECT_INVOICE_VS_CASH_IN, SET_READY_PROJECT_INVOICE_VS_CASH_IN] = useState(false); + const [READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION, SET_READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION] = useState(false); + + const [PROJECT_EXPENDITURE, SET_PROJECT_EXPENDITURE] = useState(null); + const [PROJECT_BY_PHASE, SET_PROJECT_BY_PHASE] = useState(null); + const [PROJECT_BY_FINANCIAL_HEALTH, SET_PROJECT_BY_FINANCIAL_HEALTH] = useState(null); + const [PROJECT_BY_SCHEDULE_HEALTH, SET_PROJECT_BY_SCHEDULE_HEALTH] = useState(null); + const [PROJECT_PER_DIVISION, SET_PROJECT_PER_DIVISION] = useState(null); + const [PROJECT_VALUE_PER_DIVISION, SET_PROJECT_VALUE_PER_DIVISION] = useState(null); + const [PROJECT_INVOICE_VS_CASH_IN, SET_PROJECT_INVOICE_VS_CASH_IN] = useState(null); + const [PROJECT_SCHEDULE_HEALTH_PER_DIVISION, SET_PROJECT_SCHEDULE_HEALTH_PER_DIVISION] = useState(null); + const [PROJECT_BUDGET_HEALTH_PER_DIVISION, SET_PROJECT_BUDGET_HEALTH_PER_DIVISION] = useState(null); -const horizontalBarChartOption = { - indexAxis: 'y', - elements: { - bar: { - borderWidth: 2, - }, - }, - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: false, - }, - datalabels: { - color: '#FFFFFF' - } - } -} -const pieChartOption = { - elements: { - pie: { - borderWidth: 2, - }, - }, - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: true, - position: 'right', - labels: { - boxWidth: 10 - } - }, - datalabels: { - color: '#FFFFFF' - } - } -} + useEffect(() => { + getCompanyCashFlow(); // expenditure + getProjectPerPhase(); // project by phase + getInvoiceOutstanding(); // project invoice vs cash in + getProjectPerBudgetHealth(); // project by financial health + getProjectPerScheduleHealth(); // project by schedule health + getProjectBudgetHealthPerDivision(); // project by budget health per division + getProjectScheduleHealthPerDivision(); // project by schedule health per division + getTotalProjectPerDivision(); // project by division + getTotalProjectValuePerDivision(); // project value by division + }, []) -const lineChartOption = { - elements: { - line: { - borderWidth: 2, - }, - }, - responsive: false, - plugins: { - datalabels: { - color: '#FFFFFF' - } - } -} -const optionsExpenditure = { - indexAxis: 'y', - elements: { - bar: { - borderWidth: 2, - }, - }, - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - display: false, - position: 'right', - labels: { - boxWidth: 10 - } - }, - datalabels: { - color: '#FFFFFF' - } - // title: { - // display: true, - // text: 'Chart.js Horizontal Bar Chart', - // }, - }, -}; - -const optionsScheduleHealthPerDivision = { - elements: { - bar: { - borderWidth: 2, - }, - }, - responsive: false, - plugins: { - legend: { - display: true, - position: 'top', - labels: { - boxWidth: 10 - } - }, - datalabels: { - color: '#FFFFFF' - } - } -} -const dataExpenditure = { - labels: ['Total Budget', 'Expenditure', 'Invoice', 'Cash In'], - datasets: [ - { - label: '', - data: [302, 197, 197, 157], - borderColor: ['#480CA8', '#B5179E', '#A26A16', '#4C4747'], - backgroundColor: ['#480CA8', '#B5179E', '#A26A16', '#4C4747'], - borderRadius: 5, - borderSkipped: false - } - ], -}; - -const dataScheduleHealthPerDivision = { - labels: ['Telecommunication Solution Service', 'Telecommunication Solution Product', 'Energy Solution', 'Constructions'], - datasets: [ - { - label: "Behind Schedule", - data: [1, 0, 0, 0], - borderColor: '#E80053', - backgroundColor: '#E80053', - borderWidth: 2, - borderRadius: 20, - borderSkipped: false - }, - { - label: "Early Warning", - data: [2, 0, 1, 1], - borderColor: '#FFD600', - backgroundColor: '#FFD600', - borderWidth: 2, - borderRadius: 20, - borderSkipped: false - }, - { - label: "On Schedule", - data: [4, 2, 3, 1], - borderColor: '#52AC0B', - backgroundColor: '#52AC0B', - borderWidth: 2, - borderRadius: 20, - borderSkipped: false - } - ], -} + // project expenditure + const getCompanyCashFlow = async () => { + const URL = `${BASE_OSPRO}/api/dashboard/get-company-cashflow/${filterYear}` + const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + const content = "Get Project Expenditure."; + + if (!result) { + NotificationManager.error(`${content} Could not connect to internet.`, "Failed"); + SET_READY_PROJECT_EXPENDITURE(true); + return; + } -const CardExpenditure = ({title, subtitle}) => { - return ( -
-
-
-
{title}
-
{subtitle}
-
-
-
Detailed View
-
-
- -
- -
- -
- ) -} -const CardDashboard = ({title, subtitle, chartType, chartData, chartOption}) => { - let chart = null; - if (chartType === 'vertical-bar') { - chart = - } - else if (chartType === 'horizontal-bar') { - chart = - } - else if (chartType === 'pie') { - chart = - } - else if (chartType === 'line') { - chart = - } - - return ( -
-
-
-
{title}
-
{subtitle}
-
-
- -
- {chart} -
- -
- ) -} + if (result.status !== 200) { + NotificationManager.error(`${content} ${result.data.message}`, "Failed"); + SET_READY_PROJECT_EXPENDITURE(true); + return; + } -const CardScheduleHealthPerDivision = ({title, subtitle, chartData, chartOption}) => { - return ( -
-
-
-
{title}
-
{subtitle}
-
-
-
Budget
-
Schedule
-
-
- -
- -
- -
- ) -} + if (result.status == 200 && result.data.data) { + SET_PROJECT_EXPENDITURE(result.data.data); + } -const DashboardBOD = () => { - const token = localStorage.getItem("token") - const HEADER = { - headers: { - "Content-Type": "application/json", - "Authorization": `Bearer ${token}` - } - } - - return ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ); + SET_READY_PROJECT_EXPENDITURE(true); + } + + const getInvoiceOutstanding = async () => { + const URL = `${BASE_OSPRO}/api/dashboard/get-invoice-outstanding/${filterYear}` + const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + const content = "Get Project Invoice vs Cash In."; + + if (!result) { + NotificationManager.error(`${content} Could not connect to internet.`, "Failed"); + SET_READY_PROJECT_INVOICE_VS_CASH_IN(true); + return; + } + + if (result.status !== 200) { + NotificationManager.error(`${content} ${result.data.message}`, "Failed"); + SET_READY_PROJECT_INVOICE_VS_CASH_IN(true); + return; + } + + if (result.status == 200 && result.data.data) { + SET_PROJECT_INVOICE_VS_CASH_IN(result.data.data); + } + SET_READY_PROJECT_INVOICE_VS_CASH_IN(true); + } + + const getProjectPerScheduleHealth = async () => { + const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-schedule-health/${filterYear}` + const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + const content = "Get Project by Schedule Health."; + + if (!result) { + NotificationManager.error(`${content} Could not connect to internet.`, "Failed"); + SET_READY_PROJECT_BY_SCHEDULE_HEALTH(true) + return; + } + + if (result.status !== 200) { + NotificationManager.error(`${content} ${result.data.message}`, "Failed"); + SET_READY_PROJECT_BY_SCHEDULE_HEALTH(true) + return; + } + + if (result.status == 200 && result.data.data) { + SET_PROJECT_BY_SCHEDULE_HEALTH(result.data.data); + } + + SET_READY_PROJECT_BY_SCHEDULE_HEALTH(true) + } + + const getProjectPerBudgetHealth = async () => { + const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-budget-health/${filterYear}` + const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + const content = "Get Project by Budget Health."; + + if (!result) { + NotificationManager.error(`${content} Could not connect to internet.`, "Failed"); + SET_READY_PROJECT_BY_FINANCIAL_HEALTH(true); + return; + } + + if (result.status !== 200) { + NotificationManager.error(`${content} ${result.data.message}`, "Failed"); + SET_READY_PROJECT_BY_FINANCIAL_HEALTH(true); + return; + } + + if (result.status == 200 && result.data.data) { + SET_PROJECT_BY_FINANCIAL_HEALTH(result.data.data) + } + SET_READY_PROJECT_BY_FINANCIAL_HEALTH(true); + } + + const getProjectBudgetHealthPerDivision = async () => { + const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-budget-health-per-division/${filterYear}` + const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + const content = "Get Project by Schedule Health per Division."; + + if (!result) { + NotificationManager.error(`${content} Could not connect to internet.`, "Failed"); + SET_READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION(true); + return; + } + + if (result.status !== 200) { + NotificationManager.error(`${content} ${result.data.message}`, "Failed"); + SET_READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION(true); + return; + } + + if (result.status == 200 && result.data.data) { + SET_PROJECT_BUDGET_HEALTH_PER_DIVISION(result.data.data[0]); + } + SET_READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION(true); + + } + + const getProjectScheduleHealthPerDivision = async () => { + const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-schedule-health-per-division/${filterYear}` + const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + const content = "Get Project by Schedule Health."; + + if (!result) { + NotificationManager.error(`${content} Could not connect to internet.`, "Failed"); + SET_READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION(true); + return; + } + + if (result.status !== 200) { + NotificationManager.error(`${content} ${result.data.message}`, "Failed"); + SET_READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION(true); + return; + } + + if (result.status == 200 && result.data.data) { + SET_PROJECT_SCHEDULE_HEALTH_PER_DIVISION(result.data.data[0]); + } + SET_READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION(true); + } + + const getProjectPerPhase = async () => { + const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-phase/${filterYear}` + const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + const content = "Get Project by Phase."; + + if (!result) { + NotificationManager.error(`${content} Could not connect to internet.`, "Failed"); + SET_READY_PROJECT_BY_PHASE(true); + return; + } + + if (result.status !== 200) { + NotificationManager.error(`${content} ${result.data.message}`, "Failed"); + SET_READY_PROJECT_BY_PHASE(true); + return; + } + + if (result.status == 200 && result.data.data) { + let sortedProjectByPhase = sortBy(result.data.data[0], { prop: 'order', desc: false }); + SET_PROJECT_BY_PHASE(sortedProjectByPhase); + } + + SET_READY_PROJECT_BY_PHASE(true); + } + + const getTotalProjectPerDivision = async () => { + const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-division/${filterYear}` + const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + const content = "Get Project by Division."; + + if (!result) { + NotificationManager.error(`${content} Could not connect to internet.`, "Failed"); + SET_READY_PROJECT_PER_DIVISION(true); + return; + } + + if (result.status !== 200) { + NotificationManager.error(`${content} ${result.data.message}`, "Failed"); + SET_READY_PROJECT_PER_DIVISION(true); + return; + } + + if (result.status == 200 && result.data.data) { + SET_PROJECT_PER_DIVISION(result.data.data); + } + SET_READY_PROJECT_PER_DIVISION(true); + } + const getTotalProjectValuePerDivision = async () => { + const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-value-per-division/${filterYear}` + const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) + const content = "Get Project Value by Division"; + + if (!result) { + NotificationManager.error(`${content} Could not connect to internet.`, "Failed"); + SET_READY_PROJECT_VALUE_PER_DIVISION(true); + return; + } + + if (result.status !== 200) { + NotificationManager.error(`${content} ${result.data.message}`, "Failed"); + SET_READY_PROJECT_VALUE_PER_DIVISION(true); + return; + } + + if (result.status == 200 && result.data.data) { + SET_PROJECT_VALUE_PER_DIVISION(result.data.data); + } + SET_READY_PROJECT_VALUE_PER_DIVISION(true); + } + + + const RenderBottomModal = useMemo(() => { + if (openDetailedView) { + return ( + setOpenDetailedView(false)} + /> + ) + } + else { + return null; + } + }, [openDetailedView]) + + const FloatingFilter = () => { + return ( +
+ +
+ ) + } + + return ( + +
+ {/* */} + + + + + +
+
+
+
Project Expenditure
+
Total Project Expenditure from on-going project.
+
+ {PROJECT_EXPENDITURE ? +
setOpenDetailedView(!openDetailedView)}> +
Detailed View
+
+ : +
+
Detailed View
+
+ } + +
+
+ {READY_PROJECT_EXPENDITURE ? + PROJECT_EXPENDITURE ? + + : + + : + + {/* + + + */} + + + + + } + +
+
+ +
+ + + + + + + + + + + + + item.name) : [], + datasets: [ + { + label: "", + // data: [7, 2, 4, 3], + data: PROJECT_PER_DIVISION ? PROJECT_PER_DIVISION.map((item, idx) => item.total) : [], + borderColor: ["#023E8A", "#C851B7", "#FD7034", "#3A0CA3"], + backgroundColor: ["#023E8A", "#C851B7", "#FD7034", "#3A0CA3"], + borderWidth: 2, + borderSkipped: false + }, + ], + } : null} + /> + + + item.name) : [], + datasets: [ + { + label: "", + // data: [50, 120, 72, 60], + data: PROJECT_VALUE_PER_DIVISION ? PROJECT_VALUE_PER_DIVISION.map((item, idx) => item.total) : [], + borderColor: ["#023E8A", "#C851B7", "#FD7034", "#3A0CA3"], + backgroundColor: ["#023E8A", "#C851B7", "#FD7034", "#3A0CA3"], + borderWidth: 2, + borderSkipped: false + }, + ], + } : null} + /> + + + + + + +
+
+
+
Project By Phase
+
Progress of project from multiple division.
+
+
+
+ {READY_PROJECT_BY_PHASE ? + PROJECT_BY_PHASE && PROJECT_BY_PHASE.length > 0 ? + item.name) : [], + datasets: [ + { + label: "", + // data: [3, 1, 2, 3, 7], + // borderColor: ["#F74B25", "#B5179E", "#7209B7", "#023E8A", "#3B009A"], + // backgroundColor: ["#F74B25", "#B5179E", "#7209B7", "#023E8A", "#3B009A"], + data: PROJECT_BY_PHASE ? PROJECT_BY_PHASE.map((item, idx) => item.totalProject) : [], + borderColor: PROJECT_BY_PHASE ? PROJECT_BY_PHASE.map((item, idx) => item.color) : [], + backgroundColor: PROJECT_BY_PHASE ? PROJECT_BY_PHASE.map((item, idx) => item.color) : [], + borderWidth: 2, + borderRadius: 5, + borderSkipped: false + }, + ], + }} + /> + : + + : + + {/* + + + */} + + + + + } + +
+
+ +
+ + +
+
+
+
Project Invoice vs Cash In - Outstanding Expense
+
Total Invoice vs Cash In taken from all project.
+
+
+
+ { READY_PROJECT_INVOICE_VS_CASH_IN ? + PROJECT_INVOICE_VS_CASH_IN ? + item.project) : [], + datasets: [ + { + label: "Invoiced", + // data: [16, 8, 12, 10, 13, 12, 10, 10, 8], + data: PROJECT_INVOICE_VS_CASH_IN ? PROJECT_INVOICE_VS_CASH_IN.map((item, idx) => item.invoiced) : [], + borderColor: '#A36A16', + backgroundColor: '#A36A16', + // borderWidth: 2, + borderRadius: 5, + borderSkipped: false + }, + { + label: "Cash In", + // data: [1, 1, 7, 6, 10, 9.5, 8, 9, 7.5], + data: PROJECT_INVOICE_VS_CASH_IN ? PROJECT_INVOICE_VS_CASH_IN.map((item, idx) => item.paid) : [], + borderColor: '#4C4747', + backgroundColor: '#4C4747', + // borderWidth: 2, + borderRadius: 5, + borderSkipped: false + } + ], + }} + /> + : + + : + + } +
+
+ +
+ + + setHealthPerDivisionMode(mode)} + chartType="vertical-bar" + chartData={ PROJECT_SCHEDULE_HEALTH_PER_DIVISION || PROJECT_BUDGET_HEALTH_PER_DIVISION ? { + // labels: ['Telecommunication Solution Service', 'Telecommunication Solution Product', 'Energy Solution', 'Constructions'], + // labels: [['Telecommunication', 'Solution Service'], ['Telecommunication', 'Solution Product'], 'Energy Solution', 'Constructions'], + labels: + healthPerDivisionMode === 'schedule' ? + PROJECT_SCHEDULE_HEALTH_PER_DIVISION ? PROJECT_SCHEDULE_HEALTH_PER_DIVISION.map((item, idx) => item.name) : [] + : PROJECT_BUDGET_HEALTH_PER_DIVISION ? PROJECT_BUDGET_HEALTH_PER_DIVISION.map((item, idx) => item.name) : [] + , + datasets: [ + { + label: healthPerDivisionMode === 'schedule' ? "Behind Schedule" : "Overrun", + // data: [1, 0, 0, 0], + data: healthPerDivisionMode === 'schedule' ? + PROJECT_SCHEDULE_HEALTH_PER_DIVISION ? PROJECT_SCHEDULE_HEALTH_PER_DIVISION.map((item, idx) => item.scheduleData.behindSchedule) : [] + : PROJECT_BUDGET_HEALTH_PER_DIVISION ? PROJECT_BUDGET_HEALTH_PER_DIVISION.map((item, idx) => item.budgetData.overrun) : [] + , + borderColor: '#E80053', + backgroundColor: '#E80053', + borderWidth: 2, + borderRadius: 5, + borderSkipped: false + }, + { + label: healthPerDivisionMode === 'schedule' ? "Early Warning" : "Warning", + // data: [2, 0, 1, 1], + data: healthPerDivisionMode === 'schedule' ? + PROJECT_SCHEDULE_HEALTH_PER_DIVISION ? PROJECT_SCHEDULE_HEALTH_PER_DIVISION.map((item, idx) => item.scheduleData.warning) : [] + : PROJECT_BUDGET_HEALTH_PER_DIVISION ? PROJECT_BUDGET_HEALTH_PER_DIVISION.map((item, idx) => item.budgetData.warning) : [] + , + borderColor: '#FFD600', + backgroundColor: '#FFD600', + borderWidth: 2, + borderRadius: 5, + borderSkipped: false + }, + { + label: healthPerDivisionMode === 'schedule' ? "On Schedule" : "On Budget", + // data: [4, 2, 3, 1], + data: healthPerDivisionMode === 'schedule' ? + PROJECT_SCHEDULE_HEALTH_PER_DIVISION ? PROJECT_SCHEDULE_HEALTH_PER_DIVISION.map((item, idx) => item.scheduleData.onSchedule) : [] + : PROJECT_BUDGET_HEALTH_PER_DIVISION ? PROJECT_BUDGET_HEALTH_PER_DIVISION.map((item, idx) => item.budgetData['on-budget']) : [] + , + borderColor: '#52AC0B', + backgroundColor: '#52AC0B', + borderWidth: 2, + borderRadius: 5, + borderSkipped: false + } + ], + } : null} + /> + + + +
+ {RenderBottomModal} +
+ ); } export default DashboardBOD; diff --git a/src/views/Dashboard/DashboardCustomer.js b/src/views/Dashboard/DashboardCustomer.js new file mode 100644 index 0000000..1b9ad11 --- /dev/null +++ b/src/views/Dashboard/DashboardCustomer.js @@ -0,0 +1,244 @@ +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import axios from 'axios' +import { Row, Col } 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 { BASE_OSPRO } from '../../const/ApiConst'; + +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.200000, + lng: 106.816666 +} + +const DashboardCustomer = () => { + const token = localStorage.getItem("token") + const HEADER = { + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + } + } + 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=${BASE_OSPRO}/api&gantt_id=${GANTT_ID}&proyek_id=${PROJECT_ID}&token=${token}&ro=1`; + const mapRef = useRef() + const [projectName, setProjectName] = useState("Project Tower ABC"); + const [customerName, setCustomerName] = useState("Jaya Gedung Group"); + const [plannedStart, setPlannedStart] = useState("2019-03-04") + const [plannedFinish, setPlannedFinish] = useState("2020-05-28") + const [actualStart, setActualStart] = useState("2019-03-04") + const [actualFinish, setActualFinish] = useState("2020-05-28") + const [estimatedFinish, setEstimatedFinish] = useState("2020-05-28") + const [mymap, setMymap] = useState(null); + const [activeTabIdx, setActiveTabIdx] = useState(0); + const [activeTabCommentIdx, setActiveTabCommentIdx] = useState(0); + const [planningProgress, setPlanningProgress] = useState(79); + const [actualProgress, setActualProgress] = useState(50); + + + + useEffect(() => { + console.log('URL_GANTT', URL_GANTT); + return () => { + console.log('unmount RenderMap'); + } + }, []) + + useEffect(() => { + if (activeTabIdx === 1) { + initMap(); + } + }, [activeTabIdx]); + + 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); + } + + const RenderGantt = useMemo(() => ( + + ), [activeTabIdx]) + + const Comment = ({name, division, message}) => ( +
+
{name}
+
{division}
+
{message}
+
+ ) + + return ( +
+ + + + + {/*
+
+
+
Project
+
Project Tower ABC
+
+
+ +
+
+
*/} +
+
+
+
Project
+
{projectName}
+
+
+ +
+
+
+ + + +
+
+
+
Customer
+
{customerName}
+
+
+ +
+
+
+ +
+ + +
+
+
+
Schedule
+
+
+ + Planned Start + {plannedStart ? moment(plannedStart).format('D MMMM YYYY') : '-'} + Planned Finish + {plannedFinish ? moment(plannedFinish).format('D MMMM YYYY') : '-'} + Actual Finish + {actualFinish ? moment(actualFinish).format('D MMMM YYYY') : '-'} + + + Actual Start + {actualStart ? moment(actualStart).format('D MMMM YYYY') : '-'} + Estimated Finish + {estimatedFinish ? moment(estimatedFinish).format('D MMMM YYYY') : '-'} + +
+
+
+ +
+
+ + +
setActiveTabIdx(0)}>S Curve
+ + +
setActiveTabIdx(1)}>Maps
+ +
+ + +
+ { activeTabIdx === 0 &&
{RenderGantt}
} + { activeTabIdx === 1 &&
} +
+ +
+
+ + +
+ + +
+
Progress
+
+
Planning : {planningProgress}%
+
+
+
{actualProgress && actualProgress < 50 ? `${actualProgress}%` : `Actual : ${actualProgress}%` }
+
+
+ +
+ + +
+
Health By Schedule
+
+
Behind Schedule
+
+
+ +
+ + +
+ {/*
+
setActiveTabCommentIdx(0)}>Behind Task
+
setActiveTabCommentIdx(1)}>Comment From Customer
+
*/} + + +
setActiveTabCommentIdx(0)}>Behind Task
+ + +
setActiveTabCommentIdx(1)}>Comment From Customer
+ +
+
+ + + + + +
+
+ +
+
+ +
+
+ ); +} + +export default DashboardCustomer; diff --git a/src/views/Dashboard/DashboardProject.js b/src/views/Dashboard/DashboardProject.js new file mode 100644 index 0000000..f1a0024 --- /dev/null +++ b/src/views/Dashboard/DashboardProject.js @@ -0,0 +1,323 @@ +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import axios from 'axios' +import { Row, Col } 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'; + +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.200000, + 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 } = 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 mapRef = useRef() + const [projectName, setProjectName] = useState("Project Tower ABC"); + const [projectManagerName, setProjectManagerName] = useState("John Doe"); + const [customerName, setCustomerName] = useState("Jaya Gedung Group"); + const [plannedStart, setPlannedStart] = useState("2019-03-04") + const [plannedFinish, setPlannedFinish] = useState("2020-05-28") + const [actualStart, setActualStart] = useState("2019-03-04") + const [actualFinish, setActualFinish] = useState("2020-05-28") + const [estimatedFinish, setEstimatedFinish] = useState("2020-05-28") + const [mymap, setMymap] = useState(null); + const [activeTabIdx, setActiveTabIdx] = useState(0); + const [activeTabCommentIdx, setActiveTabCommentIdx] = useState(0); + const [planningProgress, setPlanningProgress] = useState(79); + const [actualProgress, setActualProgress] = useState(43); + const [currentBudget, setCurrentBudget] = useState("168011727772"); + const [addCostToComplete, setAddCostToComplete] = useState("0"); + const [actualToDate, setactualToDate] = useState("43256751714"); + const [estAtCompletion, setEstAtCompletion] = useState("168011727772"); + const [bcwp, setBcwp] = useState("26658794618"); + const [costDeviation, setCostDeviation] = useState("0"); + const [remToComplete, setRemToComplete] = useState("124754976058"); + const [totalInvoice, setTotalInvoice] = useState("10000000000"); + const [cashIn, setCashIn] = useState("8000000000"); + const [outstandingBalance, setOutstandingBalance] = useState("2000000000"); + + + + useEffect(() => { + console.log('URL_GANTT', URL_GANTT); + return () => { + console.log('unmount RenderMap'); + } + }, []) + + useEffect(() => { + if (activeTabIdx === 1) { + initMap(); + } + }, [activeTabIdx]); + + 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); + } + + const RenderGantt = useMemo(() => ( + + ), [activeTabIdx]) + + const Comment = ({name, division, message}) => ( +
+
{name}
+
{division}
+
{message}
+
+ ) + + return ( +
+ + + + +
+
+
+
Project
+
{projectName}
+
+
+ +
+
+
+ + +
+
+
+
Project Manager
+
{projectManagerName}
+
+
+ +
+
+
+ + +
+
+
+
Customer
+
{customerName}
+
+
+ +
+
+
+ +
+ + +
+
+
+
Schedule
+
+
+ + Planned Start + {plannedStart ? moment(plannedStart).format('D MMMM YYYY') : '-'} + + + Acutal Start + {plannedStart ? moment(actualStart).format('D MMMM YYYY') : '-'} + + + Planned Finish + {plannedStart ? moment(plannedFinish).format('D MMMM YYYY') : '-'} + + + Estimated Finish + {plannedStart ? moment(estimatedFinish).format('D MMMM YYYY') : '-'} + +
+
+
+ + +
+
+
+
Financials
+
+
+ + Current Budget + {currentBudget ? renderFormatRupiah(currentBudget, 'Rp.') : '-' } + Add Cost to Complete + {addCostToComplete ? renderFormatRupiah(addCostToComplete, 'Rp.') : '-' } + + + Actual to Date + {actualToDate ? renderFormatRupiah(actualToDate, 'Rp.') : '-' } + Est. at Completion + {estAtCompletion ? renderFormatRupiah(estAtCompletion, 'Rp.') : '-' } + + + BCWP (cost vs perform) + {bcwp ? renderFormatRupiah(bcwp, 'Rp.') : '-' } + Cost Deviation + {costDeviation ? renderFormatRupiah(costDeviation, 'Rp.') : '-' } + + + Rem. to Complete + {remToComplete ? renderFormatRupiah(remToComplete, 'Rp.') : '-' } + +
+
+
+ +
+
+ + +
setActiveTabIdx(0)}>S Curve
+ + +
setActiveTabIdx(1)}>Maps
+ +
+ + +
+ { activeTabIdx === 0 &&
{RenderGantt}
} + { activeTabIdx === 1 &&
} +
+ +
+
+ + +
+ + +
+
Progress
+
+
Planning : {planningProgress}%
+
+
+
Actual : {actualProgress}%
+
+
+ +
+ + +
+
Health By Schedule
+
+
On Budget
+
+
+ + +
+
Health By Schedule
+
+
Behind Schedule
+
+
+ +
+ + +
+ {/*
Health By Schedule
+
+
On Budget
+
*/} + + Total Invoice + {totalInvoice ? renderFormatRupiah(totalInvoice, 'Rp.') : '-' } + + + Cash In + {cashIn ? renderFormatRupiah(cashIn, 'Rp.') : '-' } + + +
+
+ + Outstanding Balance + {outstandingBalance ? renderFormatRupiah(outstandingBalance, 'Rp.') : '-' } + +
+ +
+ + +
+ {/*
+
setActiveTabCommentIdx(0)}>Behind Task
+
setActiveTabCommentIdx(1)}>Comment From Customer
+
*/} + + +
setActiveTabCommentIdx(0)}>Behind Task
+ + +
setActiveTabCommentIdx(1)}>Comment From Customer
+ +
+
+ + + + + +
+
+ +
+
+ +
+
+ ); +} + +export default DashboardProject; \ No newline at end of file