Browse Source

Merge pull request 'Dev-Farhan' (#1) from Dev-Farhan into staging

Reviewed-on: https://gitea.osiutech.com/integrasia-utama/ospro_generic_fe/pulls/1
pull/1/head
farhantock 11 months ago
parent
commit
4e32763360
  1. 203
      src/components/BottomModal/BottomModal.js
  2. 36
      src/const/ApiConst.js
  3. 118
      src/views/Dashboard/DashboardBOD.js
  4. 12
      src/views/Dashboard/DashboardProjectCarousell.js
  5. 113
      src/views/Master/ProjectExpenditure/DialogForm.js
  6. 89
      src/views/Master/ProjectExpenditure/DialogFormInitial.js
  7. 46
      src/views/Master/ProjectExpenditure/InputColor.js
  8. 392
      src/views/Master/ProjectExpenditure/index.js
  9. 41
      src/views/Master/ProjectExpenditure/styles.css
  10. 110
      src/views/Master/ProjectFinancialHealth/DialogForm.js
  11. 89
      src/views/Master/ProjectFinancialHealth/DialogFormInitial.js
  12. 46
      src/views/Master/ProjectFinancialHealth/InputColor.js
  13. 392
      src/views/Master/ProjectFinancialHealth/index.js
  14. 41
      src/views/Master/ProjectFinancialHealth/styles.css
  15. 107
      src/views/Master/ProjectInvoice/DialogForm.js
  16. 89
      src/views/Master/ProjectInvoice/DialogFormInitial.js
  17. 46
      src/views/Master/ProjectInvoice/InputColor.js
  18. 392
      src/views/Master/ProjectInvoice/index.js
  19. 41
      src/views/Master/ProjectInvoice/styles.css
  20. 110
      src/views/Master/ProjectScheduleHealth/DialogForm.js
  21. 89
      src/views/Master/ProjectScheduleHealth/DialogFormInitial.js
  22. 46
      src/views/Master/ProjectScheduleHealth/InputColor.js
  23. 392
      src/views/Master/ProjectScheduleHealth/index.js
  24. 41
      src/views/Master/ProjectScheduleHealth/styles.css
  25. 43
      src/views/SimproV2/CreatedProyek/DialogGantt.js
  26. 39
      src/views/SimproV2/CreatedProyek/index.js

203
src/components/BottomModal/BottomModal.js

@ -6,8 +6,9 @@ import Icon from '@iconify/react'
import closeCircleOutline from '@iconify/icons-ion/close-circle-outline'; import closeCircleOutline from '@iconify/icons-ion/close-circle-outline';
import removeCircleOutline from '@iconify/icons-ion/remove-circle-outline'; import removeCircleOutline from '@iconify/icons-ion/remove-circle-outline';
import windowMaximaze from '@iconify/icons-mdi/window-maximize'; import windowMaximaze from '@iconify/icons-mdi/window-maximize';
import { NotificationContainer, NotificationManager } from 'react-notifications';
const BottomModal = ({title, tableHeader, tableData, closeModal, isReady}) => { const BottomModal = ({ title, tableHeader, tableData, closeModal, isReady }) => {
const [tableHeight, setTableHeight] = useState(150) const [tableHeight, setTableHeight] = useState(150)
const [resizableWidth, setResizableWidth] = useState("100%") const [resizableWidth, setResizableWidth] = useState("100%")
const [resizableHeight, setResizableHeight] = useState(300) const [resizableHeight, setResizableHeight] = useState(300)
@ -39,25 +40,42 @@ const BottomModal = ({title, tableHeader, tableData, closeModal, isReady}) => {
const renderTableRow = (item) => { const renderTableRow = (item) => {
let row = []; let row = [];
// looping through its object keys (item is an object) // looping through its object keys (item is an object)
Object.keys(item).map(key => { Object.keys(item).map(key => {
if (key === 'project_name') { if (key === 'project_name') {
let URL = `/#/dashboard-project/${item['id']}/${item['last_gantt_id']}`; let URL = `/#/dashboard-project/${item['id']}/${item['last_gantt_id']}`;
row.push(<td><a href={URL}>{item[key]}</a></td>) row.push(
} else if(key === 'last_gantt_id') { <td>
} else if(key === 'id') { <a
} else { href={URL}
row.push(<td>{item[key]}</td>) onClick={(e) => {
e.preventDefault();
if (!item['last_gantt_id']) {
NotificationManager.warning('Data Gantt Belum Tersedia.', 'Warning');
} else {
window.location.href = URL;
}
}}
>
{item[key]}
</a>
</td>
);
} else if (key === 'id' || key === 'last_gantt_id') {
} else {
row.push(<td>{item[key]}</td>);
} }
}) });
return row; return row;
} };
const RenderTable = useMemo(() => { const RenderTable = useMemo(() => {
if (tableData && tableData.length > 0) { if (tableData && tableData.length > 0) {
return ( return (
<Table size='sm'> <Table size='sm'>
<thead style={{margin: 0, backgroundColor: '#888888', color: '#FFFFFF', fontSize: 12}}> <thead style={{ margin: 0, backgroundColor: '#888888', color: '#FFFFFF', fontSize: 12 }}>
<tr key={'thead'}> <tr key={'thead'}>
{tableHeader && tableHeader.length > 0 && {tableHeader && tableHeader.length > 0 &&
tableHeader.map((item, idx) => ( tableHeader.map((item, idx) => (
@ -66,8 +84,8 @@ const BottomModal = ({title, tableHeader, tableData, closeModal, isReady}) => {
} }
</tr> </tr>
</thead> </thead>
<tbody style={{backgroundColor: '#EEEEEE'}}> <tbody style={{ backgroundColor: '#EEEEEE' }}>
{tableData && tableData.length > 0 && {tableData && tableData.length > 0 &&
tableData.map((item, index) => { tableData.map((item, index) => {
return (<tr key={index}> return (<tr key={index}>
{renderTableRow(item, tableHeader)} {renderTableRow(item, tableHeader)}
@ -85,92 +103,95 @@ const BottomModal = ({title, tableHeader, tableData, closeModal, isReady}) => {
// <td style={{ fontWeight: "bold" }} colSpan="4" align="center">No Data Available</td> // <td style={{ fontWeight: "bold" }} colSpan="4" align="center">No Data Available</td>
// </tr> // </tr>
// </Table> // </Table>
<div style={{justifyContent: 'center', textAlign: 'center', color: 'red', marginTop: 50, marginBottom: 50}}> <div style={{ justifyContent: 'center', textAlign: 'center', color: 'red', marginTop: 50, marginBottom: 50 }}>
No Data Available No Data Available
</div> </div>
) )
}, [tableData, isReady]) }, [tableData, isReady])
return ( return (
<Resizable <>
style={{ <NotificationContainer />
position: "fixed", <Resizable
width: "100%", style={{
bottom: 0, position: "fixed",
left: 0, width: "100%",
right: 0, bottom: 0,
background: "#222222", left: 0,
borderTopLeftRadius: 10, right: 0,
borderTopRightRadius: 10, background: "#222222",
zIndex: 9999 borderTopLeftRadius: 10,
}} borderTopRightRadius: 10,
size={{ zIndex: 9999
width: resizableWidth, }}
height: resizableHeight size={{
}} width: resizableWidth,
maxHeight="100vh" height: resizableHeight
minWidth="100%" }}
enable={{ top: true, right: false, bottom: false, left: false, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false }} maxHeight="100vh"
onResizeStop={(e, direction, ref, d) => { minWidth="100%"
setResizableWidth("100%"); enable={{ top: true, right: false, bottom: false, left: false, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false }}
setResizableHeight(resizableHeight + d.height); onResizeStop={(e, direction, ref, d) => {
if (e.screenY < 100) { // max setResizableWidth("100%");
setResizableHeight('100vh'); setResizableHeight(resizableHeight + d.height);
setTableHeight(450) if (e.screenY < 100) { // max
} setResizableHeight('100vh');
else if (e.screenY < 150) { setTableHeight(450)
setTableHeight(400) }
} else if (e.screenY < 150) {
else if (e.screenY < 200) { setTableHeight(400)
setTableHeight(350) }
} else if (e.screenY < 200) {
else if (e.screenY < 250) { setTableHeight(350)
setTableHeight(300) }
} else if (e.screenY < 250) {
else if (e.screenY < 300) { setTableHeight(300)
setTableHeight(250) }
} else if (e.screenY < 300) {
else if (e.screenY < 400) { setTableHeight(250)
setTableHeight(200) }
} else if (e.screenY < 400) {
else if (e.screenY < 600) { setTableHeight(200)
setTableHeight(150) // default }
} else if (e.screenY < 600) {
else if (e.screenY >= 600) { setTableHeight(150) // default
setBottomTableWindow('min'); }
} else if (e.screenY >= 600) {
}} setBottomTableWindow('min');
> }
<div style={{ paddingTop: 10, position: "relative", height: "100%" }}> }}
<Row className="bottommodal-header"> >
<Col md={8}> <div style={{ paddingTop: 10, position: "relative", height: "100%" }}>
<p className="bottommodal-title">{title}</p> <Row className="bottommodal-header">
</Col> <Col md={8}>
<Col md={4} className="bottommodal-window-button-container"> <p className="bottommodal-title">{title}</p>
<span className="bottommodal-minimize" title="minimize" </Col>
onClick={() => setBottomTableWindow("min")} <Col md={4} className="bottommodal-window-button-container">
> <span className="bottommodal-minimize" title="minimize"
<Icon icon={removeCircleOutline} width={30} height={30} color="#FFFFFF" /> onClick={() => setBottomTableWindow("min")}
</span> >
<span className="bottommodal-maximize" title={maximizeTitle} <Icon icon={removeCircleOutline} width={30} height={30} color="#FFFFFF" />
onClick={() => setBottomTableWindow("max")} </span>
> <span className="bottommodal-maximize" title={maximizeTitle}
<Icon icon={windowMaximaze} width={30} height={30} color="#FFFFFF" /> onClick={() => setBottomTableWindow("max")}
</span> >
<span className="bottommodal-close" title="close" <Icon icon={windowMaximaze} width={30} height={30} color="#FFFFFF" />
onClick={closeModal} </span>
> <span className="bottommodal-close" title="close"
<Icon icon={closeCircleOutline} width={30} height={30} color="#FFFFFF" /> onClick={closeModal}
</span> >
</Col> <Icon icon={closeCircleOutline} width={30} height={30} color="#FFFFFF" />
</Row> </span>
<Card style={{ margin: 10 }}> </Col>
<CardBody style={{ margin: 0, padding: 0, borderRadius: 5, backgroundColor: '#EEEEEE', overflowY: 'auto', height: resizableHeight === 300 ? 240 : 540 }}> </Row>
{isReady ? RenderTable : <div style={{flex: 1, textAlign: 'center', marginTop: 50}}>Loading data...</div>} <Card style={{ margin: 10 }}>
</CardBody> <CardBody style={{ margin: 0, padding: 0, borderRadius: 5, backgroundColor: '#EEEEEE', overflowY: 'auto', height: resizableHeight === 300 ? 240 : 540 }}>
</Card> {isReady ? RenderTable : <div style={{ flex: 1, textAlign: 'center', marginTop: 50 }}>Loading data...</div>}
</div> </CardBody>
</Resizable> </Card>
</div>
</Resizable>
</>
) )
} }

36
src/const/ApiConst.js

@ -456,6 +456,42 @@ export const PROJECT_PHASE_DELETE = (id) => {
}; };
export const PHASE_PROYEK = `${BASE_SIMPRO_LUMEN}/project-phase/list`; export const PHASE_PROYEK = `${BASE_SIMPRO_LUMEN}/project-phase/list`;
export const PROJECT_EXPENDITURE_ADD = `${BASE_SIMPRO_LUMEN}/project-expenditure/add`;
export const PROJECT_EXPENDITURE_SEARCH = `${BASE_SIMPRO_LUMEN}/project-expenditure/search`;
export const PROJECT_EXPENDITURE_EDIT = (id) => {
return `${BASE_SIMPRO_LUMEN}/project-expenditure/update/${id}`;
};
export const PROJECT_EXPENDITURE_DELETE = (id) => {
return `${BASE_SIMPRO_LUMEN}/project-expenditure/delete/${id}`;
};
export const PROJECT_FINANCIAL_HEALTH_ADD = `${BASE_SIMPRO_LUMEN}/project-financial-health/add`;
export const PROJECT_FINANCIAL_HEALTH_SEARCH = `${BASE_SIMPRO_LUMEN}/project-financial-health/search`;
export const PROJECT_FINANCIAL_HEALTH_EDIT = (id) => {
return `${BASE_SIMPRO_LUMEN}/project-financial-health/update/${id}`;
};
export const PROJECT_FINANCIAL_HEALTH_DELETE = (id) => {
return `${BASE_SIMPRO_LUMEN}/project-financial-health/delete/${id}`;
};
export const PROJECT_SCHEDULE_HEALTH_ADD = `${BASE_SIMPRO_LUMEN}/project-schedule-health/add`;
export const PROJECT_SCHEDULE_HEALTH_SEARCH = `${BASE_SIMPRO_LUMEN}/project-schedule-health/search`;
export const PROJECT_SCHEDULE_HEALTH_EDIT = (id) => {
return `${BASE_SIMPRO_LUMEN}/project-schedule-health/update/${id}`;
};
export const PROJECT_SCHEDULE_HEALTH_DELETE = (id) => {
return `${BASE_SIMPRO_LUMEN}/project-schedule-health/delete/${id}`;
};
export const PROJECT_INVOICE_ADD = `${BASE_SIMPRO_LUMEN}/project-invoice/add`;
export const PROJECT_INVOICE_SEARCH = `${BASE_SIMPRO_LUMEN}/project-invoice/search`;
export const PROJECT_INVOICE_EDIT = (id) => {
return `${BASE_SIMPRO_LUMEN}/project-invoice/update/${id}`;
};
export const PROJECT_INVOICE_DELETE = (id) => {
return `${BASE_SIMPRO_LUMEN}/project-invoice/delete/${id}`;
};
export const PROJECT_CHARTER_ADD = `${BASE_SIMPRO_LUMEN}/project-charter/add`; export const PROJECT_CHARTER_ADD = `${BASE_SIMPRO_LUMEN}/project-charter/add`;
export const PROJECT_CHARTER_SEARCH = `${BASE_SIMPRO_LUMEN}/project-charter/search`; export const PROJECT_CHARTER_SEARCH = `${BASE_SIMPRO_LUMEN}/project-charter/search`;
export const PROJECT_CHARTER_EDIT = (id) => { export const PROJECT_CHARTER_EDIT = (id) => {

118
src/views/Dashboard/DashboardBOD.js

@ -52,6 +52,10 @@ const DashboardBOD = (props) => {
const [READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION, SET_READY_PROJECT_SCHEDULE_BUDGET_HEALTH_PER_DIVISION] = 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_EXPENDITURE, SET_PROJECT_EXPENDITURE] = useState(null);
const [PROJECT_EXPENDITURE_COLOR, SET_PROJECT_EXPENDITURE_COLOR] = useState(null);
const [PROJECT_FINANCIAL_HEALTH_COLOR, SET_PROJECT_FINANCIAL_HEALTH_COLOR] = useState(null);
const [PROJECT_SCHEDULE_HEALTH_COLOR, SET_PROJECT_SCHEDULE_HEALTH_COLOR] = useState(null);
const [PROJECT_INVOICE_COLOR, SET_PROJECT_INVOICE_COLOR] = useState(null);
const [PROJECT_BY_PHASE, SET_PROJECT_BY_PHASE] = 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_FINANCIAL_HEALTH, SET_PROJECT_BY_FINANCIAL_HEALTH] = useState(null);
const [PROJECT_BY_SCHEDULE_HEALTH, SET_PROJECT_BY_SCHEDULE_HEALTH] = useState(null); const [PROJECT_BY_SCHEDULE_HEALTH, SET_PROJECT_BY_SCHEDULE_HEALTH] = useState(null);
@ -66,6 +70,10 @@ const DashboardBOD = (props) => {
useEffect(() => { useEffect(() => {
getCompanyCashFlow(); // expenditure getCompanyCashFlow(); // expenditure
getCompanyExpenditureColor(); // expenditure Color
getCompanyFinancialHealthColor(); // financial health Color
getCompanyScheduleHealthColor(); // schedule health Color
getCompanyInvoiceColor(); // Project Invoice vs Cash In Color
getProjectPerPhase(); // project by phase getProjectPerPhase(); // project by phase
getInvoiceOutstanding(); // project invoice vs cash in getInvoiceOutstanding(); // project invoice vs cash in
getProjectPerBudgetHealth(); // project by financial health getProjectPerBudgetHealth(); // project by financial health
@ -103,8 +111,34 @@ const DashboardBOD = (props) => {
SET_READY_PROJECT_EXPENDITURE(true); SET_READY_PROJECT_EXPENDITURE(true);
} }
// Project Expenditure Color
const getCompanyExpenditureColor = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-detail-expenditure-color/${company_id}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
SET_PROJECT_EXPENDITURE_COLOR(result.data.data)
}
const getCompanyFinancialHealthColor = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-detail-financial-health-color/${company_id}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
SET_PROJECT_FINANCIAL_HEALTH_COLOR(result.data.data)
}
const getCompanyScheduleHealthColor = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-detail-schedule-health-color/${company_id}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
SET_PROJECT_SCHEDULE_HEALTH_COLOR(result.data.data)
}
const getCompanyInvoiceColor = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-detail-invoice-color/${company_id}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
SET_PROJECT_INVOICE_COLOR(result.data.data)
}
const getInvoiceOutstanding = async () => { const getInvoiceOutstanding = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-invoice-outstanding` const URL = `${BASE_OSPRO}/api/dashboard/get-invoice-outstanding/${moment().format('YYYY')}/${company_id}/${all_project}/${hierarchy}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response) const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
const content = "Get Project Invoice vs Cash In."; const content = "Get Project Invoice vs Cash In.";
@ -501,8 +535,18 @@ const DashboardBOD = (props) => {
PROJECT_EXPENDITURE && PROJECT_EXPENDITURE.total_invoice ? Math.floor(PROJECT_EXPENDITURE.total_invoice) : 0, PROJECT_EXPENDITURE && PROJECT_EXPENDITURE.total_invoice ? Math.floor(PROJECT_EXPENDITURE.total_invoice) : 0,
PROJECT_EXPENDITURE && PROJECT_EXPENDITURE.total_paid_invoice ? PROJECT_EXPENDITURE.total_paid_invoice : 0 PROJECT_EXPENDITURE && PROJECT_EXPENDITURE.total_paid_invoice ? PROJECT_EXPENDITURE.total_paid_invoice : 0
], ],
borderColor: ['#480CA8', '#B5179E', '#A26A16', '#4C4747'], borderColor: [
backgroundColor: ['#480CA8', '#B5179E', '#A26A16', '#4C4747'], PROJECT_EXPENDITURE_COLOR && PROJECT_EXPENDITURE_COLOR.total_budget && PROJECT_EXPENDITURE_COLOR.total_budget !== "" ? PROJECT_EXPENDITURE_COLOR.total_budget : '#480ca8',
PROJECT_EXPENDITURE_COLOR && PROJECT_EXPENDITURE_COLOR.total_expenditure && PROJECT_EXPENDITURE_COLOR.total_expenditure !== "" ? PROJECT_EXPENDITURE_COLOR.total_expenditure : '#b5179e',
PROJECT_EXPENDITURE_COLOR && PROJECT_EXPENDITURE_COLOR.total_invoice && PROJECT_EXPENDITURE_COLOR.total_invoice !== "" ? PROJECT_EXPENDITURE_COLOR.total_invoice : '#a26a16',
PROJECT_EXPENDITURE_COLOR && PROJECT_EXPENDITURE_COLOR.total_paid_invoice && PROJECT_EXPENDITURE_COLOR.total_paid_invoice !== "" ? PROJECT_EXPENDITURE_COLOR.total_paid_invoice : '#4c4747'
],
backgroundColor: [
PROJECT_EXPENDITURE_COLOR && PROJECT_EXPENDITURE_COLOR.total_budget && PROJECT_EXPENDITURE_COLOR.total_budget !== "" ? PROJECT_EXPENDITURE_COLOR.total_budget : '#480ca8',
PROJECT_EXPENDITURE_COLOR && PROJECT_EXPENDITURE_COLOR.total_expenditure && PROJECT_EXPENDITURE_COLOR.total_expenditure !== "" ? PROJECT_EXPENDITURE_COLOR.total_expenditure : '#b5179e',
PROJECT_EXPENDITURE_COLOR && PROJECT_EXPENDITURE_COLOR.total_invoice && PROJECT_EXPENDITURE_COLOR.total_invoice !== "" ? PROJECT_EXPENDITURE_COLOR.total_invoice : '#a26a16',
PROJECT_EXPENDITURE_COLOR && PROJECT_EXPENDITURE_COLOR.total_paid_invoice && PROJECT_EXPENDITURE_COLOR.total_paid_invoice !== "" ? PROJECT_EXPENDITURE_COLOR.total_paid_invoice : '#4c4747'
],
borderRadius: 5, borderRadius: 5,
borderSkipped: false borderSkipped: false
} }
@ -553,8 +597,16 @@ const DashboardBOD = (props) => {
PROJECT_BY_FINANCIAL_HEALTH && PROJECT_BY_FINANCIAL_HEALTH.warning ? PROJECT_BY_FINANCIAL_HEALTH.warning : '', PROJECT_BY_FINANCIAL_HEALTH && PROJECT_BY_FINANCIAL_HEALTH.warning ? PROJECT_BY_FINANCIAL_HEALTH.warning : '',
PROJECT_BY_FINANCIAL_HEALTH && PROJECT_BY_FINANCIAL_HEALTH['on-budget'] ? PROJECT_BY_FINANCIAL_HEALTH['on-budget'] : '' PROJECT_BY_FINANCIAL_HEALTH && PROJECT_BY_FINANCIAL_HEALTH['on-budget'] ? PROJECT_BY_FINANCIAL_HEALTH['on-budget'] : ''
].filter(value => value !== null), ].filter(value => value !== null),
borderColor: ["#E80053", "#FFD600", "#52AC0B",], borderColor: [
backgroundColor: ["#E80053", "#FFD600", "#52AC0B",], PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR.overrun && PROJECT_FINANCIAL_HEALTH_COLOR.overrun !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR.overrun : '#E80053',
PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR.warning && PROJECT_FINANCIAL_HEALTH_COLOR.warning !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR.warning : '#FFD600',
PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] && PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] : '#52AC0B'
],
backgroundColor: [
PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR.overrun && PROJECT_FINANCIAL_HEALTH_COLOR.overrun !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR.overrun : '#E80053',
PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR.warning && PROJECT_FINANCIAL_HEALTH_COLOR.warning !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR.warning : '#FFD600',
PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] && PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] : '#52AC0B'
],
borderWidth: 2, borderWidth: 2,
borderSkipped: false borderSkipped: false
}, },
@ -579,8 +631,16 @@ const DashboardBOD = (props) => {
PROJECT_BY_SCHEDULE_HEALTH && PROJECT_BY_SCHEDULE_HEALTH.warning ? PROJECT_BY_SCHEDULE_HEALTH.warning : '', PROJECT_BY_SCHEDULE_HEALTH && PROJECT_BY_SCHEDULE_HEALTH.warning ? PROJECT_BY_SCHEDULE_HEALTH.warning : '',
PROJECT_BY_SCHEDULE_HEALTH && PROJECT_BY_SCHEDULE_HEALTH['on-schedule'] ? PROJECT_BY_SCHEDULE_HEALTH['on-schedule'] : '' PROJECT_BY_SCHEDULE_HEALTH && PROJECT_BY_SCHEDULE_HEALTH['on-schedule'] ? PROJECT_BY_SCHEDULE_HEALTH['on-schedule'] : ''
], ],
borderColor: ["#E80053", "#FFD600", "#52AC0B"], borderColor: [
backgroundColor: ["#E80053", "#FFD600", "#52AC0B"], PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] && PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] : '#E80053',
PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR.warning && PROJECT_SCHEDULE_HEALTH_COLOR.warning !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR.warning : '#FFD600',
PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] && PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] : '#52AC0B'
],
backgroundColor: [
PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] && PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] : '#E80053',
PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR.warning && PROJECT_SCHEDULE_HEALTH_COLOR.warning !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR.warning : '#FFD600',
PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] && PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] : '#52AC0B'
],
borderWidth: 2, borderWidth: 2,
borderSkipped: false borderSkipped: false
}, },
@ -740,9 +800,6 @@ const DashboardBOD = (props) => {
datasets: [ datasets: [
{ {
label: "", 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) : [], data: PROJECT_BY_PHASE ? PROJECT_BY_PHASE.map((item, idx) => item.totalProject) : [],
borderColor: PROJECT_BY_PHASE ? PROJECT_BY_PHASE.map((item, idx) => item.color) : [], borderColor: PROJECT_BY_PHASE ? PROJECT_BY_PHASE.map((item, idx) => item.color) : [],
backgroundColor: PROJECT_BY_PHASE ? PROJECT_BY_PHASE.map((item, idx) => item.color) : [], backgroundColor: PROJECT_BY_PHASE ? PROJECT_BY_PHASE.map((item, idx) => item.color) : [],
@ -835,27 +892,21 @@ const DashboardBOD = (props) => {
} }
}} }}
data={{ data={{
// labels: ["Gedung Tenaga Panel Surya", "Pembangunan Gedung Tower ABC", "Tower Jaringan Jawa Barat", "Tower Jaringan Jawa Timur", "Tower Jaringan Jawa Tengah", "Tower Jaringan Bali", "Project Tower ABC", "Tower Jaringan DKI Jakarta", "Tower Jaringan NTT"],
// labels: [["Gedung Tenaga", "Panel Surya"], ["Pembangunan Gedung", "Tower ABC"], ["Tower Jaringan", "Jawa Barat"], ["Tower Jaringan", "Jawa Timur"], ["Tower Jaringan", "Jawa Tengah"], "Tower Jaringan Bali", "Project Tower ABC", ["Tower Jaringan", "DKI Jakarta"], "Tower Jaringan NTT"],
labels: PROJECT_INVOICE_VS_CASH_IN ? PROJECT_INVOICE_VS_CASH_IN.map((item, idx) => item.project_code) : [], labels: PROJECT_INVOICE_VS_CASH_IN ? PROJECT_INVOICE_VS_CASH_IN.map((item, idx) => item.project_code) : [],
datasets: [ datasets: [
{ {
label: "Invoiced", 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) => Math.floor(item.invoiced)) : [], data: PROJECT_INVOICE_VS_CASH_IN ? PROJECT_INVOICE_VS_CASH_IN.map((item, idx) => Math.floor(item.invoiced)) : [],
borderColor: '#A36A16', borderColor: PROJECT_INVOICE_COLOR && PROJECT_INVOICE_COLOR.invoiced && PROJECT_INVOICE_COLOR.invoiced !== "" ? PROJECT_INVOICE_COLOR.invoiced : '#A36A16',
backgroundColor: '#A36A16', backgroundColor: PROJECT_INVOICE_COLOR && PROJECT_INVOICE_COLOR.invoiced && PROJECT_INVOICE_COLOR.invoiced !== "" ? PROJECT_INVOICE_COLOR.invoiced : '#A36A16',
// borderWidth: 2,
borderRadius: 5, borderRadius: 5,
borderSkipped: false borderSkipped: false
}, },
{ {
label: "Cash In", 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) : [], data: PROJECT_INVOICE_VS_CASH_IN ? PROJECT_INVOICE_VS_CASH_IN.map((item, idx) => item.paid) : [],
borderColor: '#4C4747', borderColor: PROJECT_INVOICE_COLOR && PROJECT_INVOICE_COLOR.paid && PROJECT_INVOICE_COLOR.paid !== "" ? PROJECT_INVOICE_COLOR.paid : '#4C4747',
backgroundColor: '#4C4747', backgroundColor: PROJECT_INVOICE_COLOR && PROJECT_INVOICE_COLOR.paid && PROJECT_INVOICE_COLOR.paid !== "" ? PROJECT_INVOICE_COLOR.paid : '#4C4747',
// borderWidth: 2,
borderRadius: 5, borderRadius: 5,
borderSkipped: false borderSkipped: false
} }
@ -891,39 +942,48 @@ const DashboardBOD = (props) => {
datasets: [ datasets: [
{ {
label: healthPerDivisionMode === 'schedule' ? "Behind Schedule" : "Overrun", label: healthPerDivisionMode === 'schedule' ? "Behind Schedule" : "Overrun",
// data: [1, 0, 0, 0],
data: healthPerDivisionMode === 'schedule' ? data: healthPerDivisionMode === 'schedule' ?
PROJECT_SCHEDULE_HEALTH_PER_DIVISION ? PROJECT_SCHEDULE_HEALTH_PER_DIVISION.map((item, idx) => item.scheduleData.behindSchedule) : [] 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) : [] : PROJECT_BUDGET_HEALTH_PER_DIVISION ? PROJECT_BUDGET_HEALTH_PER_DIVISION.map((item, idx) => item.budgetData.overrun) : []
, ,
borderColor: '#E80053', borderColor: healthPerDivisionMode === 'schedule' ?
backgroundColor: '#E80053', PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] && PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] : '#E80053'
: PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR.overrun && PROJECT_FINANCIAL_HEALTH_COLOR.overrun !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR.overrun : '#E80053',
backgroundColor: healthPerDivisionMode === 'schedule' ?
PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] && PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR['behind-schedule'] : '#E80053'
: PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR.overrun && PROJECT_FINANCIAL_HEALTH_COLOR.overrun !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR.overrun : '#E80053',
borderWidth: 2, borderWidth: 2,
borderRadius: 5, borderRadius: 5,
borderSkipped: false borderSkipped: false
}, },
{ {
label: healthPerDivisionMode === 'schedule' ? "Early Warning" : "Warning", label: healthPerDivisionMode === 'schedule' ? "Early Warning" : "Warning",
// data: [2, 0, 1, 1],
data: healthPerDivisionMode === 'schedule' ? data: healthPerDivisionMode === 'schedule' ?
PROJECT_SCHEDULE_HEALTH_PER_DIVISION ? PROJECT_SCHEDULE_HEALTH_PER_DIVISION.map((item, idx) => item.scheduleData.warning) : [] 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) : [] : PROJECT_BUDGET_HEALTH_PER_DIVISION ? PROJECT_BUDGET_HEALTH_PER_DIVISION.map((item, idx) => item.budgetData.warning) : []
, ,
borderColor: '#FFD600', borderColor: healthPerDivisionMode === 'schedule' ?
backgroundColor: '#FFD600', PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR.warning && PROJECT_SCHEDULE_HEALTH_COLOR.warning !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR.warning : '#FFD600'
: PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR.warning && PROJECT_FINANCIAL_HEALTH_COLOR.warning !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR.warning : '#FFD600',
backgroundColor: healthPerDivisionMode === 'schedule' ?
PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR.warning && PROJECT_SCHEDULE_HEALTH_COLOR.warning !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR.warning : '#FFD600'
: PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR.warning && PROJECT_FINANCIAL_HEALTH_COLOR.warning !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR.warning : '#FFD600',
borderWidth: 2, borderWidth: 2,
borderRadius: 5, borderRadius: 5,
borderSkipped: false borderSkipped: false
}, },
{ {
label: healthPerDivisionMode === 'schedule' ? "On Schedule" : "On Budget", label: healthPerDivisionMode === 'schedule' ? "On Schedule" : "On Budget",
// data: [4, 2, 3, 1],
data: healthPerDivisionMode === 'schedule' ? data: healthPerDivisionMode === 'schedule' ?
PROJECT_SCHEDULE_HEALTH_PER_DIVISION ? PROJECT_SCHEDULE_HEALTH_PER_DIVISION.map((item, idx) => item.scheduleData.onSchedule) : [] 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']) : [] : PROJECT_BUDGET_HEALTH_PER_DIVISION ? PROJECT_BUDGET_HEALTH_PER_DIVISION.map((item, idx) => item.budgetData['on-budget']) : []
, ,
borderColor: '#52AC0B', borderColor: healthPerDivisionMode === 'schedule' ?
backgroundColor: '#52AC0B', PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] && PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] : '#52AC0B'
: PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] && PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] : '#52AC0B',
backgroundColor: healthPerDivisionMode === 'schedule' ?
PROJECT_SCHEDULE_HEALTH_COLOR && PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] && PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] !== "" ? PROJECT_SCHEDULE_HEALTH_COLOR['on-schedule'] : '#52AC0B'
: PROJECT_FINANCIAL_HEALTH_COLOR && PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] && PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] !== "" ? PROJECT_FINANCIAL_HEALTH_COLOR['on-budget'] : '#52AC0B',
borderWidth: 2, borderWidth: 2,
borderRadius: 5, borderRadius: 5,
borderSkipped: false borderSkipped: false

12
src/views/Dashboard/DashboardProjectCarousell.js

@ -70,7 +70,15 @@ const center = {
}; };
const DashboardProject = (args) => { const DashboardProject = (args) => {
const token = localStorage.getItem("token"); let role_id = '', user_id = '', proyek_id = '', isLogin = '', token = '', company_id = 0, all_project = '', hierarchy = [];
role_id = localStorage.getItem("role_id");
proyek_id = localStorage.getItem("proyek_id");
user_id = localStorage.getItem("user_id");
token = localStorage.getItem("token");
isLogin = localStorage.getItem("isLogin");
company_id = localStorage.getItem('company_id');
all_project = localStorage.getItem('all_project');
hierarchy.push(JSON.parse(localStorage.getItem("hierarchy")));
const HEADER = { const HEADER = {
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@ -116,7 +124,7 @@ const DashboardProject = (args) => {
setIsReadyProjectDetail(false); setIsReadyProjectDetail(false);
setIsReadySCurve(false) setIsReadySCurve(false)
setIsReadySCurve(false); setIsReadySCurve(false);
const URL = `${BASE_OSPRO}/api/project-carausell`; const URL = `${BASE_OSPRO}/api/project-carausell/${company_id}/${all_project}/${hierarchy}`;
const result = await axios const result = await axios
.get(URL, HEADER) .get(URL, HEADER)
.then((res) => res) .then((res) => res)

113
src/views/Master/ProjectExpenditure/DialogForm.js

@ -0,0 +1,113 @@
import React, { useEffect, useState } from 'react'
import {
Modal, ModalHeader, ModalBody, ModalFooter,
Button, Form, FormGroup, Label, Input, Col, Row
} from 'reactstrap';
import 'antd/dist/antd.css';
import { Select } from "antd";
import InputColor from "./InputColor";
import "./styles.css";
import "rc-color-picker/assets/index.css";
import { useTranslation } from 'react-i18next';
const company_id = localStorage.getItem("company_id")
const { Option } = Select;
const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit }) => {
const [id, setId] = useState(0)
const [projectExpenditure, setProjectExpenditure] = useState('')
const [color, setColor] = useState('')
const { t } = useTranslation();
useEffect(() => {
if (typeDialog === "Edit") {
setId(dataEdit.id)
setProjectExpenditure(dataEdit.name)
setColor(dataEdit.color)
} else {
setId(0)
setColor('')
setProjectExpenditure('')
}
}, [dataEdit, openDialog])
const handleSave = () => {
let data = '';
if (typeDialog === "Save") {
data = {
name: projectExpenditure,
color,
company_id
}
closeDialog('save', data);
} else {
data = {
id,
name: projectExpenditure,
color,
company_id
}
closeDialog('edit', data);
}
setId(0)
}
const handleCancel = () => {
closeDialog('cancel', 'none')
setId(0)
}
const renderForm = () => {
return (
<Form>
<Row>
<Col md={6}>
<FormGroup>
<Label className="capitalize">{t('Project Expenditure')}</Label>
<Select
value={projectExpenditure}
defaultValue={projectExpenditure}
onChange={setProjectExpenditure}
style={{ width: "100%", pointerEvents: typeDialog === 'Edit' ? 'none' : '' }}
suffixIcon={typeDialog === 'Edit' ? null : <i class="fa fa-chevron-down" aria-hidden="true"></i> }
>
<Option key="Total Budget" value="Total Budget">
Total Budget
</Option>
<Option key="Expenditure" value="Expenditure">
Expenditure
</Option>
<Option key="Invoice" value="Invoice">
Invoice
</Option>
<Option key="Cash In" value="Cash In">
Cash In
</Option>
</Select>
</FormGroup>
</Col>
<Col md={6}>
<FormGroup>
<Label className="capitalize">{t('color')}</Label>
<InputColor value={color} color={color} onChange={(e) => setColor(e.color)} />
</FormGroup>
</Col>
</Row>
</Form>
)
}
return (
<>
<Modal size="lg" isOpen={openDialog} toggle={toggleDialog}>
<ModalHeader className="capitalize" toggle={closeDialog}>{typeDialog == "Save" ? `Add` : "Edit"} data</ModalHeader>
<ModalBody>
{renderForm()}
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={() => handleSave()}>{typeDialog}</Button>{' '}
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>{t('cancel')}</Button>
</ModalFooter>
</Modal>
</>
)
}
export default DialogForm;

89
src/views/Master/ProjectExpenditure/DialogFormInitial.js

@ -0,0 +1,89 @@
import React, { useEffect, useState } from 'react'
import {
Modal, ModalHeader, ModalBody, ModalFooter,
Button, Form, FormGroup, Label, Input, Col, Row
} from 'reactstrap';
const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit, idActivity, projectTypeId }) => {
const [id, setId] = useState(0)
const [activity, setActivity] = useState('')
useEffect(() => {
if (typeDialog === "edit") {
setId(dataEdit.id)
setActivity(dataEdit.name_activity)
} else {
setId(0)
setActivity('')
}
}, [dataEdit, openDialog])
const handleSave = () => {
let data = '';
if (typeDialog === "add") {
data = {
name_activity: activity,
proyek_type_id: projectTypeId
}
if (idActivity && idActivity > 0) {
data['parent_id'] = idActivity
}
closeDialog('save', data);
} else {
data = {
id,
name_activity: activity,
proyek_type_id: projectTypeId
}
if (idActivity && idActivity > 0) {
data['parent_id'] = idActivity
}
closeDialog('edit', data);
}
setId(0)
setActivity('')
}
const handleCancel = () => {
closeDialog('cancel', 'none')
setId(0)
setActivity('')
}
const renderForm = () => {
return (
<Form>
<FormGroup>
<Label className="capitalize">Activity</Label>
<Input row="4" type="textarea" value={activity} onChange={(e) => setActivity(e.target.value)} placeholder={`Persiapan ...`} />
</FormGroup>
</Form>
)
}
return (
<>
<Modal isOpen={openDialog} toggle={toggleDialog}>
<ModalHeader className="capitalize" toggle={closeDialog}>{typeDialog == "add" ? `Add` : "Edit"} Activity</ModalHeader>
<ModalBody>
{renderForm()}
</ModalBody>
<ModalFooter>
<Button className='capitalize' color="primary" onClick={() => handleSave()}>{typeDialog}</Button>{' '}
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>Batal</Button>
</ModalFooter>
</Modal>
</>
)
}
export default DialogForm;

46
src/views/Master/ProjectExpenditure/InputColor.js

@ -0,0 +1,46 @@
import React from "react";
import Input from "antd/lib/input";
import Button from "antd/lib/button";
import Dropdown from "antd/lib/dropdown";
import { Panel } from 'rc-color-picker'
import "antd/dist/antd.css";
import "./styles.css";
export default function InputColor(props) {
const { color, onChange } = props;
const [internalColor, setInternalColor] = React.useState(color);
const handleChange = (color) => {
setInternalColor(color.color);
if (onChange) {
onChange(color);
}
};
const overlay = (
<div>
<Panel
color={internalColor}
enableAlpha={false}
onChange={handleChange}
/>
</div>
);
return (
<>
<Input
value={internalColor || ""}
onChange={(e) => setInternalColor(e.target.value)}
suffix={
<Dropdown trigger={["click"]} overlay={overlay}>
<Button style={{ background: internalColor }}> </Button>
</Dropdown>
}
/>
</>
);
}

392
src/views/Master/ProjectExpenditure/index.js

@ -0,0 +1,392 @@
import * as XLSX from 'xlsx';
import DialogForm from './DialogForm';
import React, { useState, useEffect, useMemo } from 'react';
import SweetAlert from 'react-bootstrap-sweetalert';
import axios from "../../../const/interceptorApi"
import moment from 'moment'
import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap';
import { NotificationContainer, NotificationManager } from 'react-notifications';
import { PROJECT_EXPENDITURE_ADD, PROJECT_EXPENDITURE_EDIT, PROJECT_EXPENDITURE_DELETE, PROJECT_EXPENDITURE_SEARCH } from '../../../const/ApiConst';
import { Pagination, Button, Tooltip, Table } from 'antd';
import { useTranslation } from 'react-i18next';
const ProjectExpenditure = ({ params, ...props }) => {
let role_id = '', user_id = '', proyek_id = '', isLogin = '', token = '', company_id = 0, role_name = '';
if (props.location.state && props.location.state.role_id && props.location.state.user_id) {
role_id = props.location.state.role_id;
user_id = props.location.state.user_id;
token = props.location.state.token;
isLogin = props.location.state.isLogin;
role_name = props.location.state.role_name;
} else {
role_id = localStorage.getItem("role_id");
proyek_id = localStorage.getItem("proyek_id");
user_id = localStorage.getItem("user_id");
token = localStorage.getItem("token");
isLogin = localStorage.getItem("isLogin");
company_id = localStorage.getItem('company_id');
role_name = localStorage.getItem('role_name');
}
const HEADER = {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
}
}
const pageName = params.name;
const [alertDelete, setAlertDelete] = useState(false)
const [clickOpenModal, setClickOpenModal] = useState(false)
const [currentPage, setCurrentPage] = useState(1)
const [dataEdit, setDataEdit] = useState([])
const [dataExport, setDataExport] = useState([])
const [dataTable, setDatatable] = useState([])
const [idDelete, setIdDelete] = useState(0)
const [idPhaseProject, setIdPhaseProject] = useState(0)
const [openDialog, setOpenDialog] = useState(false)
const [openDialogIG, setOpenDialogIG] = useState(false)
const [rowsPerPage, setRowsPerPage] = useState(10)
const [search, setSearch] = useState('')
const [totalPage, setTotalPage] = useState(0)
const [typeDialog, setTypeDialog] = useState('Save')
const { t } = useTranslation()
useEffect(() => {
getDataProjectExpenditure()
}, [currentPage, rowsPerPage, search])
useEffect(() => {
const cekData = dataExport || []
if (cekData.length > 0) {
exportExcel()
}
}, [dataExport])
const getDataProjectExpenditure = async () => {
let start = 0;
if (currentPage !== 1 && currentPage > 1) {
start = (currentPage * rowsPerPage) - rowsPerPage
}
const payload = {
"columns": [
{
"name": "name",
"logic_operator": "ilike",
"value": search,
"operator": "AND"
}
],
"orders": {
"ascending": true,
"columns": [
'id'
]
},
"paging": {
"length": rowsPerPage,
"start": start
}
}
if (role_name !== "Super Admin") {
payload.columns.push(
{ "name": "company_id", "logic_operator": "=", "value": company_id, "operator": "AND" },
)
} else {
payload.columns.push(
{ "name": "company_id", "logic_operator": "is null", "value": "", "operator": "AND" },
)
}
const result = await axios
.post(PROJECT_EXPENDITURE_SEARCH, payload, HEADER)
.then((res) => res)
.catch((err) => err.response);
if (result && result.data && result.data.code == 200) {
result.data.data.map((res) => {
res.key = res.id.toString()
});
setDatatable(result.data.data);
setTotalPage(result.data.totalRecord);
} else {
NotificationManager.error('Gagal Mengambil Data!!', 'Failed');
}
}
const handleSearch = e => {
const value = e.target.value
setSearch(value);
setCurrentPage(1)
};
const handleOpenDialog = (type) => {
setOpenDialog(true)
setTypeDialog(type)
}
const handleExportExcel = async () => {
let start = 0;
if (currentPage !== 1 && currentPage > 1) {
start = (currentPage * rowsPerPage) - rowsPerPage
}
const payload = {
"columns": [
{
"name": "name",
"logic_operator": "like",
"value": search,
"operator": "AND"
}
],
"orders": {
"ascending": true,
"columns": [
'id'
]
},
"paging": {
"length": rowsPerPage,
"start": start
}
}
const result = await axios
.post(PROJECT_EXPENDITURE_SEARCH, payload, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code == 200) {
let resData = result.data.data;
const excelData = [];
resData.map((val, index) => {
let dataRow = {
"Nama": val.name,
"Color": val.color,
}
excelData.push(dataRow)
})
await setDataExport(excelData)
} else {
NotificationManager.error('Gagal Export Data!!', 'Failed');
}
}
const exportExcel = () => {
const dataExcel = dataExport || [];
const fileName = `Data ${pageName}.xlsx`;
const ws = XLSX.utils.json_to_sheet(dataExcel);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, `Data ${pageName}`);
XLSX.writeFile(wb, fileName);
setDataExport([])
}
const handleEdit = (data) => {
setDataEdit(data)
handleOpenDialog('Edit');
}
const handleDelete = async (id) => {
await setAlertDelete(true)
await setIdDelete(id)
}
const handleCloseDialog = (type, data) => {
if (type === "save") {
saveProjectExpenditure(data);
} else if (type === "edit") {
editMaterialR(data);
}
setDataEdit([])
setOpenDialog(false)
}
const saveProjectExpenditure = async (data) => {
const result = await axios.post(PROJECT_EXPENDITURE_ADD, data, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectExpenditure()
NotificationManager.success(`Data project expenditure berhasil ditambah`, 'Success!!');
} else {
NotificationManager.error(`${result.data.message}`, 'Failed!!');
}
}
const editMaterialR = async (data) => {
let urlEdit = PROJECT_EXPENDITURE_EDIT(data.id)
const result = await axios.put(urlEdit, data, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectExpenditure();
NotificationManager.success(`Data project expenditure berhasil diedit`, 'Success!!');
} else {
NotificationManager.error(`Data project expenditure gagal di edit`, `Failed!!`);
}
}
const toggleAddDialog = () => {
setOpenDialog(!openDialog)
}
const handleDialogIg = (id) => {
setIdPhaseProject(id)
setOpenDialogIG(true)
}
const closeDialogIG = () => {
setIdPhaseProject(0)
setOpenDialogIG(false)
}
const toggleDialogIG = () => {
if (openDialogIG) {
setIdPhaseProject(0)
}
setOpenDialogIG(!openDialogIG);
}
const onConfirmDelete = async () => {
let url = PROJECT_EXPENDITURE_DELETE(idDelete);
const result = await axios.delete(url, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectExpenditure()
setIdDelete(0)
setAlertDelete(false)
NotificationManager.success(`Data project expenditure berhasil dihapus!`, 'Success!!');
} else {
setIdDelete(0)
setAlertDelete(false)
NotificationManager.error(`Data project expenditure gagal dihapus!}`, 'Failed!!');
}
}
const cancelDelete = () => {
setAlertDelete(false)
setIdDelete(0)
}
const onShowSizeChange = (current, pageSize) => {
setRowsPerPage(pageSize)
}
const onPagination = (current, pageSize) => {
setCurrentPage(current)
}
const dataNotAvailable = () => {
if (dataTable.length === 0) {
return (
<tr>
<td align="center" colSpan="3">{t('noData')}</td>
</tr>
)
}
}
const renderTable = useMemo(() => {
const columns = [
{
title: t('action'),
dataIndex: '',
key: 'x',
className: 'nowrap',
render: (text, record) => <>
<Tooltip title={t('delete')}>
<i className="fa fa-trash" style={{ color: 'red', marginRight: '10px', cursor: "pointer" }} onClick={() => handleDelete(text.id)}></i>
</Tooltip>
<Tooltip title={t('Edit')}>
<i className="fa fa-edit" style={{ color: 'green', cursor: "pointer" }} onClick={() => handleEdit(text)}></i>
</Tooltip>{" "}
</>,
},
{ title: t('Project Expenditure'), dataIndex: 'name', key: 'name', className: "nowrap" },
{
title: t('color'),
dataIndex: 'color',
key: 'color',
render: (text) => <>
<Tooltip title={text}>
<i className="fa fa-square" style={{ color: text }} ></i>
</Tooltip>
</>,
},
];
return (
<Table
rowKey="id"
size="small"
columns={columns}
dataSource={dataTable}
pagination={false}
/>
)
}, [dataTable])
return (
<div>
<NotificationContainer />
<SweetAlert
show={alertDelete}
warning
showCancel
confirmBtnText="Delete"
confirmBtnBsStyle="danger"
title={t('deleteConfirm')}
onConfirm={onConfirmDelete}
onCancel={cancelDelete}
focusCancelBtn
>
{t('deleteMsg')}
</SweetAlert>
<DialogForm
openDialog={openDialog}
closeDialog={handleCloseDialog}
toggleDialog={() => toggleAddDialog}
typeDialog={typeDialog}
dataEdit={dataEdit}
clickOpenModal={clickOpenModal}
/>
<Card>
<CardHeader style={{ display: "flex", justifyContent: "space-between" }}>
<h4 className="capitalize">{pageName}</h4>
<Row>
<Col>
<Input onChange={handleSearch} value={search} type="text" name="search" id="search" placeholder={t('search')} />
</Col>
<Col>
<Tooltip title={t('Add Project Expenditure')}>
<Button style={{ background: "#4caf50", color: "#fff" }} onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button>
</Tooltip>
<Tooltip title={t('exportExcel')}>
<Button style={{ marginLeft: "5px" }} onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button>
</Tooltip>
</Col>
</Row>
</CardHeader>
<CardBody>
{renderTable}
<Pagination
style={{ marginTop: "25px" }}
showSizeChanger
onShowSizeChange={onShowSizeChange}
onChange={onPagination}
defaultCurrent={currentPage}
pageSize={rowsPerPage}
total={totalPage}
pageSizeOptions={["10", "15", "20", "25", "30", "35", "40"]}
/>
</CardBody>
</Card>
</div>
)
}
export default ProjectExpenditure;

41
src/views/Master/ProjectExpenditure/styles.css

@ -0,0 +1,41 @@
.App {
font-family: sans-serif;
padding: 20px;
}
h2 {
margin-top: 40px;
}
.rc-color-picker-panel {
border: 1px solid #ccc;
}
.rc-color-picker-panel-inner {
border: none;
box-shadow: none;
}
.rc-color-picker-panel-board-hsv {
border-radius: 12px;
outline: none;
}
.rc-color-picker-panel-board-value {
border: none;
border-radius: 12px;
}
.rc-color-picker-panel-board-saturation {
border: none;
border-radius: 12px;
}
.rc-color-picker-panel-ribbon {
border-radius: 12px;
}
.rc-color-picker-panel-wrap-preview {
border-radius: 12px;
}
.rc-color-picker-panel-preview span {
border-radius: 12px;
}
.rc-color-picker-panel-preview input {
border-radius: 12px;
}

110
src/views/Master/ProjectFinancialHealth/DialogForm.js

@ -0,0 +1,110 @@
import React, { useEffect, useState } from 'react'
import {
Modal, ModalHeader, ModalBody, ModalFooter,
Button, Form, FormGroup, Label, Input, Col, Row
} from 'reactstrap';
import 'antd/dist/antd.css';
import { Select } from "antd";
import InputColor from "./InputColor";
import "./styles.css";
import "rc-color-picker/assets/index.css";
import { useTranslation } from 'react-i18next';
const company_id = localStorage.getItem("company_id")
const { Option } = Select;
const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit }) => {
const [id, setId] = useState(0)
const [projectFinancialHealth, setProjectFinancialHealth] = useState('')
const [color, setColor] = useState('')
const { t } = useTranslation();
useEffect(() => {
if (typeDialog === "Edit") {
setId(dataEdit.id)
setProjectFinancialHealth(dataEdit.name)
setColor(dataEdit.color)
} else {
setId(0)
setColor('')
setProjectFinancialHealth('')
}
}, [dataEdit, openDialog])
const handleSave = () => {
let data = '';
if (typeDialog === "Save") {
data = {
name: projectFinancialHealth,
color,
company_id
}
closeDialog('save', data);
} else {
data = {
id,
name: projectFinancialHealth,
color,
company_id
}
closeDialog('edit', data);
}
setId(0)
}
const handleCancel = () => {
closeDialog('cancel', 'none')
setId(0)
}
const renderForm = () => {
return (
<Form>
<Row>
<Col md={6}>
<FormGroup>
<Label className="capitalize">{t('Project Financial Health')}</Label>
<Select
value={projectFinancialHealth}
defaultValue={projectFinancialHealth}
onChange={setProjectFinancialHealth}
style={{ width: "100%", pointerEvents: typeDialog === 'Edit' ? 'none' : '' }}
suffixIcon={typeDialog === 'Edit' ? null : <i class="fa fa-chevron-down" aria-hidden="true"></i> }
>
<Option key="Cost Everrun" value="Cost Everrun">
Cost Overrun
</Option>
<Option key="Early Warning" value="Early Warning">
Early Warning
</Option>
<Option key="On Budget" value="On Budget">
On Budget
</Option>
</Select>
</FormGroup>
</Col>
<Col md={6}>
<FormGroup>
<Label className="capitalize">{t('color')}</Label>
<InputColor value={color} color={color} onChange={(e) => setColor(e.color)} />
</FormGroup>
</Col>
</Row>
</Form>
)
}
return (
<>
<Modal size="lg" isOpen={openDialog} toggle={toggleDialog}>
<ModalHeader className="capitalize" toggle={closeDialog}>{typeDialog == "Save" ? `Add` : "Edit"} data</ModalHeader>
<ModalBody>
{renderForm()}
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={() => handleSave()}>{typeDialog}</Button>{' '}
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>{t('cancel')}</Button>
</ModalFooter>
</Modal>
</>
)
}
export default DialogForm;

89
src/views/Master/ProjectFinancialHealth/DialogFormInitial.js

@ -0,0 +1,89 @@
import React, { useEffect, useState } from 'react'
import {
Modal, ModalHeader, ModalBody, ModalFooter,
Button, Form, FormGroup, Label, Input, Col, Row
} from 'reactstrap';
const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit, idActivity, projectTypeId }) => {
const [id, setId] = useState(0)
const [activity, setActivity] = useState('')
useEffect(() => {
if (typeDialog === "edit") {
setId(dataEdit.id)
setActivity(dataEdit.name_activity)
} else {
setId(0)
setActivity('')
}
}, [dataEdit, openDialog])
const handleSave = () => {
let data = '';
if (typeDialog === "add") {
data = {
name_activity: activity,
proyek_type_id: projectTypeId
}
if (idActivity && idActivity > 0) {
data['parent_id'] = idActivity
}
closeDialog('save', data);
} else {
data = {
id,
name_activity: activity,
proyek_type_id: projectTypeId
}
if (idActivity && idActivity > 0) {
data['parent_id'] = idActivity
}
closeDialog('edit', data);
}
setId(0)
setActivity('')
}
const handleCancel = () => {
closeDialog('cancel', 'none')
setId(0)
setActivity('')
}
const renderForm = () => {
return (
<Form>
<FormGroup>
<Label className="capitalize">Activity</Label>
<Input row="4" type="textarea" value={activity} onChange={(e) => setActivity(e.target.value)} placeholder={`Persiapan ...`} />
</FormGroup>
</Form>
)
}
return (
<>
<Modal isOpen={openDialog} toggle={toggleDialog}>
<ModalHeader className="capitalize" toggle={closeDialog}>{typeDialog == "add" ? `Add` : "Edit"} Activity</ModalHeader>
<ModalBody>
{renderForm()}
</ModalBody>
<ModalFooter>
<Button className='capitalize' color="primary" onClick={() => handleSave()}>{typeDialog}</Button>{' '}
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>Batal</Button>
</ModalFooter>
</Modal>
</>
)
}
export default DialogForm;

46
src/views/Master/ProjectFinancialHealth/InputColor.js

@ -0,0 +1,46 @@
import React from "react";
import Input from "antd/lib/input";
import Button from "antd/lib/button";
import Dropdown from "antd/lib/dropdown";
import { Panel } from 'rc-color-picker'
import "antd/dist/antd.css";
import "./styles.css";
export default function InputColor(props) {
const { color, onChange } = props;
const [internalColor, setInternalColor] = React.useState(color);
const handleChange = (color) => {
setInternalColor(color.color);
if (onChange) {
onChange(color);
}
};
const overlay = (
<div>
<Panel
color={internalColor}
enableAlpha={false}
onChange={handleChange}
/>
</div>
);
return (
<>
<Input
value={internalColor || ""}
onChange={(e) => setInternalColor(e.target.value)}
suffix={
<Dropdown trigger={["click"]} overlay={overlay}>
<Button style={{ background: internalColor }}> </Button>
</Dropdown>
}
/>
</>
);
}

392
src/views/Master/ProjectFinancialHealth/index.js

@ -0,0 +1,392 @@
import * as XLSX from 'xlsx';
import DialogForm from './DialogForm';
import React, { useState, useEffect, useMemo } from 'react';
import SweetAlert from 'react-bootstrap-sweetalert';
import axios from "../../../const/interceptorApi"
import moment from 'moment'
import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap';
import { NotificationContainer, NotificationManager } from 'react-notifications';
import { PROJECT_FINANCIAL_HEALTH_ADD, PROJECT_FINANCIAL_HEALTH_EDIT, PROJECT_FINANCIAL_HEALTH_DELETE, PROJECT_FINANCIAL_HEALTH_SEARCH } from '../../../const/ApiConst';
import { Pagination, Button, Tooltip, Table } from 'antd';
import { useTranslation } from 'react-i18next';
const ProjectFinancialHealth = ({ params, ...props }) => {
let role_id = '', user_id = '', proyek_id = '', isLogin = '', token = '', company_id = 0, role_name = '';
if (props.location.state && props.location.state.role_id && props.location.state.user_id) {
role_id = props.location.state.role_id;
user_id = props.location.state.user_id;
token = props.location.state.token;
isLogin = props.location.state.isLogin;
role_name = props.location.state.role_name;
} else {
role_id = localStorage.getItem("role_id");
proyek_id = localStorage.getItem("proyek_id");
user_id = localStorage.getItem("user_id");
token = localStorage.getItem("token");
isLogin = localStorage.getItem("isLogin");
company_id = localStorage.getItem('company_id');
role_name = localStorage.getItem('role_name');
}
const HEADER = {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
}
}
const pageName = params.name;
const [alertDelete, setAlertDelete] = useState(false)
const [clickOpenModal, setClickOpenModal] = useState(false)
const [currentPage, setCurrentPage] = useState(1)
const [dataEdit, setDataEdit] = useState([])
const [dataExport, setDataExport] = useState([])
const [dataTable, setDatatable] = useState([])
const [idDelete, setIdDelete] = useState(0)
const [idPhaseProject, setIdPhaseProject] = useState(0)
const [openDialog, setOpenDialog] = useState(false)
const [openDialogIG, setOpenDialogIG] = useState(false)
const [rowsPerPage, setRowsPerPage] = useState(10)
const [search, setSearch] = useState('')
const [totalPage, setTotalPage] = useState(0)
const [typeDialog, setTypeDialog] = useState('Save')
const { t } = useTranslation()
useEffect(() => {
getDataProjectFinancialHealth()
}, [currentPage, rowsPerPage, search])
useEffect(() => {
const cekData = dataExport || []
if (cekData.length > 0) {
exportExcel()
}
}, [dataExport])
const getDataProjectFinancialHealth = async () => {
let start = 0;
if (currentPage !== 1 && currentPage > 1) {
start = (currentPage * rowsPerPage) - rowsPerPage
}
const payload = {
"columns": [
{
"name": "name",
"logic_operator": "ilike",
"value": search,
"operator": "AND"
}
],
"orders": {
"ascending": true,
"columns": [
'id'
]
},
"paging": {
"length": rowsPerPage,
"start": start
}
}
if (role_name !== "Super Admin") {
payload.columns.push(
{ "name": "company_id", "logic_operator": "=", "value": company_id, "operator": "AND" },
)
} else {
payload.columns.push(
{ "name": "company_id", "logic_operator": "is null", "value": "", "operator": "AND" },
)
}
const result = await axios
.post(PROJECT_FINANCIAL_HEALTH_SEARCH, payload, HEADER)
.then((res) => res)
.catch((err) => err.response);
if (result && result.data && result.data.code == 200) {
result.data.data.map((res) => {
res.key = res.id.toString()
});
setDatatable(result.data.data);
setTotalPage(result.data.totalRecord);
} else {
NotificationManager.error('Gagal Mengambil Data!!', 'Failed');
}
}
const handleSearch = e => {
const value = e.target.value
setSearch(value);
setCurrentPage(1)
};
const handleOpenDialog = (type) => {
setOpenDialog(true)
setTypeDialog(type)
}
const handleExportExcel = async () => {
let start = 0;
if (currentPage !== 1 && currentPage > 1) {
start = (currentPage * rowsPerPage) - rowsPerPage
}
const payload = {
"columns": [
{
"name": "name",
"logic_operator": "like",
"value": search,
"operator": "AND"
}
],
"orders": {
"ascending": true,
"columns": [
'id'
]
},
"paging": {
"length": rowsPerPage,
"start": start
}
}
const result = await axios
.post(PROJECT_FINANCIAL_HEALTH_SEARCH, payload, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code == 200) {
let resData = result.data.data;
const excelData = [];
resData.map((val, index) => {
let dataRow = {
"Nama": val.name,
"Color": val.color,
}
excelData.push(dataRow)
})
await setDataExport(excelData)
} else {
NotificationManager.error('Gagal Export Data!!', 'Failed');
}
}
const exportExcel = () => {
const dataExcel = dataExport || [];
const fileName = `Data ${pageName}.xlsx`;
const ws = XLSX.utils.json_to_sheet(dataExcel);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, `Data ${pageName}`);
XLSX.writeFile(wb, fileName);
setDataExport([])
}
const handleEdit = (data) => {
setDataEdit(data)
handleOpenDialog('Edit');
}
const handleDelete = async (id) => {
await setAlertDelete(true)
await setIdDelete(id)
}
const handleCloseDialog = (type, data) => {
if (type === "save") {
saveProjectFinancialHealth(data);
} else if (type === "edit") {
editMaterialR(data);
}
setDataEdit([])
setOpenDialog(false)
}
const saveProjectFinancialHealth = async (data) => {
const result = await axios.post(PROJECT_FINANCIAL_HEALTH_ADD, data, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectFinancialHealth()
NotificationManager.success(`Data project financial health berhasil ditambah`, 'Success!!');
} else {
NotificationManager.error(`${result.data.message}`, 'Failed!!');
}
}
const editMaterialR = async (data) => {
let urlEdit = PROJECT_FINANCIAL_HEALTH_EDIT(data.id)
const result = await axios.put(urlEdit, data, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectFinancialHealth();
NotificationManager.success(`Data project financial health berhasil diedit`, 'Success!!');
} else {
NotificationManager.error(`Data project financial health gagal di edit`, `Failed!!`);
}
}
const toggleAddDialog = () => {
setOpenDialog(!openDialog)
}
const handleDialogIg = (id) => {
setIdPhaseProject(id)
setOpenDialogIG(true)
}
const closeDialogIG = () => {
setIdPhaseProject(0)
setOpenDialogIG(false)
}
const toggleDialogIG = () => {
if (openDialogIG) {
setIdPhaseProject(0)
}
setOpenDialogIG(!openDialogIG);
}
const onConfirmDelete = async () => {
let url = PROJECT_FINANCIAL_HEALTH_DELETE(idDelete);
const result = await axios.delete(url, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectFinancialHealth()
setIdDelete(0)
setAlertDelete(false)
NotificationManager.success(`Data project financial health berhasil dihapus!`, 'Success!!');
} else {
setIdDelete(0)
setAlertDelete(false)
NotificationManager.error(`Data project financial health gagal dihapus!}`, 'Failed!!');
}
}
const cancelDelete = () => {
setAlertDelete(false)
setIdDelete(0)
}
const onShowSizeChange = (current, pageSize) => {
setRowsPerPage(pageSize)
}
const onPagination = (current, pageSize) => {
setCurrentPage(current)
}
const dataNotAvailable = () => {
if (dataTable.length === 0) {
return (
<tr>
<td align="center" colSpan="3">{t('noData')}</td>
</tr>
)
}
}
const renderTable = useMemo(() => {
const columns = [
{
title: t('action'),
dataIndex: '',
key: 'x',
className: 'nowrap',
render: (text, record) => <>
<Tooltip title={t('delete')}>
<i className="fa fa-trash" style={{ color: 'red', marginRight: '10px', cursor: "pointer" }} onClick={() => handleDelete(text.id)}></i>
</Tooltip>
<Tooltip title={t('Edit')}>
<i className="fa fa-edit" style={{ color: 'green', cursor: "pointer" }} onClick={() => handleEdit(text)}></i>
</Tooltip>{" "}
</>,
},
{ title: t('Project Financial Health'), dataIndex: 'name', key: 'name', className: "nowrap" },
{
title: t('color'),
dataIndex: 'color',
key: 'color',
render: (text) => <>
<Tooltip title={text}>
<i className="fa fa-square" style={{ color: text }} ></i>
</Tooltip>
</>,
},
];
return (
<Table
rowKey="id"
size="small"
columns={columns}
dataSource={dataTable}
pagination={false}
/>
)
}, [dataTable])
return (
<div>
<NotificationContainer />
<SweetAlert
show={alertDelete}
warning
showCancel
confirmBtnText="Delete"
confirmBtnBsStyle="danger"
title={t('deleteConfirm')}
onConfirm={onConfirmDelete}
onCancel={cancelDelete}
focusCancelBtn
>
{t('deleteMsg')}
</SweetAlert>
<DialogForm
openDialog={openDialog}
closeDialog={handleCloseDialog}
toggleDialog={() => toggleAddDialog}
typeDialog={typeDialog}
dataEdit={dataEdit}
clickOpenModal={clickOpenModal}
/>
<Card>
<CardHeader style={{ display: "flex", justifyContent: "space-between" }}>
<h4 className="capitalize">{pageName}</h4>
<Row>
<Col>
<Input onChange={handleSearch} value={search} type="text" name="search" id="search" placeholder={t('search')} />
</Col>
<Col>
<Tooltip title={t('Add Project Financial Health')}>
<Button style={{ background: "#4caf50", color: "#fff" }} onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button>
</Tooltip>
<Tooltip title={t('exportExcel')}>
<Button style={{ marginLeft: "5px" }} onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button>
</Tooltip>
</Col>
</Row>
</CardHeader>
<CardBody>
{renderTable}
<Pagination
style={{ marginTop: "25px" }}
showSizeChanger
onShowSizeChange={onShowSizeChange}
onChange={onPagination}
defaultCurrent={currentPage}
pageSize={rowsPerPage}
total={totalPage}
pageSizeOptions={["10", "15", "20", "25", "30", "35", "40"]}
/>
</CardBody>
</Card>
</div>
)
}
export default ProjectFinancialHealth;

41
src/views/Master/ProjectFinancialHealth/styles.css

@ -0,0 +1,41 @@
.App {
font-family: sans-serif;
padding: 20px;
}
h2 {
margin-top: 40px;
}
.rc-color-picker-panel {
border: 1px solid #ccc;
}
.rc-color-picker-panel-inner {
border: none;
box-shadow: none;
}
.rc-color-picker-panel-board-hsv {
border-radius: 12px;
outline: none;
}
.rc-color-picker-panel-board-value {
border: none;
border-radius: 12px;
}
.rc-color-picker-panel-board-saturation {
border: none;
border-radius: 12px;
}
.rc-color-picker-panel-ribbon {
border-radius: 12px;
}
.rc-color-picker-panel-wrap-preview {
border-radius: 12px;
}
.rc-color-picker-panel-preview span {
border-radius: 12px;
}
.rc-color-picker-panel-preview input {
border-radius: 12px;
}

107
src/views/Master/ProjectInvoice/DialogForm.js

@ -0,0 +1,107 @@
import React, { useEffect, useState } from 'react'
import {
Modal, ModalHeader, ModalBody, ModalFooter,
Button, Form, FormGroup, Label, Input, Col, Row
} from 'reactstrap';
import 'antd/dist/antd.css';
import { Select } from "antd";
import InputColor from "./InputColor";
import "./styles.css";
import "rc-color-picker/assets/index.css";
import { useTranslation } from 'react-i18next';
const company_id = localStorage.getItem("company_id")
const { Option } = Select;
const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit }) => {
const [id, setId] = useState(0)
const [projectInvoice, setProjectInvoice] = useState('')
const [color, setColor] = useState('')
const { t } = useTranslation();
useEffect(() => {
if (typeDialog === "Edit") {
setId(dataEdit.id)
setProjectInvoice(dataEdit.name)
setColor(dataEdit.color)
} else {
setId(0)
setColor('')
setProjectInvoice('')
}
}, [dataEdit, openDialog])
const handleSave = () => {
let data = '';
if (typeDialog === "Save") {
data = {
name: projectInvoice,
color,
company_id
}
closeDialog('save', data);
} else {
data = {
id,
name: projectInvoice,
color,
company_id
}
closeDialog('edit', data);
}
setId(0)
}
const handleCancel = () => {
closeDialog('cancel', 'none')
setId(0)
}
const renderForm = () => {
return (
<Form>
<Row>
<Col md={6}>
<FormGroup>
<Label className="capitalize">{t('Project Invoice')}</Label>
<Select
value={projectInvoice}
defaultValue={projectInvoice}
onChange={setProjectInvoice}
style={{ width: "100%", pointerEvents: typeDialog === 'Edit' ? 'none' : '' }}
suffixIcon={typeDialog === 'Edit' ? null : <i class="fa fa-chevron-down" aria-hidden="true"></i> }
>
<Option key="Invoiced" value="Invoiced">
Invoiced
</Option>
<Option key="Cash In" value="Cash In">
Cash In
</Option>
</Select>
</FormGroup>
</Col>
<Col md={6}>
<FormGroup>
<Label className="capitalize">{t('color')}</Label>
<InputColor value={color} color={color} onChange={(e) => setColor(e.color)} />
</FormGroup>
</Col>
</Row>
</Form>
)
}
return (
<>
<Modal size="lg" isOpen={openDialog} toggle={toggleDialog}>
<ModalHeader className="capitalize" toggle={closeDialog}>{typeDialog == "Save" ? `Add` : "Edit"} data</ModalHeader>
<ModalBody>
{renderForm()}
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={() => handleSave()}>{typeDialog}</Button>{' '}
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>{t('cancel')}</Button>
</ModalFooter>
</Modal>
</>
)
}
export default DialogForm;

89
src/views/Master/ProjectInvoice/DialogFormInitial.js

@ -0,0 +1,89 @@
import React, { useEffect, useState } from 'react'
import {
Modal, ModalHeader, ModalBody, ModalFooter,
Button, Form, FormGroup, Label, Input, Col, Row
} from 'reactstrap';
const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit, idActivity, projectTypeId }) => {
const [id, setId] = useState(0)
const [activity, setActivity] = useState('')
useEffect(() => {
if (typeDialog === "edit") {
setId(dataEdit.id)
setActivity(dataEdit.name_activity)
} else {
setId(0)
setActivity('')
}
}, [dataEdit, openDialog])
const handleSave = () => {
let data = '';
if (typeDialog === "add") {
data = {
name_activity: activity,
proyek_type_id: projectTypeId
}
if (idActivity && idActivity > 0) {
data['parent_id'] = idActivity
}
closeDialog('save', data);
} else {
data = {
id,
name_activity: activity,
proyek_type_id: projectTypeId
}
if (idActivity && idActivity > 0) {
data['parent_id'] = idActivity
}
closeDialog('edit', data);
}
setId(0)
setActivity('')
}
const handleCancel = () => {
closeDialog('cancel', 'none')
setId(0)
setActivity('')
}
const renderForm = () => {
return (
<Form>
<FormGroup>
<Label className="capitalize">Activity</Label>
<Input row="4" type="textarea" value={activity} onChange={(e) => setActivity(e.target.value)} placeholder={`Persiapan ...`} />
</FormGroup>
</Form>
)
}
return (
<>
<Modal isOpen={openDialog} toggle={toggleDialog}>
<ModalHeader className="capitalize" toggle={closeDialog}>{typeDialog == "add" ? `Add` : "Edit"} Activity</ModalHeader>
<ModalBody>
{renderForm()}
</ModalBody>
<ModalFooter>
<Button className='capitalize' color="primary" onClick={() => handleSave()}>{typeDialog}</Button>{' '}
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>Batal</Button>
</ModalFooter>
</Modal>
</>
)
}
export default DialogForm;

46
src/views/Master/ProjectInvoice/InputColor.js

@ -0,0 +1,46 @@
import React from "react";
import Input from "antd/lib/input";
import Button from "antd/lib/button";
import Dropdown from "antd/lib/dropdown";
import { Panel } from 'rc-color-picker'
import "antd/dist/antd.css";
import "./styles.css";
export default function InputColor(props) {
const { color, onChange } = props;
const [internalColor, setInternalColor] = React.useState(color);
const handleChange = (color) => {
setInternalColor(color.color);
if (onChange) {
onChange(color);
}
};
const overlay = (
<div>
<Panel
color={internalColor}
enableAlpha={false}
onChange={handleChange}
/>
</div>
);
return (
<>
<Input
value={internalColor || ""}
onChange={(e) => setInternalColor(e.target.value)}
suffix={
<Dropdown trigger={["click"]} overlay={overlay}>
<Button style={{ background: internalColor }}> </Button>
</Dropdown>
}
/>
</>
);
}

392
src/views/Master/ProjectInvoice/index.js

@ -0,0 +1,392 @@
import * as XLSX from 'xlsx';
import DialogForm from './DialogForm';
import React, { useState, useEffect, useMemo } from 'react';
import SweetAlert from 'react-bootstrap-sweetalert';
import axios from "../../../const/interceptorApi"
import moment from 'moment'
import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap';
import { NotificationContainer, NotificationManager } from 'react-notifications';
import { PROJECT_INVOICE_ADD, PROJECT_INVOICE_EDIT, PROJECT_INVOICE_DELETE, PROJECT_INVOICE_SEARCH } from '../../../const/ApiConst';
import { Pagination, Button, Tooltip, Table } from 'antd';
import { useTranslation } from 'react-i18next';
const ProjectInvoice = ({ params, ...props }) => {
let role_id = '', user_id = '', proyek_id = '', isLogin = '', token = '', company_id = 0, role_name = '';
if (props.location.state && props.location.state.role_id && props.location.state.user_id) {
role_id = props.location.state.role_id;
user_id = props.location.state.user_id;
token = props.location.state.token;
isLogin = props.location.state.isLogin;
role_name = props.location.state.role_name;
} else {
role_id = localStorage.getItem("role_id");
proyek_id = localStorage.getItem("proyek_id");
user_id = localStorage.getItem("user_id");
token = localStorage.getItem("token");
isLogin = localStorage.getItem("isLogin");
company_id = localStorage.getItem('company_id');
role_name = localStorage.getItem('role_name');
}
const HEADER = {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
}
}
const pageName = params.name;
const [alertDelete, setAlertDelete] = useState(false)
const [clickOpenModal, setClickOpenModal] = useState(false)
const [currentPage, setCurrentPage] = useState(1)
const [dataEdit, setDataEdit] = useState([])
const [dataExport, setDataExport] = useState([])
const [dataTable, setDatatable] = useState([])
const [idDelete, setIdDelete] = useState(0)
const [idPhaseProject, setIdPhaseProject] = useState(0)
const [openDialog, setOpenDialog] = useState(false)
const [openDialogIG, setOpenDialogIG] = useState(false)
const [rowsPerPage, setRowsPerPage] = useState(10)
const [search, setSearch] = useState('')
const [totalPage, setTotalPage] = useState(0)
const [typeDialog, setTypeDialog] = useState('Save')
const { t } = useTranslation()
useEffect(() => {
getDataProjectInvoice()
}, [currentPage, rowsPerPage, search])
useEffect(() => {
const cekData = dataExport || []
if (cekData.length > 0) {
exportExcel()
}
}, [dataExport])
const getDataProjectInvoice = async () => {
let start = 0;
if (currentPage !== 1 && currentPage > 1) {
start = (currentPage * rowsPerPage) - rowsPerPage
}
const payload = {
"columns": [
{
"name": "name",
"logic_operator": "ilike",
"value": search,
"operator": "AND"
}
],
"orders": {
"ascending": true,
"columns": [
'id'
]
},
"paging": {
"length": rowsPerPage,
"start": start
}
}
if (role_name !== "Super Admin") {
payload.columns.push(
{ "name": "company_id", "logic_operator": "=", "value": company_id, "operator": "AND" },
)
} else {
payload.columns.push(
{ "name": "company_id", "logic_operator": "is null", "value": "", "operator": "AND" },
)
}
const result = await axios
.post(PROJECT_INVOICE_SEARCH, payload, HEADER)
.then((res) => res)
.catch((err) => err.response);
if (result && result.data && result.data.code == 200) {
result.data.data.map((res) => {
res.key = res.id.toString()
});
setDatatable(result.data.data);
setTotalPage(result.data.totalRecord);
} else {
NotificationManager.error('Gagal Mengambil Data!!', 'Failed');
}
}
const handleSearch = e => {
const value = e.target.value
setSearch(value);
setCurrentPage(1)
};
const handleOpenDialog = (type) => {
setOpenDialog(true)
setTypeDialog(type)
}
const handleExportExcel = async () => {
let start = 0;
if (currentPage !== 1 && currentPage > 1) {
start = (currentPage * rowsPerPage) - rowsPerPage
}
const payload = {
"columns": [
{
"name": "name",
"logic_operator": "like",
"value": search,
"operator": "AND"
}
],
"orders": {
"ascending": true,
"columns": [
'id'
]
},
"paging": {
"length": rowsPerPage,
"start": start
}
}
const result = await axios
.post(PROJECT_INVOICE_SEARCH, payload, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code == 200) {
let resData = result.data.data;
const excelData = [];
resData.map((val, index) => {
let dataRow = {
"Nama": val.name,
"Color": val.color,
}
excelData.push(dataRow)
})
await setDataExport(excelData)
} else {
NotificationManager.error('Gagal Export Data!!', 'Failed');
}
}
const exportExcel = () => {
const dataExcel = dataExport || [];
const fileName = `Data ${pageName}.xlsx`;
const ws = XLSX.utils.json_to_sheet(dataExcel);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, `Data ${pageName}`);
XLSX.writeFile(wb, fileName);
setDataExport([])
}
const handleEdit = (data) => {
setDataEdit(data)
handleOpenDialog('Edit');
}
const handleDelete = async (id) => {
await setAlertDelete(true)
await setIdDelete(id)
}
const handleCloseDialog = (type, data) => {
if (type === "save") {
saveProjectInvoice(data);
} else if (type === "edit") {
editMaterialR(data);
}
setDataEdit([])
setOpenDialog(false)
}
const saveProjectInvoice = async (data) => {
const result = await axios.post(PROJECT_INVOICE_ADD, data, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectInvoice()
NotificationManager.success(`Data project invoice berhasil ditambah`, 'Success!!');
} else {
NotificationManager.error(`${result.data.message}`, 'Failed!!');
}
}
const editMaterialR = async (data) => {
let urlEdit = PROJECT_INVOICE_EDIT(data.id)
const result = await axios.put(urlEdit, data, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectInvoice();
NotificationManager.success(`Data project invoice berhasil diedit`, 'Success!!');
} else {
NotificationManager.error(`Data project invoice gagal di edit`, `Failed!!`);
}
}
const toggleAddDialog = () => {
setOpenDialog(!openDialog)
}
const handleDialogIg = (id) => {
setIdPhaseProject(id)
setOpenDialogIG(true)
}
const closeDialogIG = () => {
setIdPhaseProject(0)
setOpenDialogIG(false)
}
const toggleDialogIG = () => {
if (openDialogIG) {
setIdPhaseProject(0)
}
setOpenDialogIG(!openDialogIG);
}
const onConfirmDelete = async () => {
let url = PROJECT_INVOICE_DELETE(idDelete);
const result = await axios.delete(url, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectInvoice()
setIdDelete(0)
setAlertDelete(false)
NotificationManager.success(`Data project invoice berhasil dihapus!`, 'Success!!');
} else {
setIdDelete(0)
setAlertDelete(false)
NotificationManager.error(`Data project invoice gagal dihapus!}`, 'Failed!!');
}
}
const cancelDelete = () => {
setAlertDelete(false)
setIdDelete(0)
}
const onShowSizeChange = (current, pageSize) => {
setRowsPerPage(pageSize)
}
const onPagination = (current, pageSize) => {
setCurrentPage(current)
}
const dataNotAvailable = () => {
if (dataTable.length === 0) {
return (
<tr>
<td align="center" colSpan="3">{t('noData')}</td>
</tr>
)
}
}
const renderTable = useMemo(() => {
const columns = [
{
title: t('action'),
dataIndex: '',
key: 'x',
className: 'nowrap',
render: (text, record) => <>
<Tooltip title={t('delete')}>
<i className="fa fa-trash" style={{ color: 'red', marginRight: '10px', cursor: "pointer" }} onClick={() => handleDelete(text.id)}></i>
</Tooltip>
<Tooltip title={t('Edit')}>
<i className="fa fa-edit" style={{ color: 'green', cursor: "pointer" }} onClick={() => handleEdit(text)}></i>
</Tooltip>{" "}
</>,
},
{ title: t('Project Invoice'), dataIndex: 'name', key: 'name', className: "nowrap" },
{
title: t('color'),
dataIndex: 'color',
key: 'color',
render: (text) => <>
<Tooltip title={text}>
<i className="fa fa-square" style={{ color: text }} ></i>
</Tooltip>
</>,
},
];
return (
<Table
rowKey="id"
size="small"
columns={columns}
dataSource={dataTable}
pagination={false}
/>
)
}, [dataTable])
return (
<div>
<NotificationContainer />
<SweetAlert
show={alertDelete}
warning
showCancel
confirmBtnText="Delete"
confirmBtnBsStyle="danger"
title={t('deleteConfirm')}
onConfirm={onConfirmDelete}
onCancel={cancelDelete}
focusCancelBtn
>
{t('deleteMsg')}
</SweetAlert>
<DialogForm
openDialog={openDialog}
closeDialog={handleCloseDialog}
toggleDialog={() => toggleAddDialog}
typeDialog={typeDialog}
dataEdit={dataEdit}
clickOpenModal={clickOpenModal}
/>
<Card>
<CardHeader style={{ display: "flex", justifyContent: "space-between" }}>
<h4 className="capitalize">{pageName}</h4>
<Row>
<Col>
<Input onChange={handleSearch} value={search} type="text" name="search" id="search" placeholder={t('search')} />
</Col>
<Col>
<Tooltip title={t('Add Project Invoice')}>
<Button style={{ background: "#4caf50", color: "#fff" }} onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button>
</Tooltip>
<Tooltip title={t('exportExcel')}>
<Button style={{ marginLeft: "5px" }} onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button>
</Tooltip>
</Col>
</Row>
</CardHeader>
<CardBody>
{renderTable}
<Pagination
style={{ marginTop: "25px" }}
showSizeChanger
onShowSizeChange={onShowSizeChange}
onChange={onPagination}
defaultCurrent={currentPage}
pageSize={rowsPerPage}
total={totalPage}
pageSizeOptions={["10", "15", "20", "25", "30", "35", "40"]}
/>
</CardBody>
</Card>
</div>
)
}
export default ProjectInvoice;

41
src/views/Master/ProjectInvoice/styles.css

@ -0,0 +1,41 @@
.App {
font-family: sans-serif;
padding: 20px;
}
h2 {
margin-top: 40px;
}
.rc-color-picker-panel {
border: 1px solid #ccc;
}
.rc-color-picker-panel-inner {
border: none;
box-shadow: none;
}
.rc-color-picker-panel-board-hsv {
border-radius: 12px;
outline: none;
}
.rc-color-picker-panel-board-value {
border: none;
border-radius: 12px;
}
.rc-color-picker-panel-board-saturation {
border: none;
border-radius: 12px;
}
.rc-color-picker-panel-ribbon {
border-radius: 12px;
}
.rc-color-picker-panel-wrap-preview {
border-radius: 12px;
}
.rc-color-picker-panel-preview span {
border-radius: 12px;
}
.rc-color-picker-panel-preview input {
border-radius: 12px;
}

110
src/views/Master/ProjectScheduleHealth/DialogForm.js

@ -0,0 +1,110 @@
import React, { useEffect, useState } from 'react'
import {
Modal, ModalHeader, ModalBody, ModalFooter,
Button, Form, FormGroup, Label, Input, Col, Row
} from 'reactstrap';
import 'antd/dist/antd.css';
import { Select } from "antd";
import InputColor from "./InputColor";
import "./styles.css";
import "rc-color-picker/assets/index.css";
import { useTranslation } from 'react-i18next';
const company_id = localStorage.getItem("company_id")
const { Option } = Select;
const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit }) => {
const [id, setId] = useState(0)
const [projectScheduleHealth, setProjectScheduleHealth] = useState('')
const [color, setColor] = useState('')
const { t } = useTranslation();
useEffect(() => {
if (typeDialog === "Edit") {
setId(dataEdit.id)
setProjectScheduleHealth(dataEdit.name)
setColor(dataEdit.color)
} else {
setId(0)
setColor('')
setProjectScheduleHealth('')
}
}, [dataEdit, openDialog])
const handleSave = () => {
let data = '';
if (typeDialog === "Save") {
data = {
name: projectScheduleHealth,
color,
company_id
}
closeDialog('save', data);
} else {
data = {
id,
name: projectScheduleHealth,
color,
company_id
}
closeDialog('edit', data);
}
setId(0)
}
const handleCancel = () => {
closeDialog('cancel', 'none')
setId(0)
}
const renderForm = () => {
return (
<Form>
<Row>
<Col md={6}>
<FormGroup>
<Label className="capitalize">{t('Project Schedule Health')}</Label>
<Select
value={projectScheduleHealth}
defaultValue={projectScheduleHealth}
onChange={setProjectScheduleHealth}
style={{ width: "100%", pointerEvents: typeDialog === 'Edit' ? 'none' : '' }}
suffixIcon={typeDialog === 'Edit' ? null : <i class="fa fa-chevron-down" aria-hidden="true"></i> }
>
<Option key="Behind Schedule" value="Behind Schedule">
Behind Schedule
</Option>
<Option key="Early Warning" value="Early Warning">
Early Warning
</Option>
<Option key="On Schedule" value="On Schedule">
On Schedule
</Option>
</Select>
</FormGroup>
</Col>
<Col md={6}>
<FormGroup>
<Label className="capitalize">{t('color')}</Label>
<InputColor value={color} color={color} onChange={(e) => setColor(e.color)} />
</FormGroup>
</Col>
</Row>
</Form>
)
}
return (
<>
<Modal size="lg" isOpen={openDialog} toggle={toggleDialog}>
<ModalHeader className="capitalize" toggle={closeDialog}>{typeDialog == "Save" ? `Add` : "Edit"} data</ModalHeader>
<ModalBody>
{renderForm()}
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={() => handleSave()}>{typeDialog}</Button>{' '}
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>{t('cancel')}</Button>
</ModalFooter>
</Modal>
</>
)
}
export default DialogForm;

89
src/views/Master/ProjectScheduleHealth/DialogFormInitial.js

@ -0,0 +1,89 @@
import React, { useEffect, useState } from 'react'
import {
Modal, ModalHeader, ModalBody, ModalFooter,
Button, Form, FormGroup, Label, Input, Col, Row
} from 'reactstrap';
const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit, idActivity, projectTypeId }) => {
const [id, setId] = useState(0)
const [activity, setActivity] = useState('')
useEffect(() => {
if (typeDialog === "edit") {
setId(dataEdit.id)
setActivity(dataEdit.name_activity)
} else {
setId(0)
setActivity('')
}
}, [dataEdit, openDialog])
const handleSave = () => {
let data = '';
if (typeDialog === "add") {
data = {
name_activity: activity,
proyek_type_id: projectTypeId
}
if (idActivity && idActivity > 0) {
data['parent_id'] = idActivity
}
closeDialog('save', data);
} else {
data = {
id,
name_activity: activity,
proyek_type_id: projectTypeId
}
if (idActivity && idActivity > 0) {
data['parent_id'] = idActivity
}
closeDialog('edit', data);
}
setId(0)
setActivity('')
}
const handleCancel = () => {
closeDialog('cancel', 'none')
setId(0)
setActivity('')
}
const renderForm = () => {
return (
<Form>
<FormGroup>
<Label className="capitalize">Activity</Label>
<Input row="4" type="textarea" value={activity} onChange={(e) => setActivity(e.target.value)} placeholder={`Persiapan ...`} />
</FormGroup>
</Form>
)
}
return (
<>
<Modal isOpen={openDialog} toggle={toggleDialog}>
<ModalHeader className="capitalize" toggle={closeDialog}>{typeDialog == "add" ? `Add` : "Edit"} Activity</ModalHeader>
<ModalBody>
{renderForm()}
</ModalBody>
<ModalFooter>
<Button className='capitalize' color="primary" onClick={() => handleSave()}>{typeDialog}</Button>{' '}
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>Batal</Button>
</ModalFooter>
</Modal>
</>
)
}
export default DialogForm;

46
src/views/Master/ProjectScheduleHealth/InputColor.js

@ -0,0 +1,46 @@
import React from "react";
import Input from "antd/lib/input";
import Button from "antd/lib/button";
import Dropdown from "antd/lib/dropdown";
import { Panel } from 'rc-color-picker'
import "antd/dist/antd.css";
import "./styles.css";
export default function InputColor(props) {
const { color, onChange } = props;
const [internalColor, setInternalColor] = React.useState(color);
const handleChange = (color) => {
setInternalColor(color.color);
if (onChange) {
onChange(color);
}
};
const overlay = (
<div>
<Panel
color={internalColor}
enableAlpha={false}
onChange={handleChange}
/>
</div>
);
return (
<>
<Input
value={internalColor || ""}
onChange={(e) => setInternalColor(e.target.value)}
suffix={
<Dropdown trigger={["click"]} overlay={overlay}>
<Button style={{ background: internalColor }}> </Button>
</Dropdown>
}
/>
</>
);
}

392
src/views/Master/ProjectScheduleHealth/index.js

@ -0,0 +1,392 @@
import * as XLSX from 'xlsx';
import DialogForm from './DialogForm';
import React, { useState, useEffect, useMemo } from 'react';
import SweetAlert from 'react-bootstrap-sweetalert';
import axios from "../../../const/interceptorApi"
import moment from 'moment'
import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap';
import { NotificationContainer, NotificationManager } from 'react-notifications';
import { PROJECT_SCHEDULE_HEALTH_ADD, PROJECT_SCHEDULE_HEALTH_EDIT, PROJECT_SCHEDULE_HEALTH_DELETE, PROJECT_SCHEDULE_HEALTH_SEARCH } from '../../../const/ApiConst';
import { Pagination, Button, Tooltip, Table } from 'antd';
import { useTranslation } from 'react-i18next';
const ProjectScheduleHealth = ({ params, ...props }) => {
let role_id = '', user_id = '', proyek_id = '', isLogin = '', token = '', company_id = 0, role_name = '';
if (props.location.state && props.location.state.role_id && props.location.state.user_id) {
role_id = props.location.state.role_id;
user_id = props.location.state.user_id;
token = props.location.state.token;
isLogin = props.location.state.isLogin;
role_name = props.location.state.role_name;
} else {
role_id = localStorage.getItem("role_id");
proyek_id = localStorage.getItem("proyek_id");
user_id = localStorage.getItem("user_id");
token = localStorage.getItem("token");
isLogin = localStorage.getItem("isLogin");
company_id = localStorage.getItem('company_id');
role_name = localStorage.getItem('role_name');
}
const HEADER = {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
}
}
const pageName = params.name;
const [alertDelete, setAlertDelete] = useState(false)
const [clickOpenModal, setClickOpenModal] = useState(false)
const [currentPage, setCurrentPage] = useState(1)
const [dataEdit, setDataEdit] = useState([])
const [dataExport, setDataExport] = useState([])
const [dataTable, setDatatable] = useState([])
const [idDelete, setIdDelete] = useState(0)
const [idPhaseProject, setIdPhaseProject] = useState(0)
const [openDialog, setOpenDialog] = useState(false)
const [openDialogIG, setOpenDialogIG] = useState(false)
const [rowsPerPage, setRowsPerPage] = useState(10)
const [search, setSearch] = useState('')
const [totalPage, setTotalPage] = useState(0)
const [typeDialog, setTypeDialog] = useState('Save')
const { t } = useTranslation()
useEffect(() => {
getDataProjectScheduleHealth()
}, [currentPage, rowsPerPage, search])
useEffect(() => {
const cekData = dataExport || []
if (cekData.length > 0) {
exportExcel()
}
}, [dataExport])
const getDataProjectScheduleHealth = async () => {
let start = 0;
if (currentPage !== 1 && currentPage > 1) {
start = (currentPage * rowsPerPage) - rowsPerPage
}
const payload = {
"columns": [
{
"name": "name",
"logic_operator": "ilike",
"value": search,
"operator": "AND"
}
],
"orders": {
"ascending": true,
"columns": [
'id'
]
},
"paging": {
"length": rowsPerPage,
"start": start
}
}
if (role_name !== "Super Admin") {
payload.columns.push(
{ "name": "company_id", "logic_operator": "=", "value": company_id, "operator": "AND" },
)
} else {
payload.columns.push(
{ "name": "company_id", "logic_operator": "is null", "value": "", "operator": "AND" },
)
}
const result = await axios
.post(PROJECT_SCHEDULE_HEALTH_SEARCH, payload, HEADER)
.then((res) => res)
.catch((err) => err.response);
if (result && result.data && result.data.code == 200) {
result.data.data.map((res) => {
res.key = res.id.toString()
});
setDatatable(result.data.data);
setTotalPage(result.data.totalRecord);
} else {
NotificationManager.error('Gagal Mengambil Data!!', 'Failed');
}
}
const handleSearch = e => {
const value = e.target.value
setSearch(value);
setCurrentPage(1)
};
const handleOpenDialog = (type) => {
setOpenDialog(true)
setTypeDialog(type)
}
const handleExportExcel = async () => {
let start = 0;
if (currentPage !== 1 && currentPage > 1) {
start = (currentPage * rowsPerPage) - rowsPerPage
}
const payload = {
"columns": [
{
"name": "name",
"logic_operator": "like",
"value": search,
"operator": "AND"
}
],
"orders": {
"ascending": true,
"columns": [
'id'
]
},
"paging": {
"length": rowsPerPage,
"start": start
}
}
const result = await axios
.post(PROJECT_SCHEDULE_HEALTH_SEARCH, payload, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code == 200) {
let resData = result.data.data;
const excelData = [];
resData.map((val, index) => {
let dataRow = {
"Nama": val.name,
"Color": val.color,
}
excelData.push(dataRow)
})
await setDataExport(excelData)
} else {
NotificationManager.error('Gagal Export Data!!', 'Failed');
}
}
const exportExcel = () => {
const dataExcel = dataExport || [];
const fileName = `Data ${pageName}.xlsx`;
const ws = XLSX.utils.json_to_sheet(dataExcel);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, `Data ${pageName}`);
XLSX.writeFile(wb, fileName);
setDataExport([])
}
const handleEdit = (data) => {
setDataEdit(data)
handleOpenDialog('Edit');
}
const handleDelete = async (id) => {
await setAlertDelete(true)
await setIdDelete(id)
}
const handleCloseDialog = (type, data) => {
if (type === "save") {
saveProjectScheduleHealth(data);
} else if (type === "edit") {
editMaterialR(data);
}
setDataEdit([])
setOpenDialog(false)
}
const saveProjectScheduleHealth = async (data) => {
const result = await axios.post(PROJECT_SCHEDULE_HEALTH_ADD, data, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectScheduleHealth()
NotificationManager.success(`Data project schedule health berhasil ditambah`, 'Success!!');
} else {
NotificationManager.error(`${result.data.message}`, 'Failed!!');
}
}
const editMaterialR = async (data) => {
let urlEdit = PROJECT_SCHEDULE_HEALTH_EDIT(data.id)
const result = await axios.put(urlEdit, data, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectScheduleHealth();
NotificationManager.success(`Data project schedule health berhasil di edit`, 'Success!!');
} else {
NotificationManager.error(`Data project schedule health gagal di edit`, `Failed!!`);
}
}
const toggleAddDialog = () => {
setOpenDialog(!openDialog)
}
const handleDialogIg = (id) => {
setIdPhaseProject(id)
setOpenDialogIG(true)
}
const closeDialogIG = () => {
setIdPhaseProject(0)
setOpenDialogIG(false)
}
const toggleDialogIG = () => {
if (openDialogIG) {
setIdPhaseProject(0)
}
setOpenDialogIG(!openDialogIG);
}
const onConfirmDelete = async () => {
let url = PROJECT_SCHEDULE_HEALTH_DELETE(idDelete);
const result = await axios.delete(url, HEADER)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataProjectScheduleHealth()
setIdDelete(0)
setAlertDelete(false)
NotificationManager.success(`Data project schedule health berhasil dihapus!`, 'Success!!');
} else {
setIdDelete(0)
setAlertDelete(false)
NotificationManager.error(`Data project schedule health gagal dihapus!}`, 'Failed!!');
}
}
const cancelDelete = () => {
setAlertDelete(false)
setIdDelete(0)
}
const onShowSizeChange = (current, pageSize) => {
setRowsPerPage(pageSize)
}
const onPagination = (current, pageSize) => {
setCurrentPage(current)
}
const dataNotAvailable = () => {
if (dataTable.length === 0) {
return (
<tr>
<td align="center" colSpan="3">{t('noData')}</td>
</tr>
)
}
}
const renderTable = useMemo(() => {
const columns = [
{
title: t('action'),
dataIndex: '',
key: 'x',
className: 'nowrap',
render: (text, record) => <>
<Tooltip title={t('delete')}>
<i className="fa fa-trash" style={{ color: 'red', marginRight: '10px', cursor: "pointer" }} onClick={() => handleDelete(text.id)}></i>
</Tooltip>
<Tooltip title={t('Edit')}>
<i className="fa fa-edit" style={{ color: 'green', cursor: "pointer" }} onClick={() => handleEdit(text)}></i>
</Tooltip>{" "}
</>,
},
{ title: t('Project Schedule Health'), dataIndex: 'name', key: 'name', className: "nowrap" },
{
title: t('color'),
dataIndex: 'color',
key: 'color',
render: (text) => <>
<Tooltip title={text}>
<i className="fa fa-square" style={{ color: text }} ></i>
</Tooltip>
</>,
},
];
return (
<Table
rowKey="id"
size="small"
columns={columns}
dataSource={dataTable}
pagination={false}
/>
)
}, [dataTable])
return (
<div>
<NotificationContainer />
<SweetAlert
show={alertDelete}
warning
showCancel
confirmBtnText="Delete"
confirmBtnBsStyle="danger"
title={t('deleteConfirm')}
onConfirm={onConfirmDelete}
onCancel={cancelDelete}
focusCancelBtn
>
{t('deleteMsg')}
</SweetAlert>
<DialogForm
openDialog={openDialog}
closeDialog={handleCloseDialog}
toggleDialog={() => toggleAddDialog}
typeDialog={typeDialog}
dataEdit={dataEdit}
clickOpenModal={clickOpenModal}
/>
<Card>
<CardHeader style={{ display: "flex", justifyContent: "space-between" }}>
<h4 className="capitalize">{pageName}</h4>
<Row>
<Col>
<Input onChange={handleSearch} value={search} type="text" name="search" id="search" placeholder={t('search')} />
</Col>
<Col>
<Tooltip title={t('Add Project Schedule Health')}>
<Button style={{ background: "#4caf50", color: "#fff" }} onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button>
</Tooltip>
<Tooltip title={t('exportExcel')}>
<Button style={{ marginLeft: "5px" }} onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button>
</Tooltip>
</Col>
</Row>
</CardHeader>
<CardBody>
{renderTable}
<Pagination
style={{ marginTop: "25px" }}
showSizeChanger
onShowSizeChange={onShowSizeChange}
onChange={onPagination}
defaultCurrent={currentPage}
pageSize={rowsPerPage}
total={totalPage}
pageSizeOptions={["10", "15", "20", "25", "30", "35", "40"]}
/>
</CardBody>
</Card>
</div>
)
}
export default ProjectScheduleHealth;

41
src/views/Master/ProjectScheduleHealth/styles.css

@ -0,0 +1,41 @@
.App {
font-family: sans-serif;
padding: 20px;
}
h2 {
margin-top: 40px;
}
.rc-color-picker-panel {
border: 1px solid #ccc;
}
.rc-color-picker-panel-inner {
border: none;
box-shadow: none;
}
.rc-color-picker-panel-board-hsv {
border-radius: 12px;
outline: none;
}
.rc-color-picker-panel-board-value {
border: none;
border-radius: 12px;
}
.rc-color-picker-panel-board-saturation {
border: none;
border-radius: 12px;
}
.rc-color-picker-panel-ribbon {
border-radius: 12px;
}
.rc-color-picker-panel-wrap-preview {
border-radius: 12px;
}
.rc-color-picker-panel-preview span {
border-radius: 12px;
}
.rc-color-picker-panel-preview input {
border-radius: 12px;
}

43
src/views/SimproV2/CreatedProyek/DialogGantt.js

@ -247,27 +247,28 @@ const DialogGantt = ({ openDialog, closeDialog, toggleDialog, idTask, proyekName
) : ( ) : (
<div>Gantt Project {proyekName}</div> <div>Gantt Project {proyekName}</div>
)} )}
<Select <div style={{ display:'flex'}}>
placeholder='Search Gantt Name' <Select
showSearch placeholder='Search Gantt Name'
filterOption={(inputValue, option) => showSearch
option.children.toLowerCase().includes(inputValue.toLowerCase()) filterOption={(inputValue, option) =>
} option.children.toLowerCase().includes(inputValue.toLowerCase())
onChange={(val) => setSearchGantt(val)} }
style={{ width: 200 }} onChange={(val) => setSearchGantt(val)}
> style={{ width: 200, marginRight:'10px' }}
{dataGantt.map((res) => ( >
<Option key={res.id} value={res.id}> {dataGantt.map((res) => (
{res.name_version} <Option key={res.id} value={res.id}>
</Option> {res.name_version}
))} </Option>
</Select> ))}
</Select>
{!hierarchyId && ( {!hierarchyId && (
<Button onClick={handleOpenDialogForm} size='sm' color="primary"> <Button onClick={handleOpenDialogForm} size='sm' color="primary">
<i className='fa fa-plus'></i> <i className='fa fa-plus'></i>
</Button> </Button>
)} )}
</div>
</ModalHeader> </ModalHeader>
<ModalBody> <ModalBody>

39
src/views/SimproV2/CreatedProyek/index.js

@ -142,7 +142,7 @@ const CreatedProyek = ({ params, ...props }) => {
const [userProyek, setUserProyek] = useState([]); const [userProyek, setUserProyek] = useState([]);
const [materialProyek, setMaterialProyek] = useState([]); const [materialProyek, setMaterialProyek] = useState([]);
const [dataCharter, setDataCharter] = useState(null); const [dataCharter, setDataCharter] = useState(null);
const [dataViewStartDate, setDataViewStartDate] = useState([]); const [dataViewStartDate, setDataViewStartDate] = useState("");
const [materialResource, setMaterialResource] = useState([]); const [materialResource, setMaterialResource] = useState([]);
const [toolsResource, setToolsResource] = useState([]); const [toolsResource, setToolsResource] = useState([]);
const [dataTypeProyek, setDataTypeProyek] = useState([]); const [dataTypeProyek, setDataTypeProyek] = useState([]);
@ -376,7 +376,7 @@ const CreatedProyek = ({ params, ...props }) => {
paging: { start: start, length: rowsPerPage }, paging: { start: start, length: rowsPerPage },
}; };
if (all_project) { if (all_project !== null && all_project === true) {
payload["columns"] = [ payload["columns"] = [
{ name: "company_id", logic_operator: "like", value: company_id, operator: "AND" } { name: "company_id", logic_operator: "like", value: company_id, operator: "AND" }
]; ];
@ -512,6 +512,7 @@ const CreatedProyek = ({ params, ...props }) => {
}; };
const handleOpenDialogGantt = (data) => { const handleOpenDialogGantt = (data) => {
console.log('data.mulai_proyek', data.mulai_proyek);
setDataViewStartDate(data.mulai_proyek) setDataViewStartDate(data.mulai_proyek)
setidTask(data.id); setidTask(data.id);
setProyekName(data.nama); setProyekName(data.nama);
@ -1534,32 +1535,12 @@ const CreatedProyek = ({ params, ...props }) => {
</span> </span>
<span className="menu-text">Assign Checklist K3</span> <span className="menu-text">Assign Checklist K3</span>
</div> </div>
{/* <div <div className="menu-list" onClick={() => handleOpenDialogGantt(text)}>
className="menu-list" <span className="menu-icon">
onClick={() => handleOpenDialogMaterial(text)} <i className="fa fa-bars"></i>
> </span>
<span className="menu-icon"> <span className="menu-text">Gantt</span>
<i className="fa fa-cube"></i> </div>
</span>
<span className="menu-text">Request Material Resource</span>
</div> */}
{/* <div className="menu-list" onClick={() => handleOpenDialogTools(text)}>
<span className="menu-icon">
<i className="fa fa-cog"></i>
</span>
<span className="menu-text">Request Tools</span>
</div> */}
{/* <Link to={`/project-charter/${text.id}/gantt`}> */}
{
/*text.type_proyek_id != 9 ?*/
<div className="menu-list" onClick={() => handleOpenDialogGantt(text)}>
<span className="menu-icon">
<i className="fa fa-bars"></i>
</span>
<span className="menu-text">Gantt</span>
</div>
/*: null*/
}
<div className="menu-list" onClick={() => handleSCurve(text)}> <div className="menu-list" onClick={() => handleSCurve(text)}>
<span className="menu-icon"> <span className="menu-icon">
<i className="fa fa-line-chart"></i> <i className="fa fa-line-chart"></i>
@ -1572,7 +1553,6 @@ const CreatedProyek = ({ params, ...props }) => {
</span> </span>
<span className="menu-text">Report Analysis</span> <span className="menu-text">Report Analysis</span>
</div> </div>
{/* </Link> */}
<div <div
className="menu-list" className="menu-list"
onClick={() => handleOpenDialogProyek(text.id)} onClick={() => handleOpenDialogProyek(text.id)}
@ -1995,6 +1975,7 @@ const CreatedProyek = ({ params, ...props }) => {
toggleDialog={toggleDialogHierarchy} toggleDialog={toggleDialogHierarchy}
idTask={idTask} idTask={idTask}
proyekName={proyekName} proyekName={proyekName}
dataViewStartDate={dataViewStartDate}
/> />
), ),
[openDialogHierarchy] [openDialogHierarchy]

Loading…
Cancel
Save