ardhi
2 years ago
17 changed files with 991 additions and 62 deletions
@ -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)); |
||||
} |
||||
} |
@ -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; |
@ -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; |
@ -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) |
@ -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; |
@ -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; |
@ -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; |
||||
} |
@ -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(); |
||||
|
@ -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}; |
||||
}); |
||||
} |
||||
} |
@ -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}; |
||||
}); |
||||
} |
||||
} |
@ -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(); |
||||
|
@ -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]); |
||||
} |
||||
} |
||||
} |
@ -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: '© <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…
Reference in new issue