Browse Source

WIP: map-monitoring using redux and services api

pull/2/head
ardhi 2 years ago
parent
commit
8022ac14f9
  1. 5
      package.json
  2. 5
      src/App.js
  3. 104
      src/appredux/modules/map/actions.js
  4. 46
      src/appredux/modules/map/reducers.js
  5. 46
      src/appredux/reducers.js
  6. 46
      src/appredux/store.js
  7. 163
      src/components/MapLeftContent/index.js
  8. 106
      src/components/MapRightContent/index.js
  9. 9
      src/components/MapRightContent/styles.css
  10. 1
      src/const/ApiConst.js
  11. 3
      src/routes.js
  12. 99
      src/services/api/base.js
  13. 72
      src/services/api/modules/map_monitoring/index.js
  14. 62
      src/services/api/modules/project/index.js
  15. 41
      src/services/base.js
  16. 27
      src/utils/MapUtils.js
  17. 178
      src/views/MapMonitoring/index.js

5
package.json

@ -28,6 +28,7 @@
"@iconify/icons-uil": "^1.0.6", "@iconify/icons-uil": "^1.0.6",
"@iconify/react": "^1.1.1", "@iconify/react": "^1.1.1",
"@nicholasadamou/react-iframe": "^1.0.3", "@nicholasadamou/react-iframe": "^1.0.3",
"@reduxjs/toolkit": "^1.9.2",
"@terrestris/ol-util": "^7.2.0", "@terrestris/ol-util": "^7.2.0",
"@terrestris/react-geo": "^12.0.0", "@terrestris/react-geo": "^12.0.0",
"alasql": "^1.7.3", "alasql": "^1.7.3",
@ -76,12 +77,16 @@
"react-leaflet-draw": "^0.19.8", "react-leaflet-draw": "^0.19.8",
"react-loader-spinner": "^3.1.5", "react-loader-spinner": "^3.1.5",
"react-notifications": "^1.7.2", "react-notifications": "^1.7.2",
"react-redux": "^8.0.5",
"react-router-dom": "^5.0.1", "react-router-dom": "^5.0.1",
"react-select": "^4.3.1", "react-select": "^4.3.1",
"react-slick": "^0.28.1", "react-slick": "^0.28.1",
"react-tiny-fab": "^4.0.4", "react-tiny-fab": "^4.0.4",
"react-toastify": "^5.5.0", "react-toastify": "^5.5.0",
"reactstrap": "^8.0.0", "reactstrap": "^8.0.0",
"redux": "^4.2.1",
"redux-persist": "^6.0.0",
"redux-thunk": "^2.4.2",
"simple-line-icons": "^2.4.1", "simple-line-icons": "^2.4.1",
"slick-carousel": "^1.8.1", "slick-carousel": "^1.8.1",
"underscore": "^1.13.1", "underscore": "^1.13.1",

5
src/App.js

@ -2,6 +2,9 @@ import React, { Component } from 'react';
import { HashRouter, Route, Switch } from 'react-router-dom'; import { HashRouter, Route, Switch } from 'react-router-dom';
import './App.scss'; import './App.scss';
import 'react-notifications/lib/notifications.css'; import 'react-notifications/lib/notifications.css';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { persistor, store } from './appredux/store';
const loading = () => <div className="animated fadeIn pt-3 text-center">Loading...</div>; const loading = () => <div className="animated fadeIn pt-3 text-center">Loading...</div>;
@ -18,6 +21,7 @@ class App extends Component {
render() { render() {
return ( return (
<Provider store={store}>
<HashRouter> <HashRouter>
<React.Suspense fallback={loading()}> <React.Suspense fallback={loading()}>
<Switch> <Switch>
@ -34,6 +38,7 @@ class App extends Component {
</Switch> </Switch>
</React.Suspense> </React.Suspense>
</HashRouter> </HashRouter>
</Provider>
); );
} }
} }

104
src/appredux/modules/map/actions.js

@ -0,0 +1,104 @@
import moment from "moment";
import ApiMapMonitoring from "../../../services/api/modules/map_monitoring";
import { store } from "../../store";
export const SET_MYMAP = 'SET_MYMAP';
export const SET_OPEN_LEFT = 'SET_OPEN_LEFT';
export const SET_OPEN_RIGHT = 'SET_OPEN_RIGHT';
export const SET_MAP_LOADING = 'SET_MAP_LOADING';
export const SET_USER_HISTORY = 'SET_USER_HISTORY';
export const SET_PROJECT_TREE = 'SET_PROJECT_TREE';
export const SET_USER_POINTS = 'SET_USER_POINTS';
export const SET_SELECTED_FEATURE = 'SET_SELECTED_FEATURE';
export const setMymap = obj => dispatch => {
dispatch({
type: SET_MYMAP,
payload: obj
})
}
export const setOpenLeft = obj => dispatch => {
dispatch({
type: SET_OPEN_LEFT,
payload: obj
})
}
export const setOpenRight = obj => dispatch => {
dispatch({
type: SET_OPEN_RIGHT,
payload: obj
})
}
export const setMapLoading = obj => dispatch => {
dispatch({
type: SET_MAP_LOADING,
payload: obj
})
}
export const setUserHistory = obj => dispatch => {
dispatch({
type: SET_USER_HISTORY,
payload: obj
})
}
export const setProjectTree = obj => dispatch => {
dispatch({
type: SET_PROJECT_TREE,
payload: obj
})
}
export const setUserPoints = obj => dispatch => {
dispatch({
type: SET_USER_POINTS,
payload: obj
})
}
export const setSelectedFeature = obj => dispatch => {
dispatch({
type: SET_SELECTED_FEATURE,
payload: obj
})
}
export const getUserPoints = async () => {
let userPoints = await ApiMapMonitoring.search();
// console.log('userPoints', userPoints);
if (userPoints.status && userPoints.data && userPoints.data.length > 0) {
let featureCollection = {
"type": "FeatureCollection",
"features": []
}
userPoints.data.map(n => {
let feature = {
"type": "Feature",
"properties": null,
"geometry": null
}
feature.properties = {
"Name": n.join_first_name ? n.join_first_name : '-',
"Clock in time": n.clock_in ? moment(n.clock_in).format('YYYY-MM-DD HH:mm:ss') : '-',
"Clock in location": n.clock_in_loc ? n.clock_in_loc : '-',
"Clock out time": n.clock_out ? moment(n.clock_out).format('YYYY-MM-DD HH:mm:ss') : '-',
"Clock out location": n.clock_out_loc ? n.clock_out_loc : '-',
}
feature.geometry = {
"type": "Point",
"coordinates": [n.clock_in_lng, n.clock_in_lat]
}
featureCollection.features.push(feature);
});
store.dispatch(setUserPoints(featureCollection));
}
}

46
src/appredux/modules/map/reducers.js

@ -0,0 +1,46 @@
import {
SET_MYMAP,
SET_MAP_LOADING,
SET_USER_HISTORY,
SET_PROJECT_TREE,
SET_USER_POINTS,
SET_SELECTED_FEATURE,
SET_OPEN_LEFT,
SET_OPEN_RIGHT
} from "./actions";
const initialState = {
mymap: null,
openLeft: true,
openRight: false,
mapLoading: false,
userHistory: null,
projectTree: null,
userPoints: null,
selectedFeature: null
}
function mapReducer(state = initialState, action) {
switch (action.type) {
case SET_MYMAP:
return { ...state, mymap: action.payload }
case SET_OPEN_LEFT:
return { ...state, openLeft: action.payload }
case SET_OPEN_RIGHT:
return { ...state, openRight: action.payload }
case SET_MAP_LOADING:
return { ...state, mapLoading: action.payload }
case SET_USER_HISTORY:
return { ...state, userHistory: action.payload }
case SET_PROJECT_TREE:
return { ...state, projectTree: action.payload }
case SET_USER_POINTS:
return { ...state, userPoints: action.payload }
case SET_SELECTED_FEATURE:
return { ...state, selectedFeature: action.payload }
default:
return state;
}
}
export default mapReducer;

46
src/appredux/reducers.js

@ -0,0 +1,46 @@
// import { parse, stringify } from 'flatted';
import { combineReducers } from 'redux';
import { persistReducer, createTransform } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
// modules
import mapReducer from './modules/map/reducers';
const rootReducer = combineReducers({
mapReducer
});
// export const transformCircular = createTransform(
// // (inboundState, key) => JSON.stringify(inboundState),
// // (outboundState, key) => JSON.parse(outboundState),
// (inboundState, key) => stringify(inboundState),
// (outboundState, key) => parse(outboundState),
// )
// const SetTransform = createTransform(
// // transform state on its way to being serialized and persisted.
// (inboundState, key) => {
// // convert mySet to an Array.
// return { ...inboundState, mySet: [...inboundState.mySet] };
// },
// // transform state being rehydrated
// (outboundState, key) => {
// // convert mySet back to a Set.
// return { ...outboundState, mySet: new Set(outboundState.mySet) };
// },
// // define which reducers this transform gets called for.
// { whitelist: ['mapReducer'] }
// );
const persistConfig = {
key: 'root',
storage,
// blacklist: [], // to be not persisted
// transforms: [SetTransform]
// transforms: [transformCircular]
};
// export default persistReducer(persistConfig, rootReducer);
export default rootReducer;

46
src/appredux/store.js

@ -0,0 +1,46 @@
import { configureStore } from "@reduxjs/toolkit";
import { applyMiddleware, compose, createStore } from "redux";
// import thunk from "redux-thunk";
import { persistStore } from 'redux-persist';
import thunk from "redux-thunk";
import thunkMiddleware from 'redux-thunk';
import persistedReducer from "./reducers";
// const enhancers = [
// applyMiddleware(
// thunkMiddleware,
// // createLogger({
// // collapsed: true,
// // // eslint-disable-next-line no-undef
// // predicate: () => __DEV__,
// // }),
// ),
// ];
// /* eslint-disable no-undef */
// const composeEnhancers =
// (__DEV__ &&
// typeof window !== 'undefined' &&
// window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
// compose;
// /* eslint-enable no-undef */
// const enhancer = composeEnhancers(...enhancers);
// export const store = createStore(persistedReducer, {}, enhancer)
// // export const store = configureStore(persistedReducer, enhancer);
// // export const store = configureStore( {
// // persistedReducer,
// // enhancers: enhancer
// // })
// export const persistor = persistStore(store);
export const store = configureStore({
reducer: persistedReducer,
devTools: process.env.NODE_ENV !== 'production',
middleware: [thunk]
})
export const persistor = persistStore(store)

163
src/components/MapLeftContent/index.js

@ -0,0 +1,163 @@
import React, { useEffect, useMemo, useRef, useState } from 'react'
import L from 'leaflet';
import { Button, Col, Drawer, Input, Row, Spin, Tree } from 'antd';
import ApiProject from '../../services/api/modules/project';
import { store } from '../../appredux/store';
import { getUserPoints, setMapLoading, setOpenRight, setProjectTree, setSelectedFeature, setUserPoints } from '../../appredux/modules/map/actions';
import ContentLoader from 'react-content-loader';
import { useSelector } from 'react-redux';
import ApiMapMonitoring from '../../services/api/modules/map_monitoring';
const { Search } = Input;
const MapLeftContent = () => {
// const { mapLoading } = store.getState().mapReducer;
const { mapLoading, projectTree } = useSelector(state => state.mapReducer);
// const [expandedKeys, setExpandedKeys] = useState([]);
// const [searchValue, setSearchValue] = useState('');
// const [autoExpandParent, setAutoExpandParent] = useState(true);
// const onExpand = (newExpandedKeys) => {
// setExpandedKeys(newExpandedKeys);
// setAutoExpandParent(false);
// };
const onSelect = (selectedKeys, info) => {
console.log('selected', selectedKeys, info);
};
const onCheck = (checkedKeys, info) => {
console.log('onCheck', checkedKeys, info);
if (checkedKeys.length < 1) {
store.dispatch(setUserPoints(null));
store.dispatch(setOpenRight(false));
store.dispatch(setSelectedFeature(null));
return;
}
getUserPoints();
};
// const getParentKey = (key, tree) => {
// let parentKey;
// for (let i = 0; i < tree.length; i++) {
// const node = tree[i];
// if (node.children) {
// if (node.children.some((item) => item.key === key)) {
// parentKey = node.key;
// } else if (getParentKey(key, node.children)) {
// parentKey = getParentKey(key, node.children);
// }
// }
// }
// return parentKey;
// };
// const treeData = useMemo(() => {
// const loop = (data) =>
// data.map((item) => {
// const strTitle = item.title;
// const index = strTitle.indexOf(searchValue);
// const beforeStr = strTitle.substring(0, index);
// const afterStr = strTitle.slice(index + searchValue.length);
// const title =
// index > -1 ? (
// <span>
// {beforeStr}
// <span className="site-tree-search-value">{searchValue}</span>
// {afterStr}
// </span>
// ) : (
// <span>{strTitle}</span>
// );
// if (item.children) {
// return {
// title,
// key: item.key,
// children: loop(item.children),
// };
// }
// return {
// title,
// key: item.key,
// };
// });
// return loop(projectTree);
// }, [searchValue]);
// const onChangeSearch = (e) => {
// const { value } = e.target;
// const newExpandedKeys = projectTree
// .map((item) => {
// if (item.title.indexOf(value) > -1) {
// return getParentKey(item.key, projectTree);
// }
// return null;
// })
// .filter((item, i, self) => item && self.indexOf(item) === i);
// setExpandedKeys(newExpandedKeys);
// setSearchValue(value);
// setAutoExpandParent(true);
// };
const Content = useMemo(() => {
return (
<div style={{ maxHeight: '90vh', overflow: 'auto', paddingRight: 10 }}>
<div style={{ backgroundColor: '#f0f0f0', padding: 10, marginBottom: 5, fontWeight: 'bold', fontSize: 16 }}>
Project List
</div>
{mapLoading ?
<ContentLoader
speed={2}
width="100%"
height="200"
viewBox="0 0 200 200"
backgroundColor="#f3f3f3"
foregroundColor="#ecebeb"
>
<circle cx="10" cy="20" r="8" />
<rect x="25" y="15" rx="5" ry="5" width="220" height="10" />
<circle cx="10" cy="50" r="8" />
<rect x="25" y="45" rx="5" ry="5" width="220" height="10" />
<circle cx="10" cy="80" r="8" />
<rect x="25" y="75" rx="5" ry="5" width="220" height="10" />
<circle cx="10" cy="110" r="8" />
<rect x="25" y="105" rx="5" ry="5" width="220" height="10" />
</ContentLoader>
:
<>
{/* <Search
style={{
marginBottom: 8,
}}
placeholder="Search"
onChange={onChangeSearch}
/> */}
{
projectTree ?
<Tree
checkable
defaultExpandedKeys={['all']}
defaultCheckedKeys={['all']}
onSelect={onSelect}
onCheck={onCheck}
// onExpand={onExpand}
// expandedKeys={expandedKeys}
// autoExpandParent={autoExpandParent}
treeData={projectTree}
/>
:
null
}
</>
}
</div>
)
}, [mapLoading])
return Content;
}
export default MapLeftContent;

106
src/components/MapRightContent/index.js

@ -0,0 +1,106 @@
import React, { useEffect, useMemo, useRef, useState } from 'react'
import L from 'leaflet';
import { Button, Col, Drawer, Input, Row, Spin, Tree } from 'antd';
import ApiProject from '../../services/api/modules/project';
import { store } from '../../appredux/store';
import { setMapLoading, setProjectTree } from '../../appredux/modules/map/actions';
import ContentLoader from 'react-content-loader';
import { useSelector } from 'react-redux';
import './styles.css'
const { Search } = Input;
const MapRightContent = () => {
const { mapLoading, selectedFeature } = useSelector(state => state.mapReducer);
const PopupContent = useMemo(() => {
console.log('selectedFeature', selectedFeature);
if (selectedFeature && selectedFeature.properties) {
// let content = [];
// for (let key in selectedFeature.properties) {
// content.push(<tr key={key}>
// <td>{`${key}`}</td>
// <td>:</td>
// <td>{`${selectedFeature.properties[key]}`}</td>
// </tr>)
// }
// console.log('content', content);
return (
<table className="table popup-table">
<tbody>
<tr>
<td>Name</td>
<td>:</td>
<td>{selectedFeature?.properties['Name']}</td>
</tr>
<tr>
<td>Clock in time</td>
<td>:</td>
<td>{selectedFeature?.properties['Clock in time']}</td>
</tr>
<tr>
<td>Clock in location</td>
<td>:</td>
<td>{selectedFeature?.properties['Clock in location']}</td>
</tr>
<tr>
<td>Clock out time</td>
<td>:</td>
<td>{selectedFeature?.properties['Clock out time']}</td>
</tr>
<tr>
<td>Clock out location</td>
<td>:</td>
<td>{selectedFeature?.properties['Clock out location']}</td>
</tr>
</tbody>
</table>
)
}
}, [selectedFeature])
const Content = useMemo(() => {
return (
<div style={{ maxHeight: '90vh', overflow: 'auto', paddingLeft: 10 }}>
<div style={{ backgroundColor: '#f0f0f0', padding: 10, marginBottom: 5, fontWeight: 'bold', fontSize: 16 }}>
Detail Information
</div>
<table className="table popup-table">
<tbody>
<tr>
<td className='td-popup-label'>Name</td>
<td>:</td>
<td>{selectedFeature?.properties['Name']}</td>
</tr>
<tr>
<td className='td-popup-label'>Clock in time</td>
<td>:</td>
<td>{selectedFeature?.properties['Clock in time']}</td>
</tr>
<tr>
<td className='td-popup-label'>Clock in location</td>
<td>:</td>
<td>{selectedFeature?.properties['Clock in location']}</td>
</tr>
<tr>
<td className='td-popup-label'>Clock out time</td>
<td>:</td>
<td>{selectedFeature?.properties['Clock out time']}</td>
</tr>
<tr>
<td className='td-popup-label'>Clock out location</td>
<td>:</td>
<td>{selectedFeature?.properties['Clock out location']}</td>
</tr>
</tbody>
</table>
</div>
)
}, [selectedFeature])
return Content;
}
export default MapRightContent;

9
src/components/MapRightContent/styles.css

@ -0,0 +1,9 @@
table th, .table td {
padding: 0.25rem !important;
vertical-align: top;
border-top: 1px solid #c8ced3;
}
.td-popup-label {
color: #808080;
}

1
src/const/ApiConst.js

@ -113,6 +113,7 @@ export const TOKEN_ADW = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIxMjAyI
// export let BASE_OSPRO = "https://ospro-api.ospro.id"; // export let BASE_OSPRO = "https://ospro-api.ospro.id";
export let BASE_OSPRO = "https://adw-api.ospro.id"; export let BASE_OSPRO = "https://adw-api.ospro.id";
// export let BASE_OSPRO = "http://localhost:8444";
// export let BASE_OSPRO = "http://192.168.1.123:8444"; // local // export let BASE_OSPRO = "http://192.168.1.123:8444"; // local
// export let BASE_OSPRO = "http://103.73.125.81:8444"; // ip public adw // export let BASE_OSPRO = "http://103.73.125.81:8444"; // ip public adw
export let BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`; export let BASE_SIMPRO_LUMEN = `${BASE_OSPRO}/api`;

3
src/routes.js

@ -46,6 +46,7 @@ const UserShift = React.lazy(() => import('./views/SimproV2/UserShift'));
const DashboardBOD = React.lazy(() => import('./views/Dashboard/DashboardBOD')); const DashboardBOD = React.lazy(() => import('./views/Dashboard/DashboardBOD'));
const DashboardCustomer = React.lazy(() => import('./views/Dashboard/DashboardCustomer')); const DashboardCustomer = React.lazy(() => import('./views/Dashboard/DashboardCustomer'));
const DashboardProject = React.lazy(() => import('./views/Dashboard/DashboardProject')); const DashboardProject = React.lazy(() => import('./views/Dashboard/DashboardProject'));
const MapMonitoring = React.lazy(() => import('./views/MapMonitoring'));
const routes = [ const routes = [
{ path: '/', exact: true, name: 'Home' }, { path: '/', exact: true, name: 'Home' },
@ -77,7 +78,6 @@ const routes = [
{ path: '/divisi', exact: true, name: 'Divisi', component: Divisi }, { path: '/divisi', exact: true, name: 'Divisi', component: Divisi },
{ path: '/satuan', exact: true, name: 'Satuan', component: Satuan }, { path: '/satuan', exact: true, name: 'Satuan', component: Satuan },
{ path: '/config-alert', exact: true, name: 'Config Alert', component: ConfigAlert }, { path: '/config-alert', exact: true, name: 'Config Alert', component: ConfigAlert },
{ path: '/checklist-k3', exact: true, name: 'Checklist K3', component: ChecklistK3 }, { path: '/checklist-k3', exact: true, name: 'Checklist K3', component: ChecklistK3 },
{ path: '/absensi', exact: true, name: 'Absensi', component: Absensi }, { path: '/absensi', exact: true, name: 'Absensi', component: Absensi },
{ path: '/divisi-karyawan', exact: true, name: 'Divisi Karyawan', component: DivisiKaryawan }, { path: '/divisi-karyawan', exact: true, name: 'Divisi Karyawan', component: DivisiKaryawan },
@ -100,6 +100,7 @@ const routes = [
{ path: '/user-admin', exact: true, name: 'User Admin', component: UserAdmin }, { path: '/user-admin', exact: true, name: 'User Admin', component: UserAdmin },
{ path: '/user-shift', exact: true, name: 'Shift', component: UserShift }, { path: '/user-shift', exact: true, name: 'Shift', component: UserShift },
{ path: '/working-hour', exact: true, name: 'Working Hour', component: Shift }, { path: '/working-hour', exact: true, name: 'Working Hour', component: Shift },
{ path: '/map-monitoring', exact: true, name: 'Map Monitoring', component: MapMonitoring },
// { path: '/dashboard-project/:ID/:GANTTID', exact: true, name: 'Dashboard Project', component: DashboardProject }, // { path: '/dashboard-project/:ID/:GANTTID', exact: true, name: 'Dashboard Project', component: DashboardProject },
]; ];

99
src/services/api/base.js

@ -0,0 +1,99 @@
import axios from 'axios';
export default class RequestApi {
// static Request() {
// // axios.interceptors.request.use(function (config) {
// // const token = localStorage.getItem('token')
// // config.headers.Authorization = token;
// // return config;
// // });
// const token = localStorage.getItem('token')
// let instance = axios.create({
// headers: {
// 'Content-Type': 'application/json',
// "Authorization": `Bearer ${token}`
// }
// })
// instance.interceptors.response.use(
// (response) => response,
// async (error) => {
// // const originalRequest = error.config;
// if (error.response.status === 307 || error.response.status === 403) {
// console.log(error.response);
// }
// return Promise.reject(error);
// }
// );
// return instance;
// }
static Request() {
axios.interceptors.response.use(
response => response,
async (error) => {
// console.log('error axios', error);
if (error) {
// console.log('stringify', JSON.stringify(error));
const err = JSON.parse(JSON.stringify(error));
if (err.name === 'AxiosError' && err.code === 'ERR_NETWORK') {
alert(err.message);
return;
}
if (err.response) {
if (err.response.status === 307 || err.response.status === 403) {
// console.log(err.response);
alert('Token expired, please re-login');
// clearAllState();
window.localStorage.clear();
// this.props.history.replace('/login');
return;
}
}
return Promise.reject(error);
}
}
);
return axios;
}
static Header() {
let header = {
headers: {
"Content-Type": "application/json",
// 'Cache-Control': 'no-cache'
}
}
return header;
}
static HeaderWithToken() {
const token = localStorage.getItem('token')
let header = {
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`,
// 'Cache-Control': 'no-cache',
}
}
return header;
}
static HeaderMultipart() {
const token = localStorage.getItem('token')
let header = {
headers: {
"Content-Type": "multipart/form-data",
"Authorization": `Bearer ${token}`,
// 'Cache-Control': 'no-cache',
}
}
return header;
}
}
export const AXIOS = RequestApi.Request();

72
src/services/api/modules/map_monitoring/index.js

@ -0,0 +1,72 @@
import moment from "moment";
import { BASE_SIMPRO_LUMEN } from "../../../../const/ApiConst";
import RequestApi from '../../base';
export default class ApiMapMonitoring extends RequestApi {
static async search() {
const URL = `${BASE_SIMPRO_LUMEN}/presence/search`
const dateFrom = moment().subtract(7,'d').format('YYYY-MM-DD 00:00:00');
const dateTo = moment().format('YYYY-MM-DD 23:59:00');
const payload = {
"columns": [
{
"logic_operator": "range",
"name": "created_at",
"operator": "AND",
"value": dateFrom,
"value1": dateTo
}
],
"joins": [
{
"column_join": "user_id",
"column_results": [
"username", "name"
],
"name": "m_users"
}
],
"orders": {
"ascending": false,
"columns": [
"created_at"
]
},
"paging": {
"length": 25,
"start": 0
}
}
return await RequestApi.Request().post(
URL,
payload,
RequestApi.HeaderWithToken()).then(res => {
if (res) {
if (res && res.data && res.data.data) {
// console.log('ApiPresence search', res.data.data)
if (res.data.data.length > 0) {
return {status: true, data: res.data.data};
}
else {
return {status: false, data: null}
}
}
else {
return {status: false, data: null};
}
}
else {
alert("Please check your internet connection.", "error");
return {status: false, data: null};
}
}).catch(e => {
// console.log('error search presence', e);
let error = JSON.parse(JSON.stringify(e));
console.log('error search presence', error);
if (error.message) {
alert(error.message);
}
return {status: false, data: null};
});
}
}

62
src/services/api/modules/project/index.js

@ -0,0 +1,62 @@
import { BASE_SIMPRO_LUMEN } from "../../../../const/ApiConst";
import RequestApi from '../../base';
export default class ApiProject extends RequestApi {
static async list() {
// const user_id = store.getState().userReducer && store.getState().userReducer.user && store.getState().userReducer.user.data_user ? store.getState().userReducer.user.data_user.id : 0;
const URL = `${BASE_SIMPRO_LUMEN}/project/list`
// const payload = {
// "paging": { "start": 0, "length": 25 },
// // "columns": [
// // { "name": "user_id", "logic_operator": "=", "value": user_id }
// // ],
// // "joins": [
// // {
// // "name": "m_proyek",
// // "column_join": "proyek_id",
// // "column_results": [
// // "nama", "kode_sortname", "mulai_proyek", "akhir_proyek"
// // ]
// // },
// // {
// // "name": "m_activity",
// // "column_join": "activity_id",
// // "column_results": [
// // "name", "start_date", "end_date", "persentase_progress"
// // ]
// // }
// // ],
// "orders": { "columns": ["id"], "ascending": false }
// }
return await RequestApi.Request().get(
URL,
RequestApi.HeaderWithToken()).then(res => {
if (res) {
if (res && res.data && res.data.data) {
// console.log('ApiProject search', res.data.data)
if (res.data.data.length > 0) {
return {status: true, data: res.data.data};
}
else {
return {status: false, data: null}
}
}
else {
return {status: false, data: null};
}
}
else {
alert("Please check your internet connection.", "error");
return {status: false, data: null};
}
}).catch(e => {
// console.log('error search project', e);
let error = JSON.parse(JSON.stringify(e));
console.log('error search project', error);
if (error.message) {
alert(error.message);
}
return {status: false, data: null};
});
}
}

41
src/services/base.js

@ -1,41 +0,0 @@
import axios from 'axios';
export default class RequestApi {
// constructor() {
// this.Request = this.Request.bind(this);
// }
static Request() {
// axios.interceptors.request.use(function (config) {
// const token = localStorage.getItem('token')
// config.headers.Authorization = token;
// return config;
// });
const token = localStorage.getItem('token')
let instance = axios.create({
headers: {
'Content-Type': 'application/json',
"Authorization": `Bearer ${token}`
}
})
instance.interceptors.response.use(
(response) => response,
async (error) => {
// const originalRequest = error.config;
if (error.response.status === 307 || error.response.status === 403) {
console.log(error.response);
}
return Promise.reject(error);
}
);
return instance;
}
static getToken() { }
}
export const AXIOS = RequestApi.Request();

27
src/utils/MapUtils.js

@ -0,0 +1,27 @@
import { store } from "../appredux/store";
export const removeLayerByName = (layerName) => {
const {mymap} = store.getState().mapReducer;
var layerToRemove = [];
mymap.eachLayer(function(layer) {
if (layer.wmsParams) {
if (layer.wmsParams.layers) {
let layerWmsName = layer.wmsParams.layers.split(':')[1];
if (layerName === layerWmsName) {
layerToRemove.push(layer)
}
}
}
else {
if (layer.options && layer.options.name && layer.options.name === layerName) {
layerToRemove.push(layer);
}
}
});
if (layerToRemove.length > 0) {
for (let i = 0; i < layerToRemove.length; i++) {
mymap.removeLayer(layerToRemove[i]);
}
}
}

178
src/views/MapMonitoring/index.js

@ -0,0 +1,178 @@
import React, { useEffect, useMemo, useRef, useState } from 'react'
import L from 'leaflet';
import { Button, Col, Drawer, Row, Spin, Tree } from 'antd';
import ApiProject from '../../services/api/modules/project';
import { store } from '../../appredux/store';
import { getUserPoints, setMapLoading, setMymap, setOpenLeft, setOpenRight, setProjectTree, setSelectedFeature, setUserPoints } from '../../appredux/modules/map/actions';
import ContentLoader from 'react-content-loader';
import { useSelector } from 'react-redux';
import MapLeftContent from '../../components/MapLeftContent';
import MapRightContent from '../../components/MapRightContent';
import { removeLayerByName } from '../../utils/MapUtils';
const MapMonitoring = () => {
const GRID_LEFT = 6;
const GRID_MIDDLE = 12;
const GRID_RIGHT = 6;
const GRID_TOTAL = GRID_LEFT+GRID_MIDDLE+GRID_RIGHT;
const mapRef = useRef()
const center = {
// lat: -6.200000,
// lng: 106.816666
lat: -2.4809807277842437,
lng: 120.13025155062624
}
const {userPoints, mymap, openLeft, openRight} = useSelector(state => state.mapReducer);
const [gridMiddle, setGridMiddle] = useState(GRID_MIDDLE);
const [gridLeft, setGridLeft] = useState(0);
const [gridRight, setGridRight] = useState(0);
useEffect(() => {
initMap();
getMapLeftContent();
}, [])
useEffect(() => {
renderGridMap();
}, [openLeft, openRight])
useEffect(() => {
// console.log('userPoints changes', userPoints);
if (mymap) {
removeLayerByName('popupTemp');
removeLayerByName('userPointLayer');
if (userPoints) {
let userPointLayer = L.geoJson(userPoints, {
name: 'userPointLayer',
onEachFeature: onEachFeatureUserPoints,
});
userPointLayer.addTo(mymap);
}
}
}, [userPoints])
const initMap = () => {
let mymap = L.map('map-area', {
center: center,
zoom: 5
});
store.dispatch(setMymap(mymap));
// setMymap(mymap);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(mymap);
mymap.on('click', (e) => {
removeLayerByName('popupTemp');
store.dispatch(setOpenRight(false));
store.dispatch(setSelectedFeature(null));
})
}
// init for left content panel, get projects and build tree select antd
const getMapLeftContent = async () => {
store.dispatch(setMapLoading(true));
let project = await ApiProject.list();
console.log('project', project);
if (project && project.status && project.data && project.data.length > 0) {
let projectData = [
{
"title": 'All',
"key": 'all',
"children": []
}
];
project.data.map(n => {
n.key = n.id;
n.title = n.nama;
projectData[0].children.push(n);
})
store.dispatch(setProjectTree(projectData));
// console.log('projectData', projectData);
getUserPoints();
store.dispatch(setMapLoading(false));
}
store.dispatch(setMapLoading(false));
}
const onEachFeatureUserPoints = (feature, layer) => {
layer.on('click', function(e) {
L.DomEvent.stopPropagation(e);
showHighLight(feature);
});
}
function showHighLight(feature) {
removeLayerByName('popupTemp');
// add highlight
L.geoJSON(feature, {
style: function(feature) {
return {color: 'blue'}
},
name: 'popupTemp',
onEachFeature: function (feature, layer) {
layer.name = 'popupTemp'
},
}).addTo(mymap);
// opening right panel
store.dispatch(setOpenRight(!openRight));
store.dispatch(setSelectedFeature(feature));
}
const MapContent = useMemo(() => {
return (
<Row>
<Col span={gridLeft}>
<MapLeftContent />
</Col>
<Col span={gridMiddle}>
<div id="map-area" style={{ height: '90vh', width: '100%' }} ref={mapRef}></div>
<button
title='List Projects'
style={{position: 'absolute', top: 80, left: 10, zIndex: 999, backgroundColor:'white', backgroundSize:'34px 34px', width: '34px', height: '34px', borderRadius: '2px', borderWidth: '1px', borderColor: 'grey'}}
onClick={() => store.dispatch(setOpenLeft(!openLeft))}>
<i className='fa fa-list'></i>
</button>
</Col>
<Col span={gridRight}>
<MapRightContent />
</Col>
</Row>
)
}, [openLeft, openRight, gridLeft, gridMiddle, gridRight])
const renderGridMap = () => {
let middle = GRID_MIDDLE;
let left = GRID_LEFT;
let right = GRID_RIGHT;
if (openLeft && openRight){
middle = GRID_MIDDLE;
left = GRID_LEFT;
right = GRID_RIGHT;
}
else if (!openLeft && openRight) {
middle = GRID_TOTAL - GRID_RIGHT;
left = 0;
right = GRID_RIGHT;
}
else if (openLeft && !openRight) {
middle = GRID_TOTAL - GRID_LEFT;
left = GRID_LEFT;
right = 0;
}
else if (!openLeft && !openRight) {
middle = GRID_TOTAL;
left = 0;
right = 0;
}
setGridMiddle(middle);
setGridLeft(left);
setGridRight(right);
}
return MapContent
}
export default MapMonitoring;
Loading…
Cancel
Save