Browse Source

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

Reviewed-on: ibnu/generic-ospro-frontend#43
pull/1/head
farhantock 11 months ago
parent
commit
d99fd6fbf2
  1. BIN
      public/OSPRO.ico
  2. 4
      public/index.html
  3. 2
      src/App.js
  4. 2
      src/containers/DefaultLayout/DefaultLayout.js
  5. 2
      src/routes.js
  6. 67
      src/views/Dashboard/DashboardBOD.js
  7. 132
      src/views/Dashboard/DashboardProject.js
  8. 48
      src/views/Master/MenuCompany/DialogForm.js
  9. 418
      src/views/Master/MenuCompany/DialogMasterMenu.js
  10. 89
      src/views/Master/MenuCompany/index.js
  11. 16
      src/views/Pages/Login/Login.js

BIN
public/OSPRO.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

4
public/index.html

@ -8,13 +8,13 @@
<meta name="description" content="OSPRO">
<meta name="author" content="Integrasia Utama">
<meta name="keyword" content="OSPRO,Integrasia,OSLOG,Asset,WebGIS,Aplikasi,Tracking System">
<title id="title"></title>
<title id="title">OSPRO</title>
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/simpro-icon.ico">
<link rel="shortcut icon" href="%PUBLIC_URL%/OSPRO.ico">
<!-- <link rel="shortcut icon" href="%PUBLIC_URL%/favicon_bmd_denpasar.ico"> -->
<link rel="stylesheet" href="https://unpkg.com/@coreui/icons@1.0.0/css/all.min.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"

2
src/App.js

@ -27,11 +27,9 @@ class App extends Component {
const htmlTitle = data.html_title || 'OSPRO';
document.title = htmlTitle;
} else {
console.log('No data found in localStorage for key "configApp".');
document.title = 'OSPRO';
}
} catch (error) {
console.error('Error in componentDidMount:', error);
document.title = 'OSPRO';
}
}

2
src/containers/DefaultLayout/DefaultLayout.js

@ -118,6 +118,8 @@ class DefaultLayout extends Component {
e.preventDefault()
}
await localStorage.removeItem("role_id");
await localStorage.removeItem("company_id");
document.getElementById('title').innerText = 'OSPRO';
await window.localStorage.clear();
this.props.history.replace('/login')
}

2
src/routes.js

@ -22,7 +22,7 @@ const Layer = React.lazy(() => import('./views/Layers/Layer'));
const Layers = React.lazy(() => import('./views/Layers/Layers'));
const Lembur = React.lazy(() => import('./views/Master/MasterLembur'));
const MapConfig = React.lazy(() => import('./views/MapConfig'));
const Menu = React.lazy(() => import('./views/Master/MasterMenu'));
const Menu = React.lazy(() => import('./views/Master/MenuCompany'));
const OfficeHours = React.lazy(() => import('./views/Master/MasterOfficeHours'));
const Organization = React.lazy(() => import('./views/Master/MasterOrganization'));
const PanicButton = React.lazy(() => import('./views/SimproV2/PanicButton'));

67
src/views/Dashboard/DashboardBOD.js

@ -15,7 +15,7 @@ import { HealthByBudget, HealthBySchedule } from './Components';
import { Link } from 'react-router-dom';
import { Card, CardBody, CardHeader, Input } from "reactstrap";
const DashboardBOD = (props) => {
let role_id = 0, user_id = 0, isLogin = false, token = '', company_id = 0, all_project = null, role_name='', hierarchy=[], user_name='';
let role_id = 0, user_id = 0, isLogin = false, token = '', company_id = 0, all_project = null, role_name = '', hierarchy = [], user_name = '';
if (props && props.role_id && props.user_id) {
role_id = props.role_id;
user_id = props.user_id;
@ -61,13 +61,6 @@ const DashboardBOD = (props) => {
const [READY_TABLE_DETAIL_EXPENDITURE, SET_READY_TABLE_DETAIL_EXPENDITURE] = useState(false);
const [DATA_DETAIL_EXPENDITURE, SET_DATA_DETAIL_EXPENDITURE] = useState([]);
const currentYear = new Date().getFullYear();
const years = Array.from({ length: 6 }, (_, index) => currentYear - index);
const storedYear = localStorage.getItem('selectedYear');
const initialYear = storedYear ? storedYear : currentYear.toString();
const [searchColumn, setSearchColumn] = useState(initialYear);
useEffect(() => {
getCompanyCashFlow(); // expenditure
getCompanyExpenditureColor(); // expenditure Color
@ -82,14 +75,13 @@ const DashboardBOD = (props) => {
getProjectScheduleHealthPerDivision(); // project by schedule health per division
getTotalProjectPerDivision(); // project by division
getTotalProjectValuePerDivision(); // project value by division
localStorage.setItem('selectedYear', searchColumn);
}, [searchColumn])
}, [])
// project expenditure
const getCompanyCashFlow = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-company-cashflow/${searchColumn}/${company_id}/${all_project}/${hierarchy}`
const URL = `${BASE_OSPRO}/api/dashboard/get-company-cashflow/${company_id}/${all_project}/${hierarchy}/${role_name}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
const content = "Get Project Expenditure.";
@ -115,31 +107,31 @@ const DashboardBOD = (props) => {
// Project Expenditure Color
const getCompanyExpenditureColor = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-detail-expenditure-color/${company_id}`
const URL = `${BASE_OSPRO}/api/dashboard/get-detail-expenditure-color/${company_id}/${role_name}`
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 URL = `${BASE_OSPRO}/api/dashboard/get-detail-financial-health-color/${company_id}/${role_name}`
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 URL = `${BASE_OSPRO}/api/dashboard/get-detail-schedule-health-color/${company_id}/${role_name}`
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 URL = `${BASE_OSPRO}/api/dashboard/get-detail-invoice-color/${company_id}/${role_name}`
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 URL = `${BASE_OSPRO}/api/dashboard/get-invoice-outstanding/${searchColumn}/${company_id}/${all_project}/${hierarchy}`
const URL = `${BASE_OSPRO}/api/dashboard/get-invoice-outstanding/${company_id}/${all_project}/${hierarchy}/${role_name}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
const content = "Get Project Invoice vs Cash In.";
@ -162,7 +154,7 @@ const DashboardBOD = (props) => {
}
const getProjectPerScheduleHealth = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-schedule-health/${searchColumn}/${company_id}/${all_project}/${hierarchy}`
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-schedule-health/${company_id}/${all_project}/${hierarchy}/${role_name}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
const content = "Get Project by Schedule Health.";
@ -186,7 +178,7 @@ const DashboardBOD = (props) => {
}
const getProjectPerBudgetHealth = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-budget-health/${searchColumn}/${company_id}/${all_project}/${hierarchy}`
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-budget-health/${company_id}/${all_project}/${hierarchy}/${role_name}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
const content = "Get Project by Budget Health.";
@ -209,7 +201,7 @@ const DashboardBOD = (props) => {
}
const getProjectBudgetHealthPerDivision = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-budget-health-per-division/${searchColumn}/${company_id}/${all_project}/${hierarchy}`
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-budget-health-per-division/${company_id}/${all_project}/${hierarchy}/${role_name}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
const content = "Get Project by Schedule Health per Division.";
@ -233,7 +225,7 @@ const DashboardBOD = (props) => {
}
const getProjectScheduleHealthPerDivision = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-schedule-health-per-division/${searchColumn}/${company_id}/${all_project}/${hierarchy}`
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-schedule-health-per-division/${company_id}/${all_project}/${hierarchy}/${role_name}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
const content = "Get Project by Schedule Health.";
@ -256,7 +248,7 @@ const DashboardBOD = (props) => {
}
const getProjectPerPhase = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-phase/${searchColumn}/${company_id}/${all_project}/${hierarchy}`
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-phase/${company_id}/${all_project}/${hierarchy}/${role_name}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
const content = "Get Project by Phase.";
@ -281,7 +273,7 @@ const DashboardBOD = (props) => {
}
const getTotalProjectPerDivision = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-division/${searchColumn}/${company_id}/${all_project}/${hierarchy}`
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-per-division/${company_id}/${all_project}/${hierarchy}/${role_name}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
const content = "Get Project by Division.";
@ -306,7 +298,7 @@ const DashboardBOD = (props) => {
}
const getTotalProjectValuePerDivision = async () => {
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-value-per-division/${searchColumn}/${company_id}/${all_project}/${hierarchy}`
const URL = `${BASE_OSPRO}/api/dashboard/get-total-project-value-per-division/${company_id}/${all_project}/${hierarchy}/${role_name}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
const content = "Get Project Value by Division";
@ -335,7 +327,7 @@ const DashboardBOD = (props) => {
return;
}
setOpenDetailedView(true);
const URL = `${BASE_OSPRO}/api/dashboard/get-detail-expenditure/${searchColumn}/${company_id}/${all_project}/${hierarchy}`
const URL = `${BASE_OSPRO}/api/dashboard/get-detail-expenditure/${company_id}/${all_project}/${hierarchy}/${role_name}`
const result = await axios.get(URL, HEADER).then(res => res).catch(err => err.response)
const content = "Get Detail Expenditure";
@ -454,33 +446,6 @@ const DashboardBOD = (props) => {
<div style={{ marginLeft: -25, marginRight: -25 }}>
{/* <FloatingFilter /> */}
<NotificationContainer />
<Row>
<Col span={24} style={{ backgroundColor: '#fff', height: '8vh', display: 'flex', justifyContent: 'space-between' }}>
<div style={{ marginTop: '7px', marginLeft: '10px' }}>
<h4>Dashboard</h4>
</div>
<div style={{ display: 'flex', marginRight: '15px' }}>
<div style={{ marginTop: '11px', marginRight: '5px' }}>
<p>Search by Years</p>
</div>
<div>
<Input
type="select"
value={searchColumn}
style={{ width: '100px', marginTop: '7px', cursor: 'pointer' }}
onChange={(e) => setSearchColumn(e.target.value)}
name="select"
>
{years.map((year) => (
<option key={year} value={year.toString()}>
{year}
</option>
))}
</Input>
</div>
</div>
</Col>
</Row>
<Row>
<Col span={12}>
<Row>

132
src/views/Dashboard/DashboardProject.js

@ -30,7 +30,9 @@ import {
} from "./Components";
import { Fab, Action } from "react-tiny-fab";
import "react-tiny-fab/dist/styles.css";
import { useHistory, useLocation, useParams } from "react-router-dom";
import { useHistory, useParams } from "react-router-dom";
import { Icon } from '@iconify/react';
import arrowLeft from '@iconify/icons-ion/ios-arrow-back';
const { TextArea } = Input;
@ -64,8 +66,7 @@ const center = {
};
const DashboardProject = (props) => {
let role_id = 0, user_id = 0, isLogin = false, token = '', company_id = 0, all_project = null, role_name='', hierarchy=[], user_name='';
let role_id = 0, user_id = 0, isLogin = false, token = '', company_id = 0, all_project = null, role_name = '', hierarchy = [], user_name = '';
if (props && props.role_id && props.user_id) {
role_id = props.role_id;
user_id = props.user_id;
@ -78,7 +79,6 @@ const DashboardProject = (props) => {
hierarchy = props.hierarchy;
user_name = props.user_name;
}
const HEADER = {
headers: {
"Content-Type": "application/json",
@ -140,6 +140,8 @@ const DashboardProject = (props) => {
const [isReadyGanttParents, setIsReadyGanttParents] = useState(false);
const [calculationStatus, setCalculationStatus] = useState(false);
const [isHierarchy, setIsHierarchy] = useState(null);
const [dataOverdue, setDataOverdue] = useState(null);
const [projectType, setProjectType] = useState(null);
let history = useHistory();
useEffect(() => {
@ -150,6 +152,7 @@ const DashboardProject = (props) => {
getGantt();
getGanttParents();
return () => {
console.log("unmount RenderMap");
};
}, []);
@ -158,9 +161,10 @@ const DashboardProject = (props) => {
getSCurve();
}
window.addEventListener("message", handleIframeMessage);
window.addEventListener('message', handleGetOverdue);
return () => {
window.removeEventListener("message", handleIframeMessage);
window.removeEventListener("message", handleGetOverdue);
};
}, [isHierarchy]);
@ -183,8 +187,13 @@ const DashboardProject = (props) => {
useEffect(() => {
let deviation = 0;
if (plannedCost && totalCost) {
deviation = plannedCost - totalCost;
// Convert plannedCost and totalCost to numbers
const plannedCostNumber = parseFloat(plannedCost);
const totalCostNumber = parseFloat(totalCost);
// Check if conversion is successful
if (!isNaN(plannedCostNumber) && !isNaN(totalCostNumber)) {
deviation = plannedCostNumber - totalCostNumber;
}
setRemToComplete(deviation.toString());
}, [plannedCost, totalCost]);
@ -199,6 +208,7 @@ const DashboardProject = (props) => {
const response = await axios.get(url, HEADER);
setManPower(response.data.totalRecord);
} catch (error) {
console.error("Failed to get manpower:", error);
}
};
@ -215,6 +225,7 @@ const DashboardProject = (props) => {
}
setIsReadyGantt(true);
} catch (error) {
console.error("Failed to get gantt data:", error);
setIsReadyGantt(true);
}
};
@ -227,6 +238,7 @@ const DashboardProject = (props) => {
setDataGanttParents(response);
setIsReadyGanttParents(true);
} catch (error) {
console.error("Failed to get gantt data:", error);
setIsReadyGanttParents(true);
}
};
@ -244,6 +256,7 @@ const DashboardProject = (props) => {
setAssignedHrCount(assignedList.length);
setAssignedHr(assignedList);
} catch (error) {
console.error("Failed to get assigned HR:", error);
}
};
@ -300,6 +313,7 @@ const DashboardProject = (props) => {
.get(URL, HEADER)
.then((res) => res)
.catch((err) => err.response);
console.log("getProjectDetail", result);
if (!result) {
NotificationManager.error(`Could not connect to internet.`, "Failed");
setIsReadyProjectDetail(true);
@ -314,6 +328,8 @@ const DashboardProject = (props) => {
setIsReadyProjectDetail(true);
return;
} else if (result.status == 200 && result.data.data) {
console.log(result.data.data);
// setComments(result.data.data);
setProjectName(result.data.data.nama ? result.data.data.nama : "-");
setProjectManagerName(
result.data.data.projectManager ? result.data.data.projectManager : "-"
@ -341,6 +357,7 @@ const DashboardProject = (props) => {
? result.data.data.header?.planned_end
: null
);
setProjectType(result.data.data.type_proyek_id);
setActualStart(
result.data.data.header?.start_date
? result.data.data.header.start_date
@ -408,6 +425,7 @@ const DashboardProject = (props) => {
let selisihProgress = 0;
let planningProgress = 0;
let actualProgress = 0;
console.log("test flow ", result.data.data);
let statusHealthBySchedule = "on-schedule";
if (
result.data.data.length > 0 &&
@ -431,21 +449,13 @@ const DashboardProject = (props) => {
result.data.data[0].data.budget_control.cost_deviation?.toString()
);
}
let now = new Date().toISOString().slice(0, 10);
let dates = result.data.data[0].data?.date;
let n = dates.findIndex(
(element) => new Date(now) < new Date(element[0])
);
if (n < 0) {
n = dates.length - 1;
}
if (
result.data.data.length > 0 &&
result.data.data[0].data?.percentagePlan &&
result.data.data[0].data?.percentagePlan.length > 0
) {
planningProgress = result.data.data[0].data?.percentagePlan[n];
setPlanningProgress(planningProgress);
planningProgress = result.data.data[0].data?.percentagePlan[result.data.data[0].data?.percentagePlan.length - 1];
setPlanningProgress(Math.ceil(planningProgress));
}
if (
result.data.data.length > 0 &&
@ -484,6 +494,7 @@ const DashboardProject = (props) => {
.post(URL, payload, HEADER)
.then((res) => res)
.catch((err) => err.response);
console.log("getOverdueActivities", result);
if (!result) {
NotificationManager.error(`Could not connect to internet.`, "Failed");
setIsReadyOverdueActivities(true);
@ -520,6 +531,7 @@ const DashboardProject = (props) => {
.post(URL, payload, HEADER)
.then((res) => res)
.catch((err) => err.response);
console.log("getIntegrationInvoice", result);
if (!result) {
NotificationManager.error(`Could not connect to internet.`, "Failed");
setIsReadyIntegrationInvoice(true);
@ -566,6 +578,7 @@ const DashboardProject = (props) => {
.post(URL, payload, HEADER)
.then((res) => res)
.catch((err) => err.response);
console.log("getReportDistribution", result);
if (!result) {
NotificationManager.error(`Could not connect to internet.`, "Failed");
setIsReadyReportDistribution(true);
@ -618,6 +631,7 @@ const DashboardProject = (props) => {
.post(URL, payload, HEADER)
.then((res) => res)
.catch((err) => err.response);
console.log("getComments", result);
if (!result) {
NotificationManager.error(`Could not connect to internet.`, "Failed");
setIsReadyComments(true);
@ -638,6 +652,7 @@ const DashboardProject = (props) => {
};
const handleSendComment = async () => {
// console.log('handleSendComment', comment);
setIsSendingComment(true);
if (comment === "") {
NotificationManager.error(
@ -684,6 +699,10 @@ const DashboardProject = (props) => {
setIsSendingComment(false);
};
const handleBack = async () => {
window.parent.location.reload();
};
const initMap = () => {
let mymap = L.map("map-area", {
center: center,
@ -731,6 +750,12 @@ const DashboardProject = (props) => {
}
};
const handleGetOverdue = (event) => {
if (event.data.type === 'dataOverdueUpdate') {
setDataOverdue(event.data.dataOverdue);
}
}
const RenderGantt = useMemo(
() => (
<iframe
@ -804,7 +829,8 @@ const DashboardProject = (props) => {
}, [comment, comments, isSendingComment, isReadyComments]);
const RenderBehindTasks = useMemo(() => {
return (
return SCURVE ?
(
<div>
{!isReadyOverdueActivities && <ListLoader />}
{isReadyOverdueActivities &&
@ -845,12 +871,40 @@ const DashboardProject = (props) => {
}
}
return (
<BehindTaskItem key={idx} name={item.name} message={message} />
<BehindTaskItem key={idx} name={item.name + ' - ' + item.name_version} message={message} scurve={SCURVE} proyek_id={PROJECT_ID} gantt_id={item.version_gantt_id} />
);
})}
</div>
)
:
(
<div>
{!dataOverdue && <ListLoader />}
{dataOverdue &&
dataOverdue.length < 1 && (
<div
style={{
flex: 1,
textAlign: "center",
color: "#E80053",
marginTop: 50,
marginBottom: 50,
}}
>
No overdue activity found.
</div>
)}
{dataOverdue &&
dataOverdue.length > 0 &&
dataOverdue.map((item, idx) => {
let message = `Overdue by ${item.overdue} days`;
return (
<BehindTaskItem key={idx} name={item.text} message={message} />
);
})}
</div>
);
}, [overdueActivities, isReadyOverdueActivities]);
}, [dataOverdue, isReadyOverdueActivities, overdueActivities]);
return (
<div style={{ marginLeft: -25, marginRight: -25 }}>
@ -858,7 +912,23 @@ const DashboardProject = (props) => {
<Row>
<Col span={18}>
<Row>
<Col span={8}>
{
!SCURVE ?
<Col span={2}>
<Button
style={{
height: "100%",
width: "100%",
fontSize: "18px"
}}
onClick={handleBack}
type="primary"
>
<Icon icon={arrowLeft} style={{ fontSize: "20px" }} /> Back
</Button>
</Col> : null
}
<Col span={!SCURVE ? 6 : 8}>
<div
style={{
border: "solid",
@ -1026,7 +1096,7 @@ const DashboardProject = (props) => {
</Col>
</Row>
<Row>
<Col span={SCURVE ? 8 : 10}>
<Col span={SCURVE ? 8 : projectType != 9 ? 8 : 10}>
<div
style={{
border: "solid",
@ -1151,7 +1221,7 @@ const DashboardProject = (props) => {
</div>
</div>
</Col>
{SCURVE ? (
{SCURVE || projectType != 9 ? (
<Col span={16}>
<div
style={{
@ -1290,14 +1360,16 @@ const DashboardProject = (props) => {
BCWP (cost vs perform)
</Col>
<Col span={5} style={{ fontSize: 11 }}>
{isReadySCurve ? (
bcwp ? (
renderFormatRupiah(bcwp, "Rp.")
) : (
"-"
{isReadySCurve ?
(
planningProgress > 100 || actualProgress > 100
?
renderFormatRupiah(((parseInt(plannedCost) * parseFloat((actualProgress / planningProgress) * 100).toFixed(0)) / 100).toString(), "Rp.")
:
renderFormatRupiah(((parseInt(plannedCost) * parseInt(actualProgress)) / 100).toString(), "Rp.")
)
) : (
<SingleTextLoader width={100} height={10} />
: (
"-"
)}
</Col>
<Col

48
src/views/Master/MasterMenu/DialogForm.js → src/views/Master/MenuCompany/DialogForm.js

@ -7,7 +7,7 @@ import 'antd/dist/antd.css';
import { useTranslation } from 'react-i18next';
const { Option } = Select
const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit, dataMenu }) => {
const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdit, dataMenu, listCompany, role_name, company_id }) => {
const [id, setId] = useState(0)
const [name, setName] = useState('')
const [url, setUrl] = useState('')
@ -15,17 +15,19 @@ const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdi
const [icon, setIcon] = useState('')
const [sequence, setSequence] = useState(0)
const [parentId, setParentId] = useState(null)
const [selectedCompany, setSelectedCompany] = useState(null)
const { t } = useTranslation()
useEffect(() => {
if (typeDialog === "Edit") {
setId(dataEdit.id)
setName(dataEdit.name)
setName(dataEdit.join_first_name)
setUrl(dataEdit.url)
setIcon(dataEdit.icon)
setParentId(dataEdit.parent_id)
setParentId(dataEdit.parent_menu_id)
setSequence(dataEdit.sequence)
setAliasName(dataEdit.alias_name)
setSelectedCompany(dataEdit.company_id)
} else {
setId(0)
setName('')
@ -61,12 +63,16 @@ const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdi
const err = validation();
if (!err) {
if (typeDialog === "Save") {
if (role_name === 'Super Admin') {
company_id = selectedCompany
}
data = {
name,
url,
sequence: parseInt(sequence),
icon,
alias_name: aliasName,
company_id: company_id
}
if (parentId && parentId > 0) {
@ -75,6 +81,9 @@ const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdi
closeDialog('save', data);
} else {
if (role_name === 'Super Admin') {
company_id = selectedCompany
}
data = {
id,
name,
@ -82,12 +91,12 @@ const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdi
sequence: parseInt(sequence),
icon,
alias_name: aliasName,
company_id: company_id
}
if (parentId && parentId > 0) {
data['parent_id'] = parentId
}
closeDialog('edit', data);
}
setId(0)
@ -127,6 +136,10 @@ const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdi
)
}
const onChangeCompany = (val) => {
setSelectedCompany(val);
};
const renderForm = () => {
return (
<Form>
@ -135,6 +148,33 @@ const DialogForm = ({ openDialog, closeDialog, toggleDialog, typeDialog, dataEdi
<span style={{ color: "red" }}>*</span> Wajib diisi.
</Col>
</Row>
{role_name === 'Super Admin' &&
<Row>
<Col md={12}>
<FormGroup>
<Label className="capitalize">
{t('company')}<span style={{ color: "red" }}>*</span>
</Label>
<Select
showSearch
filterOption={(inputValue, option) =>
option.children.toLowerCase().includes(inputValue.toLowerCase())
}
value={selectedCompany}
defaultValue={selectedCompany}
onChange={onChangeCompany}
style={{ width: "100%" }}
>
{listCompany.map((res) => (
<Option key={res.id} value={res.id}>
{res.company_name}
</Option>
))}
</Select>
</FormGroup>
</Col>
</Row>
}
<Row>
<Col>
<FormGroup>

418
src/views/Master/MenuCompany/DialogMasterMenu.js

@ -0,0 +1,418 @@
import * as XLSX from 'xlsx';
import DialogForm from './DialogForm.js';
import React, { useState, useEffect, useMemo } from 'react';
import SweetAlert from 'react-bootstrap-sweetalert';
import axios from 'axios';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap';
import { MENU_ADD, MENU_SEARCH, MENU_EDIT, MENU_DELETE, MENU_LIST } from '../../../const/ApiConst.js';
import { NotificationContainer, NotificationManager } from 'react-notifications';
import { Pagination, Tooltip, Table, Spin } from 'antd';
import { useTranslation } from 'react-i18next';
const token = window.localStorage.getItem('token');
const column = [
{ name: "Nama" },
{ name: "Url" },
{ name: "Ikon" },
{ name: "Alias" },
{ name: "Urutan" },
{ name: "Parent" },
]
const Index = ({ params, openDialog, toggleDialog, closeDialog }) => {
const [alertDelete, setAlertDelete] = useState(false)
const [allDataMenu, setAllDataMenu] = useState([])
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 [rowsPerPage, setRowsPerPage] = useState(10)
const [search, setSearch] = useState('')
const [tooltipDelete, setTooltipDelete] = useState(false)
const [tooltipEdit, setTooltipEdit] = useState(false)
const [tooltipExport, setTooltipExport] = useState(false)
const [tooltipTambah, setTooltipTambah] = useState(false)
const [totalPage, setTotalPage] = useState(0)
const [typeDialog, setTypeDialog] = useState('Save')
const [loading, SetLoading] = useState(false)
const { t } = useTranslation()
// const pageName = params.name;
const config = {
headers:
{
Authorization: `Bearer ${token}`,
"Content-type": `application/json`
}
};
useEffect(() => {
getDataMenu();
}, [search, currentPage, rowsPerPage])
useEffect(() => {
const cekData = dataExport || []
if (cekData.length > 0) {
exportExcel()
}
}, [dataExport])
const handleSearch = e => {
const value = e.target.value
setSearch(value);
setCurrentPage(1)
};
const getDataAllMenu = async () => {
const result = await axios
.get(MENU_LIST, config)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code == 200) {
let arr = []
let dataRes = result.data.data;
for (const v in dataRes) {
arr.push(dataRes[v])
}
setAllDataMenu(arr);
} else {
NotificationManager.error('Gagal Mengambil Data!!', 'Failed');
}
}
const getDataMenu = async () => {
SetLoading(true)
let start = 0;
if (currentPage !== 1 && currentPage > 1) {
start = (currentPage * rowsPerPage) - rowsPerPage
}
const payload = {
"paging": { "start": start, "length": rowsPerPage },
"columns": [
{ "name": "name", "logic_operator": "ilike", "value": search, "operator": "AND" }
],
"joins": [{
"name": "m_menu",
"column_join": "parent_id",
"column_results": [
"name"
]
}],
"orders": { "columns": ["id"], "ascending": false }
}
const result = await axios
.post(MENU_SEARCH, payload, config)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code == 200) {
setDatatable(result.data.data);
setTotalPage(result.data.totalRecord);
SetLoading(false)
} else {
NotificationManager.error('Gagal Mengambil Data!!', 'Failed');
SetLoading(false)
}
}
const handleOpenDialog = async (type) => {
await setTypeDialog(type)
getDataAllMenu();
// setOpenDialog(true)
}
const handleCloseDialog = (type, data) => {
if (type === "save") {
saveMenu(data);
} else if (type === "edit") {
editMenu(data);
}
setDataEdit([])
// setOpenDialog(false)
}
const toggleAddDialog = () => {
// setOpenDialog(!openDialog)
}
const handleCancel = () => {
closeDialog('cancel', 'none')
// setDatatable([]);
// setBaseUrl([]);
// setAvailableBaseUrl(false);
// setLoading(false);
}
const onConfirmDelete = async () => {
const url = MENU_DELETE(idDelete)
const result = await axios.delete(url, config)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataMenu()
setIdDelete(0)
setAlertDelete(false)
NotificationManager.success(`Data menu berhasil dihapus`, 'Success!!');
} else {
setIdDelete(0)
setAlertDelete(false)
NotificationManager.error(`Data menu gagal dihapus`, 'Failed!!');
}
}
const saveMenu = async (data) => {
const formData = data
const result = await axios.post(MENU_ADD, formData, config)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataMenu();
getDataAllMenu();
NotificationManager.success(`Data menu berhasil ditambahkan`, 'Success!!');
} else {
NotificationManager.error(`Data menu gagal ditambahkan`, 'Failed!!');
}
}
const editMenu = async (data) => {
const formData = data
const url = MENU_EDIT(data.id)
const result = await axios.put(url, formData, config)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code === 200) {
getDataMenu();
NotificationManager.success(`Data menu berhasil diubah`, 'Success!!');
} else {
NotificationManager.error(`Data menu gagal diubah`, `Failed!!`);
}
}
const handleEdit = (data) => {
setDataEdit(data)
handleOpenDialog('Edit');
}
const handleDelete = async (id) => {
await setAlertDelete(true)
await setIdDelete(id)
}
const onShowSizeChange = (current, pageSize) => {
setRowsPerPage(pageSize)
}
const onPagination = (current, pageSize) => {
setCurrentPage(current)
}
const toggle = (param) => {
if (param === "edit") {
setTooltipEdit(!tooltipEdit)
} else if (param === "delete") {
setTooltipDelete(!tooltipDelete)
} else if (param === "tambah") {
setTooltipTambah(!tooltipTambah)
} else if (param === "export") {
setTooltipExport(!tooltipExport)
}
}
const handleExportExcel = async () => {
const payload = {
"paging": { "start": 0, "length": -1 },
"columns": [
{ "name": "name", "logic_operator": "ilike", "value": search, "operator": "AND" }
],
"joins": [{
"name": "m_menu",
"column_join": "parent_id",
"column_results": [
"name"
]
}],
"orders": { "columns": ["id"], "ascending": false }
}
const result = await axios
.post(MENU_SEARCH, payload, config)
.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,
"Url": val.url,
"Icon": val.icon,
"Alias Name": val.alias_name,
"Urutan": val.sequence,
"Parent Name": val.join_first_name ? val.join_first_name : "-"
}
excelData.push(dataRow)
})
await setDataExport(excelData);
} else {
NotificationManager.error('Gagal Export Data!!', 'Failed');
}
}
const exportExcel = () => {
const dataExcel = dataExport || [];
const fileName = `Data Master Menu.xlsx`;
const ws = XLSX.utils.json_to_sheet(dataExcel);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, `Data Master Menu`);
XLSX.writeFile(wb, fileName);
setDataExport([])
}
const cancelDelete = () => {
setAlertDelete(false)
setIdDelete(0)
}
const renderTable = useMemo(() => {
const columns = [
{
title: t('action'),
dataIndex: '',
key: 'x',
render: (text, record) => <>
<Tooltip title={t('delete')}>
<i id="TooltipDelete" className="fa fa-trash" style={{ color: 'red', marginRight: '10px', cursor: "pointer" }} onClick={() => handleDelete(text.id)}></i>
</Tooltip>
<Tooltip title={t('edit')}>
<i id="TooltipEdit" className="fa fa-edit" style={{ color: 'green', cursor: "pointer" }} onClick={() => handleEdit(text)}></i>
</Tooltip>
</>,
},
{ title: t('name'), dataIndex: 'name', key: 'name' },
{ title: 'Url', dataIndex: 'url', key: 'url' },
{ title: t('icon'), dataIndex: 'icon', key: 'icon' },
{ title: 'Alias', dataIndex: 'alias_name', key: 'alias_name' },
{ title: t('order'), dataIndex: 'sequence', key: 'sequence' },
{ title: t('parentMenu'), dataIndex: 'join_first_name', key: 'join_first_name', render: (text, record) => (text ? text : "-") }
];
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}
dataMenu={allDataMenu}
/> */}
<Modal size="xl" fullscreen="xl" scrollable={true} isOpen={openDialog} toggle={toggleDialog}>
<ModalHeader className="capitalize withBtn" toggle={closeDialog} style={{ width: "100%" }}>
MASTER MENU
<div style={{ display: 'flex' }}>
<Tooltip title={t('menuAdd')}>
<Button style={{ marginLeft: "5px" }} id="TooltipTambah" color="success" onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button>
</Tooltip>
<Tooltip title={t('exportExcel')}>
<Button style={{ marginLeft: "5px" }} id="TooltipExport" color="primary" onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button>
</Tooltip>
</div>
</ModalHeader>
<ModalBody>
<>
<Spin tip="Loading..." spinning={loading}>
{renderTable}
<Pagination
style={{ marginTop: "25px" }}
showSizeChanger
onShowSizeChange={onShowSizeChange}
onChange={onPagination}
pageSizeOptions={["10", "25", "50"]}
total={totalPage}
pageSize={rowsPerPage}
current={currentPage}
/>
</Spin>
</>
</ModalBody>
<ModalFooter>
{/* {typeDialog !== "Set" && typeDialog !== "Set-Menu" && (
<Button color="primary" onClick={() => handleSave()}>{typeDialog}</Button>
)} */}
<Button className="capitalize" color="secondary" onClick={() => handleCancel()}>cancel</Button>
</ModalFooter>
</Modal>
{/* <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('searchMenu')} />
</Col>
<Col>
<Tooltip title={t('menuAdd')}>
<Button id="TooltipTambah" color="success" onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button>
</Tooltip>
<Tooltip title={t('exportExcel')}>
<Button style={{ marginLeft: "5px" }} id="TooltipExport" color="primary" onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button>
</Tooltip>
</Col>
</Row>
</CardHeader>
<CardBody>
{renderTable}
<Pagination
style={{ marginTop: "25px" }}
showSizeChanger
onShowSizeChange={onShowSizeChange}
onChange={onPagination}
current={currentPage}
pageSize={rowsPerPage}
total={totalPage}
pageSizeOptions={["10", "15", "20", "25", "30", "35", "40"]}
/>
</CardBody>
</Card> */}
</div>
)
}
export default Index;

89
src/views/Master/MasterMenu/index.js → src/views/Master/MenuCompany/index.js

@ -1,11 +1,12 @@
import * as XLSX from 'xlsx';
import DialogForm from './DialogForm';
import DialogMasterMenu from './DialogMasterMenu.js'
import React, { useState, useEffect, useMemo } from 'react';
import SweetAlert from 'react-bootstrap-sweetalert';
import axios from 'axios';
import { Button } from 'reactstrap';
import { Card, CardBody, CardHeader, Col, Row, Input } from 'reactstrap';
import { MENU_ADD, MENU_SEARCH, MENU_EDIT, MENU_DELETE, MENU_LIST } from '../../../const/ApiConst.js';
import { MENU_ADD, MENU_SEARCH, MENU_EDIT, MENU_DELETE, MENU_LIST, MENU_COMPANY_SEARCH, COMPANY_MANAGEMENT_LIST } from '../../../const/ApiConst.js';
import { NotificationContainer, NotificationManager } from 'react-notifications';
import { Pagination, Tooltip, Table } from 'antd';
import { useTranslation } from 'react-i18next';
@ -19,7 +20,20 @@ const column = [
{ name: "Parent" },
]
const Index = ({ params }) => {
const Index = ({ params, ...props }) => {
let role_id = 0, user_id = 0, isLogin = false, token = '', company_id = 0, all_project = null, role_name = '', hierarchy = [], user_name = '';
if (props && props.role_id && props.user_id) {
role_id = props.role_id;
user_id = props.user_id;
token = props.token;
isLogin = props.isLogin;
company_id = props.company_id;
all_project = props.all_project;
role_name = props.role_name;
isLogin = props.isLogin;
hierarchy = props.hierarchy;
user_name = props.user_name;
}
const [alertDelete, setAlertDelete] = useState(false)
const [allDataMenu, setAllDataMenu] = useState([])
const [clickOpenModal, setClickOpenModal] = useState(false)
@ -29,6 +43,7 @@ const Index = ({ params }) => {
const [dataTable, setDatatable] = useState([])
const [idDelete, setIdDelete] = useState(0)
const [openDialog, setOpenDialog] = useState(false)
const [openDialogMasterMenu, setOpenDialogMasterMenu] = useState(false)
const [rowsPerPage, setRowsPerPage] = useState(10)
const [search, setSearch] = useState('')
const [tooltipDelete, setTooltipDelete] = useState(false)
@ -37,6 +52,7 @@ const Index = ({ params }) => {
const [tooltipTambah, setTooltipTambah] = useState(false)
const [totalPage, setTotalPage] = useState(0)
const [typeDialog, setTypeDialog] = useState('Save')
const [listCompany, setListCompany] = useState([])
const { t } = useTranslation()
const pageName = params.name;
@ -59,6 +75,21 @@ const Index = ({ params }) => {
}
}, [dataExport])
useEffect(() => {
getDataCompany()
}, [])
const getDataCompany = async () => {
const result = await axios
.get(COMPANY_MANAGEMENT_LIST, config)
.then(res => res)
.catch((error) => error.response);
if (result && result.data && result.data.code == 200) {
setListCompany(result.data.data);
}
}
const handleSearch = e => {
const value = e.target.value
setSearch(value);
@ -93,11 +124,11 @@ const Index = ({ params }) => {
const payload = {
"paging": { "start": start, "length": rowsPerPage },
"columns": [
{ "name": "name", "logic_operator": "ilike", "value": search, "operator": "AND" }
{ "name": "alias_name", "logic_operator": "ilike", "value": search, "operator": "AND" }
],
"joins": [{
"name": "m_menu",
"column_join": "parent_id",
"column_join": "menu_id",
"column_results": [
"name"
]
@ -105,8 +136,24 @@ const Index = ({ params }) => {
"orders": { "columns": ["id"], "ascending": false }
}
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" },
)
payload.joins.push(
{ name: "m_company", column_join: "company_id", column_results: ["company_name"] }
)
payload.columns.push(
{ name: "company_name", logic_operator: "~*", value: search, table_name: "m_company" }
)
}
const result = await axios
.post(MENU_SEARCH, payload, config)
.post(MENU_COMPANY_SEARCH, payload, config)
.then(res => res)
.catch((error) => error.response);
@ -123,6 +170,18 @@ const Index = ({ params }) => {
getDataAllMenu();
setOpenDialog(true)
}
const handleOpenDialogMasterMenu = async () => {
setOpenDialogMasterMenu(true)
}
const handleCloseDialogMasterMenu = () => {
setOpenDialogMasterMenu(false)
}
const toggleDialogMasterMenu = () => {
setOpenDialogMasterMenu(!openDialog)
}
const handleCloseDialog = (type, data) => {
if (type === "save") {
@ -288,7 +347,10 @@ const Index = ({ params }) => {
</Tooltip>
</>,
},
{ title: t('name'), dataIndex: 'name', key: 'name' },
...(role_name === 'Super Admin' ?
[{ title: t('company'), dataIndex: 'join_second_company_name', key: 'join_second_company_name' }]
: []),
{ title: t('name'), dataIndex: 'join_first_name', key: 'join_first_name' },
{ title: 'Url', dataIndex: 'url', key: 'url' },
{ title: t('icon'), dataIndex: 'icon', key: 'icon' },
{ title: 'Alias', dataIndex: 'alias_name', key: 'alias_name' },
@ -330,6 +392,14 @@ const Index = ({ params }) => {
dataEdit={dataEdit}
clickOpenModal={clickOpenModal}
dataMenu={allDataMenu}
listCompany={listCompany}
role_name={role_name}
company_id={company_id}
/>
<DialogMasterMenu
openDialog={openDialogMasterMenu}
closeDialog={handleCloseDialogMasterMenu}
toggleDialog={() => toggleDialogMasterMenu}
/>
<Card>
<CardHeader style={{ display: "flex", justifyContent: "space-between" }}>
@ -339,8 +409,13 @@ const Index = ({ params }) => {
<Input onChange={handleSearch} value={search} type="text" name="search" id="search" placeholder={t('searchMenu')} />
</Col>
<Col>
{role_name === 'Super Admin' &&
<Tooltip title="Master Menu">
<Button id="TooltipMasterMenu" color="warning" onClick={() => handleOpenDialogMasterMenu()}><i className="fa fa-table"></i></Button>
</Tooltip>
}
<Tooltip title={t('menuAdd')}>
<Button id="TooltipTambah" color="success" onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button>
<Button style={{ marginLeft: "5px" }} id="TooltipTambah" color="success" onClick={() => handleOpenDialog('Save')}><i className="fa fa-plus"></i></Button>
</Tooltip>
<Tooltip title={t('exportExcel')}>
<Button style={{ marginLeft: "5px" }} id="TooltipExport" color="primary" onClick={() => handleExportExcel()}><i className="fa fa-print"></i></Button>

16
src/views/Pages/Login/Login.js

@ -58,6 +58,21 @@ class Login extends Component {
this.handleChange = this.handleChange.bind(this);
this.showHide = this.showHide.bind(this);
}
updatePageTitle = () => {
try {
const storedData = localStorage.getItem('configApp');
if (storedData !== null) {
const data = JSON.parse(storedData);
const htmlTitle = data.html_title || 'OSPRO';
console.log('htmlTitle', htmlTitle);
document.getElementById('title').innerText = htmlTitle;
} else {
document.title = 'OSPRO';
}
} catch (error) {
document.title = 'OSPRO';
}
}
showHide(e) {
e.preventDefault();
e.stopPropagation();
@ -255,6 +270,7 @@ class Login extends Component {
window.localStorage.setItem('all_project', data_user.role.all_project);
window.localStorage.setItem('hierarchy', JSON.stringify(data_user.hierarchy));
window.localStorage.setItem('configApp', JSON.stringify(data_user.configApp));
this.updatePageTitle();
} else {
// NotificationManager.error('Cek username atau password anda!', 'Gagal Login!');
NotificationManager.error(doLogin.data.message, 'Login Failed!');

Loading…
Cancel
Save